Mobile GeoLocation App in 30 minutes – Part 3: Deploying to OpenShift

Overview

In the previous two parts, you saw how to create a Node.js application and store JSON data in MongoDB (Part 1). You also saw how to present this data in a mobile Web application using Sencha Touch complete with Google Maps and geospatial searching (Part 2). Now, we are going to take both of these two pieces and deploy them on a Platform as a Service (PaaS). I looked at three different provides in this space: OpenShift, Heroku and Nodejitsu. The latter is Node.js specific while the other two you can deploy other types of applications (Java EE, Ruby, PHP, Python, etc.). I really liked RedHat’s OpenShift model. It has several nice dev features that I thought worked well which I will go over in this post. Nodejitsu worked well too but didn’t use Git (a distributed SCM tool) to manage changes. Heroku required a credit card at the point I wanted to introduce MongoDB, so I opted not to continue with Heroku. It doesn’t hurt to experiment with multiple vendors since you can sign-up for free accounts on the respective sites.

One of the main motivations to use a PaaS is that it wouldn’t be very maintainable or production worthy SSH’ing to a server and running your node process every time you wanted to launch your app. Moreover, even if you run your process, it eventually will shutdown when you close your SSH session. There is an npm package forever that can keep this from happening, but I think running Node on a PaaS is ideal.

Using OpenShift to create a Node App

The first thing you’ll want to do is create a free OpenShift account here. Once you have an account created, you can create the application directly from the website (very convenient) or you can install Ruby and the RHC gem (sudo gem install rhc) that allows you to do the same thing from command line. The website is pretty intuitive so I won’t cover this and should you want to use the CLI, the set-up is sufficiently covered here. Note that there are a few prerequisites to use the CLI, namely installed Ruby and Msysgit. Be sure to have those installed as per the provided link before continuing.

For the purposes of deploying the app we created in Part 1, you would do the following after you’ve established your OpenShift domain and added your SSH public keys to commit code from Git:

First, we create “dogtag” as a nodejs app:

$ rhc app create -a dogtag -t nodejs-0.6

Second, we add the MongoDB cartridge:

$ rhc app cartridge add -a myMongo -c mongodb-2.0

Last, we add the RockMongo cartridge to administer MongoDB from the browser (context URI: /rockmongo from your domain)

$ rhc app cartridge add -a myMongo -c rockmongo-1.1

Working with your application in OpenShift

After you have created the app, the next part is copy the DogTag Node.js code to the “dogtag” directory that was created by OpenShift. Since Git is used to manage changes, you can run the following commands to push the code to the server:

$ git add .

$ git commit -am ‘Initial check-in’

$ git push

