| CARVIEW |
Tutorial
From Rhomobile
This tutorial describes how to use Rhodes and RhoSync to build native mobile apps for all shipping smartphones. Specifically it describes how to write Rhodes controllers and templates and how to write RhoSync source adapters to sync data from backend apps to the device. The architecture of this is shown here
Contents |
Building Your Rhodes Application
With a RhoSync source operational to retrieve data from your backend, you are now ready to write a Rhodes application to allow interaction with that data from your mobile device. Or if you skipped the RhoSync step, you can just go ahead and write your Rhodes app without synchronized data. Writing a Rhodes app consists of the following steps:
- checking out Rhodes
- generating Rhodes controllers for your data models
- editing the views for your controller actions
- testing your application
Checkout Rhodes
Rhodes is located on Github in the Rhomobile repository as the Rhodes project.
Make sure you install Ruby and Rubygems before continuing.
The first step in using Rhodes is to install the Rhodes gem (you may need to put "sudo" in the front of this command):
gem install rhodes
TIP: If you get any "no such file to load -- something" messages while running the rake tasks or rhogen commands, this can usually be resolved by running '[sudo] gem install something'.
TIP: If you come upon an Error saying "'make' is not a recognized internal or external command...", then you must install GnuWin32 in order to install the make command.
You can install rhodes gems from sources. To do that, you need checkout rhodes sources from github and run the following commands:
rake gem gem install pkg/rhodes-<version>.gem
Note: To do this you will need a Tar command There are some versions of tar utility on windows which does not support pipes and therefore does not support creating .tar.gz archives so you need to install one supporting them. For example, it could be BsdTar from the https://gnuwin32.sourceforge.net/ (note that it should be exactly BsdTar, not GnuTar)
You then need to set up your rhodes installation. This will allow you to specific where your SDKs for various platforms are installed. The installation script will display its best guess as to where the SDK is. You can then enter a new location, or leave it blank to use the suggestion. Note that before you run this you should install the SDKs that you need for each device. Details of that are in Building Rhodes on Supported Platforms
rhodes-setup
Generating A Rhodes Application
The first step is to generate the application and base files. This is done with the RhoGen (Rhodes Generator) utility, which is available by installing the Rhodes gem (which you can install with "gem install rhodes").
First we will generate an application called StoreManager with the following command:
rhogen app storemanager https://localhost:3000/apps/store/sources
This will generate an application directory called StoreManager with several files. The top level of the app is in the app/index.erb file. You can edit the content there to create what your app displays. Theoretically you could put the entire contents of your app in index.erb. Or you can link to other HTML files and ERB files from there with standard HTML techniques. Then you can build your app with "rake run:iphone" to see the results.
Note also that the third argument above ("https://localhost....") is the link to the sync server if you want to do synchronized data as we describe in the next section. Specifically it will set the rhoconfig.txt file to have the following option
syncserver = 'https://localhost:3000/app/store/sources'
If you do not need synchronized offline data you can leave that third argument out.
Rhodes also contains a powerful "model generator" that lets you generate models and the controllers and views to edit their data. So let's proceed with that before doing a build of our app.
Generating Models
Now move to the the new application directory and generate a model for our application. Let's call it "product" and give it some attributes.
cd storemanager rhogen model product brand,name,price,quantity,sku
You will see RhoGen create a controller (product_controller.rb) and several HTML templates (.erb files) for reading and editing objects of the specified model:
Generating with model generator:
[ADDED] app/Product/config.rb
[ADDED] app/Product/index.erb
[ADDED] app/Product/edit.erb
[ADDED] app/Product/new.erb
[ADDED] app/Product/show.erb
[ADDED] app/Product/product_controller.rb
Editing Rhodes Views
You can edit the generated ERB files to optimize the HTML as you see fit. A very common operation is to provide links to the top level of the models from the top level app page. For example, below is the generated top level index.erb file for the Store Manager app.
<div class="toolbar"> <h1 id="pageTitle"> Storemanager </h1> </div> <ul id="home" selected="true" title="Storemanager"> <li>Add links here...</li> </ul>
To provide a link to the Product object's generated controller and templates you can replace "Add links here" with:
<ul id="home" selected="true" title="Storemanager"> <li><a href="Product">Products</a></li> </ul>
Note that you can edit this top level page or any of the other pages with any arbitrary HTML you wish. We don't attempt to teach you HTML or Ruby here but there are many good external references for both topics.
Testing Your Application on Devices
If you are using the Rhodes gem (not using Rhodes installed from source), then to run your generated app on the iPhone simulator (assuming you have the Rhodes gem and the iPhone SDK installed) just run:
rake run:iphone
If you are using Rhodes source, or if you want to build for a different platform, see Building Rhodes on Supported Platforms for instructions on building and running your Rhodes app.
Doing More With Rhodes
There is a longer tutorial that uses the RhoHub hosted service for Rhodes development here There is a technical FAQ that describes how to perform certain specific tasks in Rhodes [1]. The following sections describe how to add synchronized data to our app with RhoSync.
Adding Synchronized Data to Your App - RhoSync
RhoSync is a synchronization engine consisting of a client component on the device and a server component that runs on any server capable of running Rails applications. RhoSync requires that you write a small amount of Ruby code for the query, create, update and delete operations of your particular enterprise backend. The collection of the Ruby code for these operations we refer to as a "source" or "source adapter". The RhoSync server provides a web interface that makes it easy to manage such sources.
Install the RhoSync Server
Here are instructions for installing RhoSync on MacOS or Linux. We also have a RhoSync Windows installer available. If you are feeling adventurous, we also have an installer for the 1-4-Unstable branch available here. Once installed you can then test that its working. Start your favorite Rails web server to run RhoSync. For example from the RhoSync directory execute:
On Unix (including Mac and Win gitbash or cygwin):
script/server
On Windows:
Rhosync is stopped/started via the services control panel. (Control panel -> Administrative Tools -> Services)
Then you can view the Sources from your web browser:
https://localhost:3000
If you want to use the sample application and sources that we ship with, just login as "admin" (default password is password). You will then see the apps "owned" by "admin": SugarCRM and Siebel. Clicking on SugarCRM will show you the sources that have been defined for SugarCRM: Accounts, Cases and Employees. Choosing any source and clicking Show Records will show you the data that would be synced to the device. More details of how to configure the sample to use your own SugarCRM are available here
The sections below describe how to create new source adapters for any backend app.
Defining RhoSync Source Adapters
Once RhoSync is installed we're ready to build a RhoSync source to integrate with our backend application. To define a RhoSync source you just need to identify a handful of operations to interact with your backend data source: login, query, sync, create, update, delete and logoff. A field in the form refers to source file which contains the class. This needs to be a file in the "vendor/sync" subdirectory or "lib" subdirectory of wherever you put the RhoSync source. You generate a skeleton for the source adapter class with RhoGen as described below (more details on this process are at https://wiki.rhomobile.com/index.php/Writing_RhoSync_Source_Adapters).
Generating A Source Adapter Class
First you will need the RhoGen application generator to generate a RhoSync source class. You can get this by installing the Rhodes gem as describe above.
Then run the source generator with the name of the source adapter class. For example (pick a name for your source adapter to replace sugaraccounts below):
rhogen source products
You'll see a file similar to the following one below. From there you'll fill in the login, query, create, update, delete and logoff methods with your own code. Note that you don't need to use the source generator. You can just create a Ruby file and place it into your lib directory. The class name must match that identified on the Source form as the Source Adapter class.
class Products < SourceAdapter def initialize(source) super(source) end def login end def query end def sync super end def create(name_value_list) end def update(name_value_list) end def delete(name_value_list) end def logoff end end
The description below shows what such code might look like. The code below is written with release 1.2 of RhoSync (as of this writing the 1-2-unstable branch which is also the master branch of RhoSync).
A RhoSync Query
If you're doing a readonly non-authenticated source adapter, you can get away with just writing one method, query, to retrieve records as we describe here. The following is a sample query method to interact with a simple product catalog backend app (available at https://rhostore.heroku.com) that exposes a REST interface. The index retrieves all products in a JSON return. Note that if it returned either XML or HTML (which it can of course) RhoSync could work with it. The query code would just be slightly different.
def query parsed=nil open("https://rhostore.heroku.com/products.json") do |f| parsed=JSON.parse(f.read) end @result={} parsed.each {|item| @result[item["product"]["id"].to_s]=item["product"]} if parsed @result end
@result is created as a "hash of hashes", indexed by the ID of each product, so that the base SourceAdapter class sync() method can populate the ObjectValues table.
A RhoSync Sync Method
The sync code takes apart the query results and puts them into the object_values table. You can write your own code to do this that creates ObjectValue objects and populates their object, attribute, value attributes (along with source_id and user_id attributes). But if you populate @result with a hash of hashes you can avoid this task and just have your sync method be:
def sync super end
Specifically SourceAdapter's sync expects a hash of hashes where the hash key of the outer hash is the ID of each object. Each hash key in the inner hash represents an attribute of an individual object. Note that all datatypes must be strings (so the hash values need to all be strings not integers). For example:
@result={"1"=>{"name"=>"inner tube","brand"=>"Michelin"},"2"=>{"name"=>"tire","brand"=>"Michelin"}}
Assuming your query/sync code is correct, once you click in Show you should see the resulting object/attribute/value triples coming back from the backend in your browser pointing the RhoSync web console.
Creating Objects with RhoSync
For your create method you can assume that the RhoSync server will pass you an array of hashes of name-value pairs entitled "name_value_list". For example it might be:
[{"name"=>"sku","value"=>"1"},{"name"=>"name","value"=>"inner tube"},{"name"=>"brand","value"=>"Michelin"},{"name"=>"price","value"=>"$4.99"},{"name"=>"quantity","value"=>"10"}]
Your code for create (or edit or delete) needs to use this populated array to do its work. Below is an example of a create method against the https://heroku.com/rhostore
def create(name_value_list) attrvals={} name_value_list.each { |nv| attrvals["product["+nv["name"]+"]"]=nv["value"]} # convert name-value list to hash res = Net::HTTP.post_form(URI.parse("https://rhostore.heroku.com/products/create"),attrvals) end
A RhoSync Login
If your backend application requires authentication, you will need to write a login method in your source adapter. The following is login code from the sample source adapter for SugarCRM.
u=@source.login pwd=Digest::MD5.hexdigest(@source.password) ua={'user_name' => u,'password' => pwd} ss=client.login(ua,nil) # this is a WSDL if ss.error.number.to_i != 0 puts 'failed to login - #{ss.error.description}' else @session_id = ss['id'] uid = client.get_user_id(session_id) end
Notice that you access parameters that have been set for the source as attributes of the @source variable. For example the login and password are available in @source.login and @source.password. Also note that the "client" variable contains the SOAP driver that methods are exposed on. So this method makes a SOAP call to "client.login" where client is set up by RhoSync based upon the URL provided to the source.
Also note that subsequent methods will use the @session_id variable to access the state of the current session. You can use whatever variable name you wish for the session but we suggest following this convention.
Available "Builtin" Variables
The following variables are available for your source adapter login method. You do not have to use any of them (for example, you can hardcode URLs to connect to and usernames and passwords for authentication), but they generally make it easier to write your login methods.
- client - the SOAP driver created from the WSDL file at @source.url. This is not relevant if you are using a REST API
- @source.url - the URL where the backend is exposed. Typically used if you are calling a REST API
- @source.login - the userid to login to the backend with if its "global" (not using per user credentials)
- @source.password - the global login password to login to a source with
- @source.credential.login - the login of the currently logged in user
- @source.credential.password - the password of the currently logged user
- @source.credential.url - the URL that can be used on a per user basis. You can still use @source.url if the URL to connect to doesnt vary per user
Testing Your Rhosync Adapter
You can use the web interface to test that you are in fact retrieving data from your backend. Click on Show Records from the source adapter console to view the "object-attribute-values" coming from your backend data source. You can also edit, create and delect objects from this same web interface.
Note that in order to use your source adapter, you will need to setup an Application.y Then you will need to add a Source to the application which as a Adapter Class that is the class in your source adapter file. You will also need to create users and subscribe them to the app. Once the app is built in Rhodes those users will need to log in from their device. In the default scaffold generated app, the user login is performed from the Options menu, Login screen.
Delegated Authentication
RhoSync has built-in authentication, but in most real enterprise deployments it is best to use "delegated authentication" where you ask the backend to do authentication for you. This involves writing a class with the same name as your app and putting it in the base directory where all your source adapters are located. This is documented here.
Using RhoSync from Your Rhodes App
Recall from the Rhodes section above that you generated the app to have a connection to your RhoSync server. If you want to test your synchronized data from your RhoSync app, start up your RhoSync server as described above (e.g. with "script/server" from the RhoSync directory) making sure that your source adapter is installed and has already been tested from the web interface.
Now make sure that the rhoconfig.txt file for your Rhodes app corresponds to the where you have installed your RhoSync (the URL shown above starting with https://localhost:3000 should work). Now run your Rhodes app from an emulator. For example with:
rake run:iphone
Choose the settings icon in the lower right. Click on Login and supply a username and password that is set up on your RhoSync server. This can be the builtin admin account for testing. Or you can register another user. If you have setup delegated authentication as described above you'll want it to be a user in your backend app.
Customizing Sync Login
It is very common to want to change where the login happens in your app. For example you can put it onto your top page (app/index.erb) with something like the code below. This is taken from the Settings controller index.erb template. In general the Settings controller and associated templates show the login and sync flow well.
<% if SyncEngine::logged_in > 0 %> <li><%=link_to "Logout", :action => :logout%></li> <% else %> <li><%=link_to "Login", :action => :login%></li> <% end %>
This will display a login dialog if the user is not logged in and display logoff if they are not.
Your login action in the controller will look something like this (again from the Settings controller that is in your generated app):
def do_login
if @params['login'] and @params['password']
begin
SyncEngine.login(@params['login'], @params['password'], (url_for :action => :login_callback) )
render :action => :wait
rescue Rho::RhoError => e
@msg = e.message
render :action => :login, :query => {:msg => @msg}
end
else
@msg = "You entered an invalid login/password, please try again." unless @msg && @msg.length > 0
render :action => :login, :query => {:msg => @msg}
end
end
The callback that you write (which gets executed when login completes) will look something like this:
def login_callback
errCode = @params['error_code'].to_i
if errCode == 0
# run sync if we were successful
WebView.navigate Rho::RhoConfig.start_path
SyncEngine.dosync
else
if errCode == Rho::RhoError::ERR_CUSTOMSYNCSERVER
@msg = @params['error_message']
end
if !@msg || @msg.length == 0
@msg = Rho::RhoError.new(errCode).message
end
WebView.navigate ( url_for :action => :login, :query => {:msg => @msg} )
end
end
Anonymous Synchronized Data
The typical scenario for enterprise apps is that you want each user to login with their own credentials. However, many apps (especially consumer apps) may benefit from synchronized information and you don't want the user to have to login. In this case you can just create a login for the whole app and just execute the sync login on behalf of the user. For example you can do something like the following in the index method of your main object's controller:
class YourController
def index
if SyncEngine::logged_in == 0
SyncEngine.login("yourappuser", "yourapppassword", (url_for :controller=>"Settings",:action => :login_callback) )
redirect :controller=>"Settings", :action => :wait
end
...
end
end
More Info
There is a more indepth tutorial for RhoHub that may have information that even "offline" Rhodes developers may appreciate. The technical Frequently Asked Questions describes how to perform various tasks in Rhodes. To see each Rhodes capability demonstrated see the API samples
There is also the following book on Rhomobile. For more questions, write to us or join the Rhomobile Google Group and post.
Views
Personal tools
- This page was last modified on 9 March 2010, at 15:05.
- This page has been accessed 55,020 times.
- Privacy policy
- About Rhomobile
- Disclaimers
