The last (Javascript) frontier: RhoConnect source adapters. Part 2.

Kutir Mobility -
5 MIN READ
7

In the previous post of this series we revealed that RhoConnect source adapters can now be written in Javascript, which lets you use the same language to write both the client (RhoMobile apps) and the server side (RhoConnect source adapters) of an enterprise application and we also saw, at a high level, how these adapters are built. Today we are going to take a deep dive and see how to actually write one.

First things first. What are we going to build?

We already have a sample Javascript CRUD application created 100% in Javascript, you can see how it was built at http://edgedocs.rhomobile.com/guide/rhom_backbone. We will take this application (available for download https://github.com/javiermolina1234/rhomobile-backbonejs-sample ), develop the corresponding RhoConnect server and enable synchronization between the two.

Prerequisites:

In order to follow this tutorial, you need to have already installed on your machine:

  • RhoMobile 4.0 and its own prerequisites like an appropriate version of Ruby. Do not worry if you have not used Ruby before, though, as you will not need to touch it at all. You can consider it as a dependency you do not have to interact with.
  • Redis
  • Node.js
  • SQLite3 installed and present in the PATH environment variable

Additionally, you will want to get a copy of the client application by either cloning https://github.com/javiermolina1234/rhomobile-backbonejs-sample or by downloading https://github.com/javiermolina1234/rhomobile-backbonejs-sample/archive/master.zip

Next, open a terminal / system prompt and use the rhoconnect command to create a new RhoConnect application:

rhoconnect app js-example --js

That will create a barebones rhoconnect application. Note the --js flag at the end: it tells the rhoconnect command that we intend to use Javascript and it will cause the newly-generated application to be ready for Javascript source adapters. Because the client application you downloaded has a model called "Product", we will add a source adapter with that same name:

cd js-example

rhoconnect source Product --js

Again, the --js flag is a signal to the rhoconnect command that we wish to generate a Javascript (not Ruby) source adapter. With those preliminary steps out of the way, we can now roll up our sleevs and get our hands dirty writing some code. Open models/js/product.js (which we just created) and have a look at it:

var rc = require('rhoconnect_helpers');

var Product = function(){

  this.login = function(resp){

    // TODO: Login to your data source here if necessary

    resp.send(true);

  };

  this.query = function(resp){

    var result = {};

    // TODO: Query your backend data source and assign the records

    // to a nested hash structure. Then return your result.

    // For example:

    //

    // {

    //   "1": {"name": "Acme", "industry": "Electronics"},

    //   "2": {"name": "Best", "industry": "Software"}

    // }

    resp.send(result);

  };

  this.create = function(resp){

    // TODO: Create a new record in your backend data source.  Then

    // return the result.

    resp.send('someId');

  };

  this.update = function(resp){

    // TODO: Update an existing record in your backend data source.

    // Then return the result.

    resp.send(true);

  };

  this.del = function(resp){

    // TODO: Delete an existing record in your backend data source

    // if applicable.  Be sure to have a hash key and value for

    // "object" and return the result.

    resp.send(true);

  };

  this.logoff = function(resp){

    // TODO: Logout from the data source if necessary.

    resp.send(true);

  };

  this.storeBlob = function(resp){

    // TODO: Handle post requests for blobs here.

    // Reference the blob object's path with resp.params.path.

    new rc.Exception(

      resp, "Please provide some code to handle blobs if you are using them."

    );

  };

};

module.exports = new Product();

As you can see, it's just a simple Node.js module that contains a few functions with predefined names. "login" and "logoff" let you establish and tear down connections to your backend data source if required, but "query", "create", "update" and "del" are where the rubber meets the road: these are where you will access your backend and return data to your mobile app.

To keep things simple in this tutorial, we will use an embedded SQLite database but you will see that the principles are the same whether your data is stored on a MySQL, MSSQL Server or Oracle database. We will tell Node.js that our code uses sqlite by opening package.json and adding the "dblite" module to the dependencies:

{

  "name" : "rhoconnect",

  "version" :"1.0.0",

  "main": "rhoconnect",

  "dependencies": {

    "redis" : "*",

    "dblite": "*"

  }

}

Run npm install so that the dblite module is added to your RhoConnect app.

npm install

That makes the dblite module available to our code, but we still have to reference it. Open model/js/products.js and add the relevant line as shown below:

var rc = require('rhoconnect_helpers');

// add the following line in your product.js file

var dblite = require('dblite');

That gives us access to the system's installed sqlite instance and this is why you need to have sqlite installed on your machine for this example, the dblite module invokes sqlite3 under the hood and pipes commands and results to and from the sqlite3 process.

