Creating Database aware applications in Ruby on Rails

Introduction

In this article, we will explore the capabilities of Ruby with respect to the Data Tier. One can understand the power of Ruby which greatly simplifies the development of data aware applications after reading this article. This is mainly because of the abstraction introduced in Ruby in the form of Active Record. Active Record defines the object relational mapping between Ruby objects and Database tables. The first section of this article will deal with Active Record and how to work with CRUD operations using it. The later section of the article guides in developing a full-blown web application using the Active Record API.

Active Record Basics

There are various implementations for Object Relation mapping model available in Ruby. One such popular implementation is Active Record. Usage of Active Record API in an application will lead to writing fewer lines of code. Active Record also provides various utility classes for directly creating tables from the application using simpler syntax. The mapping between Ruby classes and database tables can be elegantly done. Active Record follows the convention of having plaral names for the table name and singular name for the class names. For example, one can say that the class ‘Employee’ will map directly to ‘employees’ table in Active Record context. It is also possible to dynamically generate method names through Active Record, the example of which will be seen later in this article.

Creating records

In this section, we will see the basics of using Active Record API. More specifically we will see how to map Ruby classes and database table for creating records in the database. Note that this example using MySql database, so make sure before using the application, a compatible version of MySql gem is installed in your machine.

require "logger"

require "rubygems"
require "active_record"
require "pp"

ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Base.establish_connection(:adapter => "mysql" ,
:database => "ruby", :username => "root", :password => "XXX")

class Bank < ActiveRecord::Base
end

Bank.delete_all

hdfc_bank = Bank.new();
hdfc_bank.id = '1';
hdfc_bank.name = "HDFC Bank";
hdfc_bank.operation_date = Date.today;
hdfc_bank.head_office = "Mumbai";
hdfc_bank.save;
puts ("HDFC Bank object created");

sbi_bank = Bank.new();
sbi_bank.id = '2';
sbi_bank.name = "SBI Bank";
sbi_bank.operation_date = Date.today;
sbi_bank.head_office = "Bangalore";
sbi_bank.save;
puts ("SBI Bank object created");

icici_bank = Bank.new();
icici_bank.id = '3';
icici_bank.name = "ICICI Bank";
icici_bank.operation_date = Date.today;
icici_bank.head_office = "Delhi";
icici_bank.save;
puts ("ICICI Bank object created");

The first few statements import the necessary dependencies packages such as ‘logger’ and ‘active_record’. We have defined a logger that points to the standard console. A connection is established to the MySql database using the method call ‘establish_connection’ by passing in the database adapter name, the database name and username/password details. It is assumed that a table with the name ‘banks’ exist in the database for this example. Next we have defined a class called ‘Bank’ which extends from ‘ActiveRecord::Base’. This single line will ensure that a mapping is established between the Ruby class ‘Bank’ and the database table ‘banks’.
Since the Bank class extends from ‘ActiveRecord::Base’, most of the common database CRUD operations become implicitly available to the Bank class. One such operation is delete_all() which will remove all the bank records from the database. Note that the banks table has the columns ‘ID’, ‘NAME’, ‘OPERATION_DATE’ and ‘HEAD_OFFICE’ which will directly map to the properties ‘id’, ‘name’, ‘operation_date’ and ‘head_office’ of the Bank class.
A new empty bank record is created using the statement Bank.new(), after that we initialize various properties of the bank object. A call to save() on the Bank object will persist the entity to the database.

Finding Records

In the previous section, we have seen how to use the Active Record API for inserting data into the database. In this section, we will see how to find data using easy-to-use predefined data-aware methods.

require "logger"

require "rubygems"
require "active_record"
require "pp"

ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Base.establish_connection(:adapter => "mysql" ,
:database => "ruby", :username => "root", :password => "XXX")

class Bank < ActiveRecord::Base
	
end


bank_object = Bank.find(:first)
puts ("Id is #{bank_object.id}");
puts ("Name is #{bank_object.name}");


bank_object = Bank.find(:last)
puts ("Id is #{bank_object.id}");
puts ("Name is #{bank_object.name}");

bank_object = Bank.find(2)
puts ("Id is #{bank_object.id}");
puts ("Name is #{bank_object.name}");

all_banks = Bank.find_by_sql("select * from banks");
for bank in all_banks
	puts ("#{bank.name}");
end


sbi_bank = Bank.find_by_name("SBI Bank");
puts ("Name is #{sbi_bank.name}");

icici_bank = Bank.find_by_head_office("Delhi");
puts ("Name is #{icici_bank.name}");

Bank.find(:all).each do |bank|
puts "#{bank.name}"
end

