How to Interact with a Database using Rhom

Exclusive offer: get 50% off this eBook here
Rhomobile Beginner's Guide

Rhomobile Beginner's Guide — Save 50%

Step-by-step instructions to build an enterprise mobile web application from scratch

$26.99    $13.50
by Abhishek Nalwaya | July 2011 | Open Source

ORM stands for Object-Relational Mapping. ORM libraries map database tables to classes, rows of those tables to objects of the class, and columns to object attributes. Object wraps a row of a database table or view, encapsulates the database access, and adds domain logic on that data.

In this article by Abhishek Nalwaya, author of Rhomobile Beginners Guide, we'll explore Rhom, which is Object-Relational Mapping (ORM) provided by Rhodes and look at how it manipulates data in our application. We'll find how ORM manages table relationships in this article and dig into the ORM object life cycle.

 

Rhomobile Beginner's Guide

Rhomobile Beginner's Guide

Step-by-step instructions to build an enterprise mobile web application from scratch

        Read more about this book      

(For more resources on this topic, see here.)

What is ORM?

ORM connects business objects and database tables to create a domain model where logic and data are presented in one wrapping.

In addition, the ORM classes wrap our database tables to provide a set of class-level methods that perform table-level operations. For example, we might need to find the Employee with a particular ID. This is implemented as a class method that returns the corresponding Employee object. In Ruby code, this will look like:

employee= Employee.find(1)

This code will return an employee object whose ID is 1.

 

Exploring Rhom

Rhom is a mini Object Relational Mapper (ORM) for Rhodes. It is similar to another ORM, Active Record in Rails but with limited features. Interaction with the database is simplified, as we don't need to worry about which database is being used by the phone. iPhone uses SQLite and Blackberry uses HSQL and SQLite depending on the device.

Now we will create a new model and see how Rhom interacts with the database.

 

Time for action – Creating a company model

We will create a model company. In addition to a default attribute ID that is created by Rhodes, we will have one attribute name that will store the name of the company.

Now, we will go to the application directory and run the following command:

$ rhogen model company name

which will generate the following:

[ADDED] app/Company/index.erb
[ADDED] app/Company/edit.erb
[ADDED] app/Company/new.erb
[ADDED] app/Company/show.erb
[ADDED] app/Company/index.bb.erb
[ADDED] app/Company/edit.bb.erb
[ADDED] app/Company/new.bb.erb
[ADDED] app/Company/show.bb.erb
[ADDED] app/Company/company_controller.rb
[ADDED] app/Company/company.rb
[ADDED] app/test/company_spec.rb

We can notice the number of files generated by the Rhogen command.

Now, we will add a link on the index page so that we can browse it from our homepage.

Add a link in the index.erb file for all the phones except Blackberry. If the target phone is a Blackberry, add this link to the index.bb.erb file inside the app folder. We will have different views for Blackberry.

<li>
<a href="<%= url_for :controller => :Company %>"><span class
="title"> Company</span><span class="disclosure_indicator"/></a>
</li>

Rhomobile Beginners Guide

We can see from the image that a Company link is created on the homepage of our application. Now, we can build our application to add some dummy data.

Rhomobile Beginners Guide

You can see that we have added three companies Google, Apple, and Microsoft.

What just happened?

We just created a model company with an attribute name, made a link to access it from our homepage, and added some dummy data to it. We will add a few companies' names because it will help us in the next section.

Association

Associations are connections between two models, which make common operations simpler and easier for your code. So we will create an association between the Employee model and the Company model.

 

Time for action – Creating an association between employee and company

The relationship between an employee and a company can be defined as "An employee can be in only one company but one company may have many employees". So now we will be adding an association between an employee and the company model. After we make entries for the company in the company model, we would be able to see the company select box populated in the employee form.

The relationship between the two models is defined in the employee.rb file as:

belongs_to :company_id, 'Company'

Here, Company corresponds to the model name and company_id corresponds to the foreign key.

Since at present we have the company field instead of company_id in the employee model, we will rename company to company_id.

To retrieve all the companies, which are stored in the Company model, we need to add this line in the new action of the employee_controller:

@companies = Company.find(:all)

The find command is provided by Rhom, which is used to form a query and retrieve results from the database. Company.find(:all) will return all the values stored in the Company model in the form of an array of objects.

Now, we will edit the new.erb and edit.erb files present inside the Employee folder.