What will happen then is that the code changes will push to the server, stop the node.js instance, deploy your code and restart the server…very cool! It will pick-up any NPM packages you need as long as you define them in the package.json file (in our case, we need express and mongoose). After that you can go to the provided URL from OpenShift and test the various URL routes we defined in Part 1 (e.g., http://nodetest-loutilities.rhcloud.com/dogtag). You’ll notice that there is no data, so I created a JSON file that you can import into MongoDB to have some initial data. To do the import, you need to run the following command when you SSH to your server’s node:

$ mongoimport –host $OPENSHIFT_NOSQL_DB_HOST –port $OPENSHIFT_NOSQL_DB_PORT –username $OPENSHIFT_NOSQL_DB_USERNAME –password $OPENSHIFT_NOSQL_DB_PASSWORD –db dogtag –collection dogtags –file dogs.json –jsonArray

Note the $OPENSHIFT* provided constants so that you don’t have to figure out the actual host, port, username and password you are on which is especially helpful in a cloud environment running on multiple nodes. Once you import, you can check the files and add a geospatial index, which is required for the geospatial queries as follows:

$ mongo
> use dogtag
> db.dogtags.find() //will return results
> db.dogtags.find().forEach( function (e) { e.coords = [ e.longitude, e.latitude ]; db.dogtags.save(e); }); //will add the required format for a coordinates field to index
> db.dogtags.ensureIndex( { coords : “2d” } ) //indexes the coords field for geospatial queries
> exit

Now, if you go to the URL again you will have JSON data returned in a JSONP callback. You can change the callback simply by adding &callback=[your callback name]. This is necessary to prevent cross-site scripting issues and required when using the JSONP proxy in Sencha Touch.

Deploying the Mobile App

Since the Mobile App was created with Sencha Touch, I just used a “DIY” (Do it yourself) app from OpenShift to deploy my app there. The process is very similar:

$ rhc app create -a dogtags -t diy-0.1

This creates a very basic Web app. You can cd to the “diy” directory that is created and copy the code from the Mobile app there. It will prompt you to replace index.html, which is fine. There is one thing you need to do in the existing code; you will need to update the store’s proxy to point to your “dogtag” node.js app you created in the previous section as well as the Pettracker controller which also has these references. To do that, simply update the URL with the one OpenShift provides and append “dogtag” on the end to get the list as follows:

 proxy: {  
       type: 'jsonp',  
       url: 'http://nodetest-loutilities.rhcloud.com/dogtag',  
       reader: {  
           type: 'json',  
           idProperty: '_id',  
           rootProperty: 'records',  
           useSimpleAccessors: true  
       }  
}  

Lastly, use git to push the updates to the server. It will deploy the new code and restart the server. Launch your app from a Webkit browser or from your mobile device and you should be in business similar to what you see here.

Once the app is deployed to OpenShift, you can tail the logs by running the following:

$ rhc app tail -a <app-name>

You can also start and stop the server as follows:

$ rhc app [start | stop ] -a <app-name>

And to snapshot a particular revision and go back to it you run the following:

$ rhc app snapshot save -a <app-name>

$ rhc app restore -a <app-name> -f <path to snapshot file>

Conclusion

I hope you found this three part series useful. In addition to creating a Node.js app and front-ending it with a Mobile application, you saw how easy it is to deploy such an application to the cloud.

Mobile GeoLocation App in 30 minutes – Part 2: Sencha Touch

Overview

This is the second part of a three part series to show how we can use Sencha Touch (a mobile web JavaScript framework) to create the GUI to integrate with our Node.js REST API that we built in Part 1. As you may recall from the previous post, I’m tracking dogs and storing their coordinates in a MongoDB. Now we will create a mobile app that will list the dogs and when selected will show them on a Google Map relative to your current location. Let’s get started.

Connecting to Node.js using MVC

Connecting to Node or really any REST/JSONP service is fairly trivial. Since Sencha Touch supports MVC on the client side, I will illustrate how to create the application using that approach. This is very useful because it keeps with one of the main programming tenets: Separation of Concerns. In this way we can create concise JavaScript applications that break-up the application along the lines of model, view and controller components. First step is to create the model as follows:

Ext.define('MyApp.model.Pet', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            {
                name: 'name'
            },
            {
                name: 'description'
            },
            {
                name: 'longitude',
                type: 'float'
            },
            {
                name: 'latitude',
                type: 'float'
            },
            {
                name: 'coords'
            },
            {
                name: 'date',
                type: 'date'
            },
            {
                name: '_id'
            }
        ]
    }
});

Notice above that I’m defining a “Pet” model by extending the Ext.data.Model class and assigning the fields to the model. Additionally, I set types as needed so they aren’t considered strings. Next, we create the store that will populate the model:

Ext.define('MyApp.store.PetTracker', {
    extend: 'Ext.data.Store',
    requires: [
        'MyApp.model.Pet'
    ],
    config: {
        autoLoad: true,
        model: 'MyApp.model.Pet',
        storeId: 'PetTracker',
        proxy: {
            type: 'jsonp',
            url: 'http://localhost:8080/dogtag',
            reader: {
                type: 'json',
                idProperty: '_id',
                rootProperty: 'records',
                useSimpleAccessors: true
            }
        }
    }
});

The PetTracker Store uses a JSONP proxy and binds the output of the service to the model we previously created. Notice, that I simply put the URL to the Node.js URL binding that will list out all dogtags and use a JSON reader to put them in the model.

Binding the Model with the View and Controller

Now that I have the data, where do I put it in the GUI? Sencha Touch provides several mobile GUI widgets that look very much like native mobile apps. In my case, I decided to use the panel with a docked toolbar at the top. Let’s take a look:

