Sencha Touch application with Ruby on Rails – Part 1

Hi,

As promised I’m back with an example application written in Ruby on Rails and Javascript built on the Sencha Touch framework.  This is a Contacts application with a “checkout” functionality.  I’ve had to split it into two blog posts due to size. This is what we are trying accomplish:

  • Add a new contact via the Web interface or via the device (pad, phone) interface
  • Contacts that have been added on the device are stored in local storage
  • Pressing “Sync” on the device will push unsync’ed data from the device to the web storage
  • List all contacts that are stored locally on the phone
  • List all contacts that are stored on the server in the “Catalog”
  • Upon clicking an entry in the Catalog it is checked out to your local storage

Here are some screen shots of the final product – Yellow contacts are un-synced.  Catalog not shown.

Let’s get started

Side Note: I recommend using Safari for development of Sencha Touch applications. Safari’s Develop menu gives it an advantage over other browsers in my opinion.  Although I’ve heard Chrome is quite good as well:

  1. If you do not see a Develop menu at the top of the screen between Bookmarks and Window go to Preferences (Edit/Preferences on Win, Safari/Preferences on Mac).  From the Preferences menu select Advanced.  From the Advanced page select “Show Develop menu in menu bar”
  2. From Develop/User Agent you can view the site as if you are viewing it from a mobile device.  I’m not sure if other browsers have this but it is a killer feature
  3. From Develop/Show Web Inspector you can view a very nice debugger panel.  The Web Inspector panel provides a tab that displays the local storage.  It also provides a console tab where you can type in JavaScript command and test code against you application. This has proved to be invaluable when trying to debug the Sencha Touch application.

Rails Application

Create the Rails Application

First thing, create your Rails app. The Ruby On Rails server app in this example is minimal.  It was created as a scaffold and the controller has been customized to handle the xml going back and forth. From the command line the following will create your Rails application and database.

$ rails new contacts
$ cd contacts
$ rails g scaffold contact first_name:string last_name:string email:string phone:string
$ rake db:migrate
$ rails s

Navigate to http://localhost:3000.  You should see the rails welcome page.  Next we’re going to update routes.rb so that the root points to your index page for your contacts.  First delete the index.html page in the public directory. Then update config/routes.rb to set the root to contacts/index.

ContactsDemo::Application.routes.draw do
  root :to => 'contacts#index'
  resources :contacts
end

Update the Controller

Open up app/controllers/contacts_controllers.rb. The controller needs to be updated so that it plays nicely with your Sencha Touch app. There are two things you’re trying to do with the Rails code:

  1. Update the index so it delivers an xml list of your contacts
  2. Update the create so that new contacts are created or updated accordingly.  NOTE: This is a very high-level approach to synchronizing records between two data sources. If you are familiar with Version Control (Subversion and Git) then you have seen synchronization done at a very deep level. This application will not check at the field level for collisions or anything on that order. Simply put: The application will treat any incoming record as the latest and will update the online DB with the entire record.

Index (Update: add dasherize => false. This will make your xml send first_name rather than first-name)

def index
  @contacts = Contact.all
  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render : xml => @contacts, :dasherize => false }
                # Note: WordPress workaround: do not put a space between the colon and xml
                # WordPress was doing funny things with colonxml so I had to put a space
  end
end

Add 3 new methods to the end of the controller: parse_sencha_xml, update_record, and parse_contact_params

def parse_sencha_xml
  p_hash = params[:xmlData][:contact]
  return nil if p_hash.nil? || p_hash.empty?
  # call update if the remote_id is populated, means it's already in the db
  if p_hash['remote_id'].to_i > 0
    return update_record(p_hash)
  end
  contact = Contact.create!(parse_contact_params(p_hash))
  return contact
end

def update_record(p_hash)
  id = params[:xmlData][:contact]['remote_id']
  c = Contact.find(id)
  update_params = parse_contact_params(p_hash)
  c.update_attributes(update_params)
  return c
end