<h4 class="groupTitle">Company</h4>
<ul>
<li>
<select name="employee[company_id]">
<% @companies.each do |company|%>
<option value="<%= company.object%>"
<%= "selected" if company.object == @employee.
company_id%>
>
<%=company.name %></option>
<%end%>
</select>
</li>
</ul>

If you observe in the code, we have created a select box for selecting a company. Here we have defined a variable @companies that is an array of objects. And in each object we have two fields named company name and its ID. We have created a loop and shown all the companies that are there in the @companies object.

Rhomobile Beginners Guide

In the above image the companies are populated in the select box, which we added before and it is displayed in the employee form.

What just happened?

We just created an association between the employee and company model and used this association to populate the company select box present in the employee form.

As of now, Rhom has fewer features then other ORMs like Active Record. As of now there is very little support for database associations.

 

Rhomobile Beginner's Guide Step-by-step instructions to build an enterprise mobile web application from scratch
Published: July 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

 

        Read more about this book      

(For more resources on this topic, see here.)

Exploring methods available for Rhom

Now, we will learn various methods available in Rhom for CRUD operation. Generally, we need to Create, Read, Update, and Delete an object of a model. Rhom provides various helper methods to carry out these operations:

  • delete_all: deletes all the rows that satisfy the given conditions.
    Employee.delete_all(:conditions => {gender=>'Male'})

    The above command will delete all the male employees.

  • destroy: this destroys the Rhom object that is selected.
    @employee = Employee.find(:all).first
    @employee.destroy

    This will delete the first object of employees, which is stored in @employee variable.

  • find: this returns Rhom object(s) based on arguments.