Ext.define('MyApp.view.PetPanel', {
    extend:'Ext.Panel',
    alias: 'widget.petListPanel',
    config:{
        layout:{
            type:'fit'
        },
        items:[
            {
                xtype:'toolbar',
                docked:'top',
                title:'Pettracker'
            },
            {
                xtype:'list',
                store:'PetTracker',
                id:'PetList',
                itemId:'petList',
                emptyText: "<div>No Dogs Found</div>",
                loadingText: "Loading Pets",
                itemTpl:[
                    '<div>{name} is a {description} and is located at {latitude} (latitude) and {longitude} (longitude)</div>'
                ]
            }
        ],
        listeners:[
            {
                fn:'onPetsListItemTap',
                event:'itemtap',
                delegate:'#PetList'
            }
        ]
    },
    onPetsListItemTap:function (dataview, index, target, record, e, options) {
        this.fireEvent('petSelectCommand', this, record);
    }
});

In the example above, I create a panel with fit layout (aka, responsive web design) and put a toolbar and list component in it. To get the data in the list, all I have to do is set the store to the store name I created earlier (line 16). On line 22, you can see that I’m using expression language to templatize the output of the store data in each list row (look Ma, no FOR loops!). Finally, we need to create the Controller to respond to events in the code. In the View code, you see on line 36 that I fire the ‘petSelectCommand’ when a list item is tapped. Let’s look at the controller code for this.

Ext.define('MyApp.controller.PetTracker', {
    extend: 'Ext.app.Controller',
    markers: [],
    directionsDisplay: null,
    directionsService: null,
    config: {
        stores: ['PetTracker'],
        refs: {
            petListPanel: 'petListPanel',
            petList: '#PetList',
            petMap: 'petMap',
            radiusPicker: 'radiusPicker'
        },
        control: {
            petListPanel: {
                petSelectCommand: "onPetSelected"
            },
            petMap: {
                backButton: "onBackButton",
                mapRender: "onMapRender",
                nearButton: "onNear"
            },
            radiusPicker: {
                pickerChanged: "onPickerRadiusChange"
            }
        }
    },

    launch: function () {
        // Initialize Google Map Services
        this.directionsDisplay = new google.maps.DirectionsRenderer();
        this.directionsService = new google.maps.DirectionsService();

        var mapRendererOptions = {
            //draggable: true,  //Allows to drag route
            //hideRouteList: true,
            suppressMarkers: true
        };

        this.directionsDisplay.setOptions(mapRendererOptions);
    },

    // Transitions
    slideLeftTransition: { type: 'slide', direction: 'left' },
    slideRightTransition: { type: 'slide', direction: 'right' },

    onPetSelected: function (list, record) {
        var mapView = this.getPetMap();
        mapView.setRecord(record);
        Ext.Viewport.animateActiveItem(mapView, this.slideLeftTransition);


        this.renderMap(mapView, mapView.down("#petMap").getMap(), record.data);
    },

    onBackButton: function () {
        var store = Ext.getStore('PetTracker');
        store.getProxy().setUrl('http://nodetest-loutilities.rhcloud.com/dogtag/');
        store.load();
        Ext.Viewport.animateActiveItem(this.getPetListPanel(), this.slideRightTransition);
    },

    renderMap: function (extmap, map, record) {
        // erase old markers
        if (this.markers.length > 0) {
            Ext.each(this.markers, function (marker) {
                marker.setMap(null);
            });
        } 
        var position = new google.maps.LatLng(record.latitude, record.longitude);

        var dynaMarker = new google.maps.Marker({
            position: position,
            title: record.name + "'s Location",
            map: map,
            icon: 'resources/img/yellow_MarkerB.png'
        });

        this.markers.push(dynaMarker);
    }
});

Covering all that’s inside this controller is beyond the scope of this article and besides there’s already really good articles on this such as the one here. If we pick-up from the event we fired in the view, you can see on line 16 where I’ve bound the onPetSelected() method to the event. On line 47, you can see the implementation of that method where I do the slide transition to the map view panel and then render the map in that panel. Finally, there’s this small piece of code to bootstrap the application and launch it.