The constants ‘:first’ and ‘:last’ carry special meaning in the context of Active Record and when used willl fetch the first and the last records from the database when used in tandem with the find() method. It is also possible to retrieve the data using the primary key value. We have used find() by passing in a value of 2, in this case a comparison will happen between the primary key column with 2. Other variations for finding the objects are through queries and dynamic methods. For example, in the above code, we have used the query for fetching all the bank objects from the database. Another using strategy for retrieving data from the database is to use find_by_<property_name> notation. This means, it is possible to use the methods find_by_name(), find_by_head_office(), find_by_operation_date() directly on the bank obejcts by passing in the appropriate property value.

Edit Records

Having seen the usage of Create and Find, we will see how to use Active Record for editing persistent objects, the following code snippet will illustrate the usage.

require "logger"

require "rubygems"
require "active_record"
require "pp"

ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Base.establish_connection(:adapter => "mysql" ,
:database => "ruby", :username => "root", :password => "XXX")

class Bank < ActiveRecord::Base
	
end

bank = Bank.find(1);
bank.name = "HDFC";
bank.save;

Bank.update_all("head_office = 'New Location'");

Bank.delete_all();

We have used a flavour of find() method to retrieve the object from the database, and then have updated its properties using the regular approach. A call to save() will now update the corresponding entity in the database, instead of creating a new entity. Likewise, there are other useful methods such as update_all() and delete_all(). A call to update_all() which accept an expression, in the example case we have used ‘head_office = New Location’, this will make all the head_office column values to have the value ‘New Location’. Similarly delete_all() will remove all the records from the database.

Establishing relationship

Complex relationship between objects as well as its corresponding mapping at the database level can be easily achieved using Active Record. The following example illustrates the usage of relationship between the objects blogs and posts. A blog can have multiple posts and each post must know to which blog it belongs to.

require "logger"

require "rubygems"
require "activerecord"

ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Base.establish_connection(:adapter => "mysql" ,
:database => "ruby", :username => "root", :password => "XXX")

ActiveRecord::Schema.define do

  create_table :blogs, :force => true do |b|
    b.string   :name     
    b.string   :title
    b.string   :description
  end

  create_table :posts, :force => true do |p|
    p.integer :blog_id
    p.text    :description
  end
end


class Blog < ActiveRecord::Base
  has_many :posts
end


class Post < ActiveRecord::Base
  belongs_to :blog
end


Post.delete_all();
Blog.delete_all();

java_blog = Blog.create(
	:name => "Java Blog", :title => "Java beat Blog", :description => "Contains Java Articles and Tips"
)
post1 = Post.new();
post1.id = 'P1'
post1.description = 'Java Articles are great';
post1.blog = java_blog;
post1.save;


post2 = Post.new();
post2.id = 'P2'
post2.description = 'Java Tips are very useful';
post2.blog = java_blog;
post2.save;

cpp_blog = Blog.create(
	:name => "CPP Blog", :title => "CPP Blog", :description => "Contains CPP Articles and Tips"
)
post3 = Post.new();
post3.id = 'P3'
post3.description = 'CPP Articles are great';
post3.blog = cpp_blog;
post3.save;


post4 = Post.new();
post4.id = 'P4'
post4.description = 'CPP Tips are very useful';
post4.blog = cpp_blog;
post4.save;

The above example also illustrates the usage of Active Record’ Schema for creating tables and relationships directly from the application. The keywords ‘has_many’ defines a one to many relationship between Blog and Post objects and ‘belong_to’ ensures that the master reference Blog is preserved in the Post object. After running the application, the table structure will look something similar to the following,

Blog Id Name Title Description 1 Java Blog Java beat Blog 2 CPP Blog CPP Blog Post Id Blog Id Description 1 1 Java Articles are great 2 1 Java Tips are very useful 3 2 CPP Articles are great 4 2 CPP Tips are very useful

Creating database aware application

In this example, we will create a sample application for task management. Basically the application will provide options for creating, deleting, editing and viewing task items. We will be taking the support of various utilities that comes as part of Rails distribution for creating this sample.

Creating the project

Create a project called ‘task’ by executing the following command. The following command ensures that the basic set of artifacts necessary for a rails project is created.

rails new task

Also delete the default index.html file which is available in ‘/public/’ directory. Change the current directory to ‘task’ before proceeding with the rest of the section.

Configuring Database

The database that we will be using in this sample application will be mysql. Make sure that mysql database is installed in the local machine. Database related configurations for a rails application goes in database.yml file which is present in ‘/config/’directory. Rails framework provides database configuration options for development, testing and production environments. Edit the database.yml and make sure that the contents of the file matches the following,

development:
  adapter: mysql
  database: javabeat_dev
  username: root
  password: ####
  pool: 5
  timeout: 5000

test:
  adapter: mysql
  database: javabeat_test
  username: root
  password: ####
  pool: 5
  timeout: 5000