def parse_contact_params(p_hash)
  contact_hash = Hash.new
  attr = Contact.new.attributes
  p_hash.each do |key, value|
    if (key == 'id' || key == 'remote_id' )
    # skip, this is the device PK or the db PK.  The db PK has already been captured
    elsif attr.has_key?(key)
      contact_hash[key] = value
    end
  end
  return contact_hash
end

Finally, update the Create method to use the new methods. It will now look for matching records before adding a new record. If it finds one, it will update it.  Update create so that it calls the new methods

def create
  respond_to do |format|
    format.html {
      @contact = Contact.new(params[:contact])
      if @contact.save
        redirect_to root_path
      else
        render :action => 'new'
      end
    }
    format.xml {
      @contact = parse_sencha_xml
      render : xml => @contact, :status => :created, :dasherize => false
             # Note: WordPress workaround: do not put a space between the colon and xml
# WordPress was doing funny things with colonxml so I had to put a space
    }
  end
end

Routing and viewing both Mobile and Standard sites

Now that our controller is updated there are a few things we need to do to take care of routing and add the ability to view mobile or standard sites.

Update app/controller/application_controller to allow for mobile or standard display. The following is pulled directly from Railscasts episode 199. Rails will evaluate the user_agent from the incoming request and true if it contains “Mobile” (Android or iOS) or “webOS” (Palm)

class ApplicationController < ActionController::Base
  helper :all
  protect_from_forgery

  private
    def mobile_device?
      user_agent = request.user_agent
      user_agent =~ /Mobile|webOS/
    end
    helper_method :mobile_device?

end

Update the view so that the page displays in a browser. First update the index page.  The new mobile_device? method is used to determine which part of the index page to display.

<% if(mobile_device?) %>

 <!-- Code Viewable to mobile devices -->
 <%= content_for :head do %>
 <%= stylesheet_link_tag 'sencha-touch-debug' %>
 <%= javascript_include_tag 'touch_1_0_1/sencha-touch-debug' %>

 <%= javascript_include_tag 'app/app' %>
 <%= javascript_include_tag 'app/util' %>
 <%= javascript_include_tag 'app/models/Contact' %>
 <%= javascript_include_tag 'app/views/ContactCatalog' %>
 <%= javascript_include_tag 'app/views/ContactEdit' %>
 <%= javascript_include_tag 'app/views/ContactNew' %>
 <%= javascript_include_tag 'app/views/ContactShow' %>
 <%= javascript_include_tag 'app/views/ContactsList' %>
 <%= javascript_include_tag 'app/views/Viewport' %>
 <%= javascript_include_tag 'app/controllers/contacts' %>

 <style>
 .synced {
 background-color: #EDE613;
 }
 </style>

 <% end %>

<% else %>
 <%= content_for :head do %>
 <%= stylesheet_link_tag 'scaffold' %>
 <%= javascript_include_tag :defaults %>
 <% end %>

 <!--Add code from index.html.erb -->
 ...

<% end %>

Next update app/views/layouts/application.html.erb to remove a couple of lines that you don’t need for mobile browsers

<!DOCTYPE html>
<html>
<head>
 <title>ContactsDemo</title>
 <%= csrf_meta_tag %>
 <%= yield :head %>
</head>
<body>

<%= yield %>

</body>
</html>

That’s it for the rails application. When you view the page in a standard browser you should see the index page that lists all of your contacts.  If you change the User Agent (Safari) to Mobile Safari 4.1 you will see a blank gray screen. If something appears to explode when you switch to Mobile Safari remove the javascript imports that start with ‘app/… I can’t remember if Safari gets made if you ask it to import something that is not there. I will be posting the Sencha Touch application next time.

Thanks for reading!

References:

Excellent MVC Tutorial with Sencha Touch and PhoneGap

Railscasts episodes 199, 247, and 248 – by Ryan Bates.  Totally amazing gift of time, energy, and knowledge from Ryan, an absolute must watch for Rails developers.  If you want to do it in Rails, Ryan has probably already done it and has a nice succinct video that explains it to you.

Advertisements

7 thoughts on “Sencha Touch application with Ruby on Rails – Part 1

  1. you realy should share the source code so that everyone else could have a look at how it’s done.

    congrats on the tutorial!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s