Ext.Loader.setConfig({
    enabled: true
});


Ext.application({
    models: [
        'Pet'
    ],
    stores: [
        'PetTracker'
    ],
    views: [
        'PetPanel',
        'MapPanel',
        'RadiusPicker'
    ],
    name: 'MyApp',
    controllers: [
        'PetTracker'
    ],
    launch: function() {
        var petList = {
            xtype: 'petListPanel'
        };
        var petMap = {
            xtype: 'petMap'
        };
        var radiusPicker = {
            xtype: 'radiusPicker'
        };
        Ext.Viewport.add([petList, petMap, radiusPicker]);
    }
});

Conclusion

Well there you have it: how to create a mobile web application integrated with Node.js and Google Maps. You can see this application in action by pointing your mobile device to this site, http://dogtags-loutilities.rhcloud.com/, or internally here. I hope you see the value in implementing client side MVC so that your code is concise and maintainable, especially if multiple developers were to work on it. You can view the complete code base here. Look for the final part of this series where I will show you how easy it is to deploy this application to the cloud using a PaaS like OpenShift, Heroku or Nodejitsu.

SenchaCon Day3: Debugging SenchaTouch

Evan Trimboli and Tommy Maintz give this talk.

Common JS Gotchas:

  • No compiler so all errors at runtime.  Be sure you check case and check spelling. Declare variables in the appropriate scope by declaring vars at the top of a function to avoid accidental local vars.  Careful using reserved words like “class”.
  • Truthiness & Equality: in JS there’s multiple ways things can be true/false rather than explicit.  Type coercion not performed by “===”.  Note: null/undefined are different (e.g., undefined means it doesn’t exist).
  • Closure you have to check for variable binding (outside the closure) and variable shadowing (response is an example in an ajax call).
  • No block scope only function scope (e.g., a variable in an IF block is global to the function)
  • Prototypal inheritance
  • Higher Order Functions can be passed to functions and be variables.

Tools to Debug

Chrome Debugger

  • Elements screen to look at position in DOM, see styles and their precedence. You can search for DOM ids and modify styles dynamically.
  • Scripts screen allows you to debug and view local and global variables. You can look at the call stack and set conditional breakpoints.
  • Resources screen shows size and time it took to load various artifacts that come over HTTP or XHR.
  • Console screen allows interactive commands and use log(), warn() and error() to log to the console

JSLint

  • Code Quality Tool to check for undefined or inadvertently declared global vars, required blocking, unreachable code, trailing commas and much more.

RemoteJS

  • Made by Webkit staff on Sencha team to help debug Android devices (on Github and blog).

Sencha Touch Specifics

On Data

  • Is the URL correct?? Did the request complete?? Is data valid??
  • Does the model align with the Component (from JSON for example).

Event Driven Model

  • Use appropriate callbacks
  • Remember that things are typically async

Overnesting

  • Panels with no layout
  • Specify layouts instead (be sure to specify layouts for Panel and use “fit”).  Use the DOM view in the Chrome debugger to see if elements have the correct height (common mistake is that they’ll be zero).

Dynamic Layouts

  • Remember doLayout and doComponent to reset layout after changes to the component

What is “this”

  • Log console.log(this) to be sure you’re dealing with the right object especially in callbacks and handlers.  You may not be in the right context.
  • All callbacks allow you to specify scope

Event Handling

  • Use capture and observe to check events.

SenchaCon Day3: SenchaTouch Performance Optimization

Tommy Mainz gives this talk.

Problems: DOM size, events, drawing, animation and code execution all affect performance on mobile devices.

DOM size:

  • Smaller screens help to keep DOM size smaller because there’s only so much data you can show without cognitive overload.
  • However, destroy components that you’re not using anymore in the deactivate. E.g. container.on(‘deactivate’, function(oldCard), { oldCard.destroy(); }); container.setActiveItem(newCard, ‘slide’); or overlay.on(‘hide’, function() { overlay.destroy(); });

Events – Solutions:

  • Use Event Manager from SenchaTouch which has automatic removal of listeners and delegation for Touch events.
  • Use event delegation when possible e.g. Ext.get(‘list’).on{ tap: function(e, target) {target.toggleCls(;pressed’); } delegate: ‘.list-item’)