production:
  adapter: mysql
  database: javabeat_prod
  username: root
  password: ####
  pool: 5
  timeout: 5000

In the above file, we have configured different databases for different environments. Make sure to modify the username and the password for the database. Also there is one more place to instruct the Rails framework that we want mysql gem for interacting with the mysql database and this happens to be the file ‘Gemfile’ that is present in the project’s root directory.

gem 'mysql', '2.8.1'

Make sure to add the above line in the ‘Gemfile’ and remove any dependencies related to ‘sqlite’ which by default will be present.
The next step is to create the database that we have configured in the database.yml. One way is to connect to the MySql server through a MySql client and then issue ‘create database <DATABASE_NAME>’ for creating the database for the development and the testing environments. Other possible option is to execute to the following command which does the job of creating the database.

rake db:create

Scaffolding

In Rails terms, scaffolding refers to the process of the generation of artifacts such as controllers, models and view. In our case, we want to create controller, model and multiple views for the task item. Execute the following command,

rails generate scaffold TaskItem tile:string summary:text start_date:date end_date:date status:string priority:string total_hours_of_work:integer

We want scaffolding for ‘TaskItem’ where the model ‘Task’ is supposed to contain various properties like title, summary, start_date, end_date, status, priority and total_hours_of_work. Also note that syntax for specifying these list of properties in the command line which follows ‘<propertyName>:<propertyType>’. Property types with values ‘string’ will be displayed as a text field, those with values ‘text’ will be displayed as a text-box, for ‘date’ property types a date selector UI will be provided for selecting month, day and year. Executing the above command will generate a lot of files and folders for the project. In the subsequent sections, we will discuss the details about them.

Model

The model file ‘task_item.rb’ will be generated and placed in the directory ‘/app/models’. The name of the model class will be TaskItem (the one that is specified in the command prompt). The listing for this class is shown below.

class TaskItem < ActiveRecord::Base end

As you can see the TaskItem extends from the base class ‘ActiveRecord:Base’. This inheritance will make sure that the ‘TaskItem’ is a data-aware model class and the capabilities for a data-aware object such as Create/Update/Delete/Read will be applicable to objects created from TaskItem.

Controller

The Controller class provides the actions for creating, editing displaying, deleting and viewing. The following provides code snippet for TaskItemsController.

class TaskItemsController < ApplicationController

…

end

The controller class must extend the base ‘ApplicationController’ in a data-aware environment. We will see the list of supported actions provided by the controller in the following section.

New Task

Given below is the code snippet for creating a new task action. One can see that a new task is created through ‘TaskItem.new’ and the result is stored in task_item variable. When it comes to the rendering part, in case of html rendering, when the url is accessed through ‘http://localhost:3000/task_items/new’, the view is taken from the file ‘new.html.erb’ which is present in ‘/app/views/task_items’ directory. For xml rendering, when the url is accessed as ‘http://localhost:3000/task_items/new.xml’, the xml format of the new task-item is shown, although one would rarely use this feature.

def new
	@task_item = TaskItem.new

	respond_to do |format|
	  format.html # new.html.erb
	  format.xml  { render :xml => @task_item }
	end
end

One can see that this above action creates a empty task and redirects the user to a different page where the user can provide information for creating a task. The next section discusses about creating the new task.

Create Task

This action will be triggered once the user presses the ‘Create’ button after giving all the information .

def create
	@task_item = TaskItem.new(params[:task_item])

    respond_to do |format|
      if @task_item.save
        format.html { redirect_to(@task_item, :notice => 'Task item was successfully created.') }
        format.xml  { render :xml => @task_item, :status => :created, :location => @task_item }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @task_item.errors, :status => :unprocessable_entity }
      end
    end
 end

Note that the information collected from the user will be stored in the ‘params’ variable and the list of values present in the variable will be used for creating a new task. Upon successful creation of the task, appropriate message is displayed to the user.

Edit Task

In the case of editing a task, the task item to be edited is fetched from the database by calling the method TaskItem.find() that takes the id of the task as a parameter. Note that the request parameters will always be available in the in-built ‘params’ variable.

def update
    @task_item = TaskItem.find(params[:id])

    respond_to do |format|
      if @task_item.update_attributes(params[:task_item])
        format.html { redirect_to(@task_item, :notice => 'Task item was successfully updated.') }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @task_item.errors, :status => :unprocessable_entity }
      end
    end
end

We take the id of the task item to be edited and then call the method update_attributes() for updating the task object.

Show Task

The show action resembles the same functionality as that of update functionality. The task item to be shown in the UI is fetched with the help of task item. The fetched task item is then rendered in the browser either in html or in XML depending on the request URL.