We can pass the following arguments:

  • :all: returns all objects from the model
  • :first: returns the first object
  • :conditions: this is optional and is a hash of attribute/values to match with (i.e. {'name' => 'John'})
  • :order: it is an optional attribute that is used to order the list
  • :orderdir: it is an optional attribute that is used to order the list in the desired manner ('ASC' - default, 'DESC' )
  • :select: it is an optional value which is an array of strings that are needed to be returned with the object
  • :per_page: it is an optional value that specifies the maximum number of items that can be returned
  • :offset: it is an optional attribute that specifies the offset from the beginning of the list
  • Example:
    @employees = Employee.find(:all, :order => 'name', :orderdir => 'DESC')

    This will return an array of employee objects that are ordered by name in the descending order:

    Employee. find( :all,:conditions =>["age > 40"], :select => [name,company] )

    It will return the name and company of all the employees whose age is greater than 40.

  • new : Creates a new Rhom object based on the provided attributes, or initializes an empty Rhom object.
    @company = Company.new({'name'=>'ABC Inc.')

    It will only create an object of Company class and will save to the database only on explicitly saving it.

  • save : Saves the current Rhom object to the database.
    @company.save

    It will save company object to the database and returns true or false depending on the success of the operation.

  • Create : Creates a new Rhom object and saves it to the database. This is the fastest way to insert an item to a database.
    @company = Company.create({'name' => 'Google'})

    It will insert a row with the name "Google" in the database.

  • Paginate: It is used to display a fixed number of records on each page.
    paginate(:page => 1, :per_page => 20)

    It will return records numbered from 21 to 40.

  • update_attributes(attributes): Updates the specified attributes of the current Rhom object and saves it to the database.
    @employee = Employee.find(:all).first
    @employee. update_attributes({'age' => 23})

    The age of the first employee stored in the database is updated to 23.

We have now understood all the basic helper methods available in Rhom that will help us to perform all the basic operations on the database. Now we will create a page in our application and then use the find method to show the filtered result.

 

Time for action – Filtering record by company and gender

We will create a page that will allow us to filter all the records based on company and gender, and then use the find command to show the filtered results on the next page.

We will follow these steps to create the page:

  1. Create a link for filter page on the home page i.e. index.erb in the app folder:

    <li>
    <a href="<%= url_for :controller => :Employee, :action => :
    filter_employee_form %>"><span class="title"> Filter Employee </
    span><span class="disclosure_indicator"/></a>
    </li>

    Rhomobile Beginners Guide

    We can see in the screenshot that a Filter Employee link is created on the home page.

  2. Create an action filter_employee_form in employee_controller.rb:
    def filter_employee_form
    @companies = Company.find(:all)
    end

    We have used the find helper provided by Rhom that will retrieve all the companies and store them in @companies.

  3. Create a page filter_employee_form.erb in the app/Employee folder and write the following code:

    <div class="pageTitle">
    <h1>Filter Page</h1>
    </div>

    <div class="toolbar">
    <div class="leftItem backButton">
    <a class="cancel" href="<%= url_for :action => :index
    >">Cancel</a>
    </div>
    </div>

    <div class="content">
    <form method="POST" action="<%= url_for :controller =>
    :Employee, :action => :filter_employee_result %>">

    <h4 class="groupTitle">Gender</h4>
    <ul>
    <li><label for="gender">Male</label>
    <input type="radio" name="gender" value="Male"/>
    </li>
    <li><label for="gender">Female</label>
    <input type="radio" name="gender" value="Female"/>
    </li>
    </ul>
    <h4 class="groupTitle">Company</h4>
    <ul>
    <li>
    <select name="company_id">
    <% @companies.each do |company|%>
    <option value="<%= company.object%>"
    >
    <%=company.name %></option>
    <%end%>
    </select>
    </li>
    </ul>

    <input type="submit" class="standardButton" value="Filter" />
    </form>
    </div>

    As we can see, this page is divided into three sections: toolbar, title, and content. If we see the content section, we have created radio buttons for Gender and a select box to list all the companies. We can select either Male or Female from the radio button and one company from the list of dynamically populated companies.

    Rhomobile Beginners Guide

  4. Create an action filter_employee_result in employee_controller.rb:

    def filter_employee_result
    @employees = Employee.find(:all, :conditions =>{'gender' => @
    params['gender'],'company_id'=> @params['company_id']})
    end

    The conditions symbol in the find statement is used to specify the condition for the database query and @params is a hash that contains the selections made by the user in the filter form. @params['gender'] and @params['company'] contains the gender and company_id is selected on the filter page.

  5. Create a file called filter_employee_result.erb and place it in the app/Employee folder.

    <div class="pageTitle">
    <h1>Filter by Company and Gender</h1>
    </div>

    <div class="toolbar">
    <div class="leftItem regularButton">
    <a href="<%= Rho::RhoConfig.start_path %>">Home</a>
    </div>
    <div class="rightItem regularButton">
    <a class="button" href="<%= url_for :action => :new %>">New</a>
    </div>
    </div>

    <div class="content">
    <ul>
    <% @employees.each do |employee| %>
    <li>
    <a href="<%= url_for :action => :show,
    :id => employee.object %>">
    <span class="title"><%= employee.name %>
    </span><span class="disclosure_indicator"></span>
    </a>
    </li>
    <% end %>
    </ul>
    </div>

This result page is again divided into three sections: toolbar, title, and content. All the employees filtered on the basis of specified selections made on the filter page are stored in @employees and displayed inside the content section of this page.

Rhomobile Beginners Guide

What just happened?

We created a filter page to filter all the employees on the basis of their gender and company. Then, by using the find method of Rhom, we filtered employees for the specified gender and company and displayed the results on a new page.

 

Rhomobile Beginner's Guide Step-by-step instructions to build an enterprise mobile web application from scratch
Published: July 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

 

        Read more about this book      

(For more resources on this topic, see here.)

Have a go hero – find (*args) Advanced proposal

We have learnt in this section to use find helper to write only simple queries. To write advanced queries, Rhom provides find (*args) Advanced.

A normal query would look like this:

@employees = Employee.find(:all, :conditions =>{'gender' => 
@params['gender'],'company_id'=> @params['company_id']})

This can also be written as:

@employees = Employee.find(:all, :conditions =>{
{:name =>'gender' ,:op =>"like"} => @params['gender'],
{:name =>'company_id', :op => 'like'}=> @params['company_id']},
:op => 'AND'
)

The advantage of using the latter form is that we can write advanced options with our query.

Let's say we want to create a hash condition for the following SQL:

find( :all,
:conditions =>["LOWER(description) like ? or
LOWER(title) like ?", query, query],
:select => ['title','description'] )

It can be written in this way:

find( :all,
:conditions => { {:func=>'LOWER', :name=>'description',
:op=>'LIKE'}=>query,
{:func=>'LOWER', :name=>'title', :op=>'LIKE'}=>query}, :op => 'OR',
:select => ['title','description'])

 

How Rhodes stores data

As we have already discussed, iPhone and Android use SQLite. And for Blackberry it uses SQLite on a device it supports, otherwise it will use HSQL database. But the question is how does Rhom store data and how can we handle migration?

Rhodes provides two ways to store data in a phone:

  1. Property Bag
  2. Fixed Schema

Property Bag

Property Bag is the default option available for our models. In Property Bag, the entire data is stored in a single table with a fixed number of columns.

The table contains the following columns:

  • Source_id
  • attribute
  • object
  • value
  • update_type

When you use the Property Bag model, you don't have to track schema changes (adding or removing attributes). However, Rhodes uses the Property Bag schema to store app data in a SQL database. If the internal Property Bag schema changes after an application is updated or reloaded, the database will be (re)created and all existing data would be erased. See rhodes\lib\rhodes.rb and rhodes\lib\framework\rhodes.rb for the internal database schema version:

DBVERSION = '2.0.3'

On the first launch of the application after it is installed/updated/reloaded, a database will be (re)created if app_db_version in the rhoconfig.txt is different from what it was before. If the database version is changed and the database is recreated, then all data in the database will be erased.

Since Rhodes 2.2.4, the Rhosync session is kept in the database, so SyncEngine.logged_in will return true. At the start of the application we can check if the database is empty and the user is still logged in and then run sync without interactive login.

Application db version in rhoconfig.txt:

app_db_version = '1.0'

We can list a few advantages and disadvantages of the fixed schema:

Advantages

  1. It is simple to use, attributes are not required to be specified before use
  2. We don't need to migrate data if we add/remove attributes

Disadvantages

  1. Size is three times bigger than the fixed schema
  2. Slow while synchronizing

Fixed Schema model

While using the Fixed Schema model, the developer is entirely responsible for the structure of the SQL schema. So when you add or delete some properties, or just change app logic you may need to perform data migration or database reset. To track schema changes, use the schema_version parameter in the model:

class Employee
include Rhom::FixedSchema
set :schema_version, '1.1'
end

We can see that we have set the schema version to 1.1. Now, if we change the schema then we have to change the version in the model.

We can list a few advantages and disadvantages of the fixed schema:

Advantages

  • Smaller size, you can specify index for only required attributes.
  • Faster sync time than Property bag.

Disadvantage

  • You have to support all schema changes.

This is how the model looks in a Fixed Schema:

class Employee
include Rhom::FixedSchema
# Uncomment the following line to enable sync with Employee.
# enable :sync
set :schema_version, '1.1'
property :name, :string
property :age, :string
property :company, :string
property :address, :string
property :gender, :string
property :salary, :string
#add model specifc code here
end

To add a column in the table we have to add a property and then reset the Device. It is important to note that we have to reset the database to reflect the changes.

To create an index in fixed schema we will write following line in model:

index :by_name_tag, [:name, :tag] #will create index for name and tag columns

To create a primary key in the fixed schema we will write following line in model:

unique_index :by_phone, [:phone]

Choosing between Property Bag or Fixed Schema depends on your requirement. If your schema is not fixed and keeps changing then use Property Bag and if we have huge data and non-changing schema then we use Fixed Schema.

Summary

We have the covered following topics in this article:

  • What is ORM
  • What is Rhom
  • What is an Association
  • We have explored various commands provided by Rhom
  • Difference between Property Bag and Fixed Schema.

Further resources on this subject:


About the Author :


Abhishek Nalwaya

Abhishek Nalwaya is the author of the book, Rhomobile Beginner's Guide. He is a Ruby enthusiast and loves to participate regularly at Ruby and Ruby on Rails meetup groups. He works for a Management Consulting firm. He has spoken at many conferences, meetups, and was the speaker at RubyConf India 2012 and RubyMotion Conference 2013.

Books From Packt


iPhone JavaScript Cookbook
iPhone JavaScript Cookbook

Cocos2d for iPhone 0.99 Beginner's Guide
Cocos2d for iPhone 0.99 Beginner's Guide

BlackBerry Enterprise Server 5 Implementation Guide
BlackBerry Enterprise Server 5 Implementation Guide

MeeGo 1.0 Mobile Application Development Cookbook
MeeGo 1.0 Mobile Application Development Cookbook

jQuery Mobile First Look
jQuery Mobile First Look

Sencha Touch 1.0 Mobile JavaScript Framework
Sencha Touch 1.0 Mobile JavaScript Framework

Android 3.0 Application Development Cookbook
Android 3.0 Application Development Cookbook

Xcode 4 iPhone Development Beginner's Guide
Xcode 4 iPhone Development Beginner's Guide


Your rating: None Average: 4 (1 vote)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
t
r
q
D
k
3
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software