Drawing: GPU

  • Keep your DOM structures “light” (especially in Android) by limited amount of advanced CSS3 (e.g., opacity, rgba, border-radius, gradients, text-shadow and box-shadows) and limiting the amount and depth of DOM elements.
  • Don’t go too deep: prevent unnecessary layouts. e.g. you have a tab panel with a panel of two tabs (using layout of fit) but instead should just use tabpanel with items being tabs.

Animations: GPU

  • Slide to a list e.g. on beforeActivate use list.setload(true) on activate remove loading mask (set it to false).  It will be more seamless and less burden on the

Code Execution: Compiler

  • Android v8 compiler is leading in this space
  • Not the usual suspects, like CPUs and JS engines
  • The reason is JIT compilation which is related to size of the file that the method exists in; complexity of method doesn’t seem to be relevant but comments/docs in code
  • Solution is to use minified and compressed code of both SenchaTouch and your own code.
  • You can set up builds to do this with .jsb3 files (JSBuilder is bundled with SenchaTouch)  you have builds and packages that will minify and consolidate those files.

SenchaCon Day1: Keynote

Abe Elias (Sencha’s CEO) gives the initial keynote.  Sencha Labs launched 2010 added Rafael, Connect and jQTouch this summer.  Just added invoVIS, MooTouch and CSS3PIE to the mix.

On mobile, we jam the internet onto a mobile device.  What we should (and will) be doing is creating amazing mobile interfaces for the Web that work on multiple devices.  Sencha wants HTML5 to be a huge success.  Their vision is to have all development hosted on the Web wherever, whenever.  Their strategy: 2010 they compiled the best WebKit engineers.  In 2011, they are going to work with device manufacturers and carries to be sure that you can use these webkit apps across many devices.

Next up…a few demos:

  • Marketo (UX Designer Glen Lipka) shows a rich experience using Smart Lists that leverages drag-n-drop to build interactive search to create grid reports on the fly.
  • WidgetBox shows how to build a mobile app in 3 minutes.  Adds mobile widgets built on SenchaTouch to a real live app.  Shows adding twitter, YouTube and polls on they fly.  Graphics show up on the fly.  Take a look at AppMakr too which is similar in concept.
  • SalesForce.com shows their “chatter” application for collaboration.  Showed another application built by a developer that hates doing UI development.  ExtJS gives them the ability to bring applications to the end user in a fraction of the time it used to take.

Sencha goes on to make a few announcements:

  • James Pearce from Sencha shows improvements to the community.  SenchaDevs will provide a directory of those developers working with Sencha by the end of Dec 2010.  A prospective client can search this listing to find developers.  This service will be provided for free.
  • Marketing director from Sencha shows some very impressive mobile apps built on Sencha Touch from their contest this fall.  One is a talking crossword puzzle (the winner!), another is TravelMate which shows a translation service and save phrases and currency conversion.  e-Resistable shows an e-delivery service for food items.  Pretty slick!  See all apps creating from this contest here.

SenchaTouch is designed to “unleash the power of HTML 5.” Rich media tools will be at the disposal as well as device access to things like SMS, Contacts, Camera, etc. Filesystem, DB, cache access.  Worker Parallel processing. x-App Messaging for interapp communication.  Sockets will allow you to go beyond HTTP protocol.

Things to look for in 2011:

  • SDKs: ExtJS4 (Jan 2011) – fast, HTML5 chartlog, and great data pkg. SenchaTouch rich touch experience and highly customizable. Touch 1.1  (Jan 11) RIM and Nokia will be added. and in 1.5 (July 11) will manage large data sets and New UX components.
  • Tools: UI Builder will morph into a AppBuilder to build fast apps.  Sencha Designer will also be able to build drag-n-drop applications that can build mobile apps in a matter of minutes!
  • Web Services: Integrate with several app services such as WebFonts, data services, and several others.

Free Upgrade commercial license from ExtJS 3!

And the BIG Announcement: SenchaTouch 1.0 is available and will be licensed commercially for FREE!!