Next, add some initialization code:

var Product = function(){

  // use a database in the root folder of our project

  var db = dblite(__dirname+'/../../database.db');

  // create a table to store products if we have not done so yet

  db.query('create table if not exists Products (id integer primary key, name text, brand text)');

We will open a database called "database.db" two levels up from our product.js file, which corresponds with the top-level folder of this RhoConnect instance, and create a table to store our product data. If the table is there already from a previous run, the "if not exists" clause will save us from causing an error.

Until now, we have been doing mostly preparation work; now is where it gets real: the query method.

query

As we saw in the previous installment of this two-part series, "query" corresponds to the Read operation (get data out of the database and return it to the mobile application). Here is what our implementation looks like in this sample:

this.query = function(resp){

    // fetch all rows from the Products table

    db.query("select * from Products", {id: Number, name:String, brand: String}, function(rows) {

      var result = {};

      // iterate over the rows

      for (var i=0; i

        var row = rows[i];

        // our result value must be a hash of hashes with the structure

        // identifier : { "property" : "value" }

        result[row.id] = {"name" : row.name, "brand": row.brand};

      }

      // return the result to the mobile app

      resp.send(result);    

    });

};

That was not so hard after all, right? query the database to get every known product and return them in the appropriate format.

create

Apart from reading existing data, we will need some way to create new records. That's what "create" is for:

this.create = function(resp){

    // create the product in our database

    db.query("insert into Products values (:id, :name, :brand)",

      {

        id: null, // sqlite will populate this value automatically

        // for the rest, we use what we received from the mobile app,

        // or null if we didn't get anything for that property

        name: resp.params.create_object.name || null,

        brand: resp.params.create_object.brand || null

      });

    // send back the new product's primary key

    db.lastRowID("Products", function(lastRowID) {

      resp.send(lastRowID);

    });

};

Again, pretty straightforward - do an insert with the appropriate values and return the primary key of the new row.

Once an application can create and read data, the next natural step is to be able to modify that information. Changes can take two forms: updates to existing data and deletions.

update

this.update = function(resp){

    var query = "update Products set ";

    var values = {};

    var known_fields = ["name", "brand"];

    var should_prepend_comma = false;

    for (var i=0; i

      var field = known_fields[i];

      var value = resp.params.update_object[field];

      if (typeof(value)!=="undefined") {

        if (should_prepend_comma) {

          query+=", ";

        } else {

          // we will need it next time

          should_prepend_comma = true;

        }

        query+=field+"=:"+field;

        values[field]=resp.params.update_object[field];

      }

    }

    query+=" where id=:id";

    values.id = resp.params.update_object.id;

    db.query(query, values);

    resp.send(true);

};

The code for update may look complex at first sight but, at the core, what it is doing is very simple. The resp.params.update_object hash contains one entry for each property that was modified in the application. The code checks to see which of "name" and "brand" are present and builds the SQL update statement dynamically. Finally, a "where" clause is added and off the query goes to the database.

del

By this point you have a very good idea of how the code for "del" is going to look like:

this.del = function(resp){

  db.query("delete from Products where id=:id",

    {

      id: resp.params.delete_object.id

    });

  resp.send(true);

};

That's it for the server side! Wasn't that a walk in the park?. Start your rhoconnect server with

rhoconnect start

and on to the client side - the RhoMobile app.

The client side

The app you downloaded is completely functional but it only stores data locally. What you have to do to link it to your shiny new RhoConnect server is:

- open rhoconfig.txt and set syncserver to the IP address of the computer where you are running RhoConnect. For this example, you can just set it to localhost and run the app on RhoSimulator on the same computer:

syncserver = 'http://localhost:9292'

- open public/js/application.js and look for comments about RhoConnect. There are two things to uncomment in this file, one is a call to login near the top:

Rho.RhoConnectClient.login('user1', 'password');

and the other is near the end of the file

// Uncomment for RhoConnect integration

$(document).on("click",".sync", function() {

  Rho.RhoConnectClient.doSync();

});

- in build.yml, uncomment the rhoconnect-client extension

- finally, open public/_templates.html and you will find another thing to uncomment: a button to trigger synchronization:

           

When you are ready, start the app with

rake run:rhosimulator

Voila, the application now boasts two way data synchronization, with Javascript being the only language you have used throughout the whole process. Try creating a few products and click "sync" so that they are sent to the RhoConnect server. Then close the app and delete the rhosimulator folder, this will erase the local database of the application. Start the app again and click sync, and it will bring back the data from the server. Refresh the page to see the results and enjoy mobile app development in Javascript.

profile

Kutir Mobility

Please Register or Login to post a reply

Replies