Creating Database aware applications in Ruby on Rails

December 6, 2010

Ruby

«»

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.

Download Example Source Code

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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,

1
<strong>Blog</strong> Id Name Title Description 1 Java Blog Java beat Blog 2 CPP Blog CPP Blog <strong>Post</strong> 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
email

«»

Comments

comments