def show
    @task_item = TaskItem.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @task_item }
    end
end

Delete Task

For deleting a task also, we follow similar logic as ‘edit’, where the id of the task is taken into consideration for fetching the task to be deleted. The method destroy() can be used for actually deleting the task object.

def destroy
    @task_item = TaskItem.find(params[:id])
    @task_item.destroy

    respond_to do |format|
      format.html { redirect_to(task_items_url) }
      format.xml  { head :ok }
    end
end

View all Tasks

The code snippet for viewing all the tasks is given below.

def index
    @task_items = TaskItem.all

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @task_items }
    end
end

The call ‘TaskItem.all’ will retrieve all the task items from the database and the results are stored in the variable task_items. In this action also, html rendering (‘http://localhost:3000/task_items’)as well as xml rendering (‘http://localhost:3000/taskitems.xml’)is supported.

Views

In this section we will look into the various views that the above action definitions are having references.

Index View

The complete listing for the Index view is given below. This view will be displayed by default when the user accesses the URL ‘http://localhost:3000/task_items’.

<h1>Listing all the task items</h1>

<table border = '1'>
  <tr>
    <th>Tile</th>
    <th>Summary</th>
    <th>Start date</th>
    <th>End date</th>
    <th>Status</th>
    <th>Priority</th>
    <th>Total hours of work</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @task_items.each do |task_item| %>
  <tr>
    <td><%= task_item.tile %></td>
    <td><%= task_item.summary %></td>
    <td><%= task_item.start_date %></td>
    <td><%= task_item.end_date %></td>
    <td><%= task_item.status %></td>
    <td><%= task_item.priority %></td>
    <td><%= task_item.total_hours_of_work %></td>
    <td><%= link_to 'Show', task_item %></td>
    <td><%= link_to 'Edit', edit_task_item_path(task_item) %></td>
    <td><%= link_to 'Destroy', task_item, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New Task item', new_task_item_path %>

Note that the above file uses a mixture of html and ruby scriptlets. The very first time the view is accessed it will be empty. Upon creating of sample tasks, the view may look something similar to the following

Listing all the tasks

New Task View

The screen-shot for creating a new task view is shown below.

For creating a new task
The code listing for ‘new.html.erb’ is given as follows.

<h1>Create new task page</h1>

<%= render 'form' %>

<%= link_to 'Back', task_items_path %>

Here we are using the concept of Partials in Ruby. Paritals are templates for a view that can be reused in multiple places. Here we have defined a partial in the name ‘form’ and hence the convention is that the view file ‘_form.html.erb’ will be placed in the directory ‘/app/views/task_items’ directory. The source code for ‘_form.html.erb’ will be presented later in the section. Given such a template file, it is possible to use the same template for new task as well as for edit task action because the UI controls for ‘new task’ and ‘edit task’ are one and the same.

Edit Task View

The edit task view is not different from ‘new task view’ and it also uses the partial ‘form’, the complete source code of which is given below.

<h1>Edit the selected task</h1>

<%= render 'form' %>

<%= link_to 'Show', @task_item %> |
<%= link_to 'Back', task_items_path %>

The sample screen-shot of ‘edit task’ is give below.

For editing existing task

Show Task View

When the user clicks the Show link from the ‘View all tasks’ page, information about the task will be displayed. Please refer the following screenshot for one such task information.

For viewing a task information

Given below is the source code listing for ‘show task view’. Note that there is view ensures that all the properties of a task are taken into consideration for display.

<h2> View information about the selected task </h2>

  <b>Tile:</b>
  <%= @task_item.tile %>



  <b>Summary:</b>
  <%= @task_item.summary %>



  <b>Start date:</b>
  <%= @task_item.start_date %>



  <b>End date:</b>
  <%= @task_item.end_date %>



  <b>Status:</b>
  <%= @task_item.status %>



  <b>Priority:</b>
  <%= @task_item.priority %>



  <b>Total hours of work:</b>
  <%= @task_item.total_hours_of_work %>

<%= link_to 'Edit', edit_task_item_path(@task_item) %> |
<%= link_to 'Back', task_items_path %>

Conclusion

This article explored the power of Ruby with respect to database integration, especially with Active Record features. The first section of the article discussed on writing basic CRUD operations along with the mapping between Ruby classes and database tables. Establishing relationships are explained with simple example. The later section of the article guided in writing a web application that makes use of Active Record features for creating/editing/listing tasks.

Comments

comments

About Krishna Srinivasan

He is Founder and Chief Editor of JavaBeat. He has more than 8+ years of experience on developing Web applications. He writes about Spring, DOJO, JSF, Hibernate and many other emerging technologies in this blog.

Speak Your Mind

*

Close
Please support the site
By clicking any of these buttons you help our site to get better