Académique Documents
Professionnel Documents
Culture Documents
ActiveRecord
It’s an implementation of a design pattern ‘Active Record’ (observe the space in between), which
is an Object Relational Mapping(ORM) mechanism.
ActiveRecord is one such implementation of this design pattern which helps us do all our
database tasks (Data Definition and Data Manipulation through its meta programming
capabilities) without we having to write any SQL statement. It represents database tables through
classes and instances of those classes.
A class which wraps around a database table is called a “model”, instances of these classes are
“model instances”. Models encapsulate business logic
Some more examples of ORM implementation are ‘Hibernate’ for Java and ‘Castle’ in .Net
Though ActiveRecord is a ruby library built for Rails, it can be used with other frameworks as
well. But we will stick to rails for now. Just for the information ActiveRecord source code is the
biggest part of Rails total source.
ActiveRecord library supports almost all popular databases such as Oracle, sqlServer, db2,
postgresql, sqlite, Sybase etc.
As one can see, the whole process of SQL execution would be slower as compared to running a
‘stored procedure’ in the database (which is a pre-compiled code). One can always ask, can we
run a ‘stored procedure’ from AR directly?, yes, we can. But as stored procedures are relational
database specific, this could become a limitation.
We can(and often need to) run SQL directly from AR to speed up the process But then one has to
learn writing good SQL queries and implement ANSI SQL so as to make our application
RDBMS agnostic.
Before we dive-in learning the nuts and bolts of AR, please make a note of the following points:
1. ActiveRecord is a Model layer, an interface which helps us manages our database. All the
CRUD (Create, Read, Update, Delete) operations can be to be done through AR. The
Relational database can be used merely as a record storage purpose.
2. The code we write here would consist of most of the business logic. Obviously it is very
much likely to be reusable across applications.
3. While working on any application, one should analyze, how far our application needs to
be database agnostic, when to utilize the power of flexibility and when do you need the
speed of execution and when to do the balancing act.
1. AR-database connectivity.
2. Conventions over configuration
3. Creating a Model and various CRUD features available in AR.
4. Validations
5. Callbacks
6. Model Associations. One-to-one, One-to-many (w/o eager loading), many-to-many
(using has-and-belongs-to-many and :through options)
Note :
1. We will be creating/altering our relational database schema through migrations (see Chapter
5). But for the sake of keeping focus only on AR ‘model’, we create tables in mySQL directly.
As we have seen earlier in chapter 3, the file ‘database.yml’ has all configuration settings
associated with the database, which ActiveRecord uses to connect to.
<environment>:
adapter: mysql Development, Test or Production
encoding: utf8
database: sample1_development
username: root
password :root
host: localhost
ActiveRecord can connect itself to different databases. The appropriate adapters are required to
connect to them.
One would wonder why rails prefers to use .yml files over XML. The reason is simplicity
readability (human readable) and usability of the .yml structure over XML.
Pl observe the .yml file as a ‘key – value’ pair. Ruby syntax can very easily convert this file to a
hash table and carry out the connectivity. http://yaml4r.sourceforge.net/cookbook/ is a great
source for those interested in knowing all about YML files.
Rails uses YAML files extensively to specify fixtures which we will see in the coming sessions..
For the magic of ActiveRecord to work, you must name our database tables and columns
according to specific conventions.
Model class name should be in CamelCase and is mapped to the Table which is a pluralized
form of the model class name.
E.g. If the model name is ‘User’, rails would assume the table name is ‘users’ (table name is
lower case). If our model class is ‘Person’, mapping table name would ‘people’, a pluralizing of
the word ‘Person’. (Tables are ‘plural’ and the associated model is ‘singular’)
How does rails knows this? With the help of ‘Inflector’ class, we get the answer.
>> Inflector.pluralize('test')
=> "tests"
>> Inflector.pluralize('mouse')
=> "mice"
>> Inflector.pluralize('person')
=> "people"
>> "company".pluralize
=> companies
B. Primary Key name should always be ‘id’, which should be an auto-incremented integer, case
sensative.
C. Foreign keys join database tables. When you need a foreign key field in a table, the key takes
the form "{singular foreign table name}_id".
E.g. person_id with an English singular and an _id suffix.
E. For association tables (also known as intersection tables) that resolve many-to-many
relationships, we use the following naming convention: {name of first entity}_{name of second
entity} e.g. products_categories ( explained with example later in the chapter)
Note: This convention adds to standardization which in turn adds to the speedy development. But
still, one would wonder, what if one has to bypass/override these conventions, due to some
practical problems(say, incase of some tables already exists).Yes, there is a way. I have listed
some overrides at the end of this chapter.
Create a Model:
1. Pl create one table in mySql with the following structure (do not worry about the size of
individual fields right now, take the defaults)
users
id integer
first_name string
last_name string
user_name string
login string
password string
phoneNo integer
You will observe, no. of folders and files getting created. Do not bother too much about ‘test’,
‘app’ and ‘db’ folders/files right now. Let’s just focus on the app/models folder, and ‘user.rb’ file.
user.rb
end
ActiveRecord::Base is the class which all models inherit. This inheritance gives
our User class, a tremendous amount of functionality. As we learn further we will realize that
these model classes will contain all the business logic of our applications.
In this case, ‘User’ class automatically maps to ‘users’ tables as per rails conventions.
Objects ‘user1’ and ‘user2’ are mapped on two records of ‘users’ table. While we are executing
these simple instructions, underneath, AR is doing the magic of generating/executing the SQL
statements. These operations are logged in a log file.
Please open /log/development.log file in a separate window and find the SQL generated.
Rails has logging built right into the framework, it exposes a Logger object to all the code in a
Rails application. Whenever we execute operations in AR, it generates a log of activities.
Logs help us monitor/manage our application behavior/performance.
We can generate log messages at the warning, info, error, and fatal levels.
log/development.log:
If you observe these SQL statement generated, it has some cryptic sequence of characters known
as color escape sequences that are used by default. Simply set colorize_logging to false to
make it more readable output:
ActiveRecord::Base.colorize_logging = false
Or better still, open config/environment.rb file and at the end of the file(last line), you type
the above statement and save the file. We need to ‘exit’ from the console window since we
have changed the settings and enter again to see the effect.
Note: After every ruby console command execution, continue to refresh this log file and observe
the SQL generated by ActiveRecord.
Now that we have done the log file setting, let’s continue with the model discussions.
Note: find(:all) would return an array like object which we can iterate using ‘each’ method.
I hope you get some rough idea of, when a request from the browser is made, how the model
retrieves the desired records from the database.
Look at the method ‘find_by_first_namel’. ActiveRecord adds a dynamic finder for each of the
attributes available in the table.
While using something like User.find_by_user_name is very readable and easy, it is an overhead
to rails,
ActiveRecord dynamically generates these methods within method missing and this can be quite
slow.
Using MyModel.find_by_sql directly, or MyModel.find, is more efficient.
>> User.find :all, :conditions => “first_name LIKE ‘S%’ ” , :limit => 10
Tip:Optimize on execution time. We should retrieve only the information that we need. A lot of
execution time can be wasted by running selects for data that is not used.
Explore ‘:select’ option (extract only those fields we are going to use) , :limit and :offset options
(if the record size is large, this option is a must)
Custom Methods
user.rb
def full_name
name = first_name + ‘ ‘ + last_name
end
def self.find_all_ordered
find :all, :order => ‘last_name, first_name’
end
end
Just observe the custom methods carefully. No where we are writing any ruby statements to
display the records. That’s the job of ‘Views’ which we will learn later. I can use the same class
for multiple web applications as this class can become generic.
‘Reusability’ is the core of Object Orientated Programming.
Validations
Adding validations to our code means, the record won't be created or saved unless these
conditions are met or satisfied. Model-based validations help make data within our database
stays consistent.
User.rb
Since we have modified the User.rb, to reload the class, exit from the console and re-enter the
console window.
>> user.errors
For more samples and various validation options, a further reading is advised
http://rails.rubyonrails.com/classes/ActiveRecord/Validations/ClassMethods.html
Callbacks
In any of the business application, ‘validation’ and ‘authentication’ of a record could be different
activities/processes. Validations could be just related to the filed levels, while authentication
would be applied to the entire form. E.g. In any credit card transaction, we can add ‘validations’
to our model to check if name, card no, expiry date etc are entered properly, but the actual
‘authenticity’ of the card(getting a confirmation for the validity of the card ) needs to be carried
out , before the actual transaction takes place. This ‘pre process’ can be done using callbacks.
The ‘post process’ could be to send emails to the customer with the status of the transaction.
Even this we call as Callbacks.
During the application life cycle, we come across the following events:
Calculations
Do you want to get maximums, minimums, averages, or sums for data in your tables?
ActiveRecord’s Calculations make these all possible. Most can be customized pretty deeply with
extra options, so go read about them.
http://api.rubyonrails.org/classes/ActiveRecord/Calculations/ClassMethods.html
Model Associations
We are aware that web applications contain bunch of tables and they are ‘associated’ (related)
with each other. With ‘normalization’ as the key, we avoid redundancy. These associations are of
different types e.g.one-to-one, one-to-many, many-to-many, polymorphic etc. ActiveRecord as of
today, can not read these associations automatically, we as programmers, need to define them in
models, so we call it Model Associations.
One-to-one Association
For the sake of example, pl assume, the user (or author), in our ‘users’ table can write at
most one ‘article’. To set this association, we need to
1. Have 2 tables, ‘users’ and ‘articles’ (we already have ‘users’ table)
2. We need 2 models ‘User’ and ‘Article’ to map on the above 2 tables (we already have
‘User’ model/class).
3. Define the association between these models.
4. See how the above association works in AR by adding/updating records in these tables.
users
id integer
first_name string
last_name string
user_name string
login string articles
password string id integer
user_id integer
title string
created_on datetime
Pl create the ‘articles’ table in mySql as described above. The ‘user_id’ column is our foreign
key in ‘articles’ table (as per rails naming convention).
2) Now that the tables are set, we need our model ‘Article’ and in that we set the association.
railsApps\sample1> ruby script/generate model Article
3) Edit both the model classes and add the following association methods
user.rb article.rb
has_one :article and belongs to :user are the methods (not method definitions), that take symbol
as a parameter. The belongs_to method adds an association, called user, to article,
Please note that, :articles is singular, since the association is of the form ‘has_one’, in case
association is one-to-many, it would become
(has_many :articles). >> a1 = Article.new
>> a1.title = “Instant Ruby”
>> user = User.find(1)
railsApps\sample1> ruby script/console >> user.articles << a1
Loading development environment >> user.save
>>user
** Observe the SQL being generated in each case (use the log file)
One-to-many Association
Consider a case where ‘One User can write many Articles’. This indicates, we have a
one-to-many association.
users
id integer
first_name string
last_name string
user_name string
login string articles
password string id integer
user_id integer
title string
created_on datetime
2) Its time to update our models. Ideally, I would destroy the previously created model and
create a new one with the same name as :
belongs to :user and has_many :articles are the methods (not method definitions) that take
symbol as a parameter. The belongs_to method adds an association, called user, to article,
Please note that, :articles is plural, since the association is of the form ‘has_many’, in case
association is one-to-one, it would become (has_one :article).
Check out the following in your ruby console: DO CHECK the sql statements generated after
execution of these instructions too.
From the above example, its quite evident that the parent model instance (‘user’) gets the
child model methods (‘articles’)
** Observe the SQL being generated in each case (use the log file).
One more question arises, and that is when we delete a ‘user’ from the users table, ‘what happens
to the ‘article records’, as there is an association, do they get deleted automatically? NO.
The solution is to explore the Help files and you get the way out, we use the dependency
option , :dependent.
user.rb
When you refresh the console, you would see the user ‘John’ and his all authored books are
deleted from ‘articles’ table.
Eager loading
The ActiveRecord documentation says: “Eager loading is a way to find objects of a certain class
and a number of named associations along with it in a single SQL call. This is one of the easiest
ways of to prevent the dreaded 1+N problem in which fetching 100 posts that each need to
display their author triggers 101 database queries. Through the use of eager loading, the 101
queries can be reduced to 1.”
To cut this story short, suppose we want to fetch all articles of a specific user, with the current
knowledge we would carry out operations this way.
You will notice that 2 queries are fired to fetch the result set. A better way to go about it using,
eager loading, using :include option
>>user1 = User.find(:first, :include => :articles)
>>user1.articles
In one query, we get the result set, did you observe the ‘LEFT OUTER JOIN’?
One thing, we need to remember though, ActiveRecord ignores the ‘:select’ directive if we use
‘:include’. This could be a little overhead. But there is always an alternative. Use ‘:joins’ option
>>user2 = User.find(:first,
:select => “users.first_name, articles.title”,
:joins => "left outer join articles on users.id = articles.user_id")
>>user2.first_name
>>user2.title
SELECT users.first_name, articles.title FROM `users` left outer join articles on users.id =
articles.user_id LIMIT 1
Continuing our model discussion, in practice, we know a ‘user’ (or author) can write
‘many articles’ and one ‘article’ can have ‘many users(or authors)’.
So, please set your tables and the model classes as shown below.
Note: Continue to observe and study the SQL getting generated in log files.
users
id integer
first_name string
last_name string
user_name string
login string
password string
articles_users
user_id integer
article_id integer
articles
id integer article.rb
class Article < ActiveRecord::Base
title string has_and_belongs_to_many :users
created_on datetime end
user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :articles
end
I am going to dive a little further and explain the same association but with a different database
design and a model method option (:through).
we again need to modify our design a little bit, (Its an agile world, change is the only constant,
you see)
users
id integer
first_name string
last_name string
user_name string
login string
password string
user_articles
user_id integer
article_id integer
articles
id integer
title string
created_on datetime
user.rb user_article.rb
*not* user_article
I am assuming, you have added few records in users and articles tables.
Assume we have to find out all the users( authors) for a specific article.
Ideally we should get result in two steps viz
>> a = Article.find(1) # the first article in the table.
>> a.users # and this should fetch all the users for that article.
Did you see, we needed one extra steps to carry out the job? Did you observe the query
generated, well it needs to be optimized too.
Through associations allow for one model to be accessed through an intermediary association
article.rb
The query will have an Inner join, the way it should be. We can even
add another user (author) to the same article
>> a = Article.find(1)
>> a.users
>> a.users << User.find(3) # this will insert one user for that article by adding one record in
#the user_articles table
Assignment:
ActiveRecord::Base.pluralize_table_names = false
Discuss: When you don’t want to follow any of the conventions, where are you saving
time, or adding simplicity/maintainability to your code?
As ActiveRecord is the biggest part of Rails 2.0 , I have tried to cover maximum details, I
thought are good to start with. Let us turn our attention now to Data Definition part of
ActiveRecord with Migrations which is our next chapter.
Assignment :
Discuss :
1. Explore the concept of ‘SQL injection’ and how rails can protect itself from it, prove with an
example.
2. Log files track every activity happening in AR., Is it worth it? What are the impacts when
application goes live.
3. We know that the field level ‘validations’ are done at the client side (Using java script), then
what’s the advantage of having ‘validations’ in models?
4. Explore ‘Finder’ methods further ( :conditions, :order, :group, :limit, :offset, :joins, :include,
:select, :readonly etc)
http://www.railsbrain.com/api/rails-2.0.2/doc/index.html?a=M001686&name=find
Workout:
5. Explore conditional searching, check whether, extracting records based on search conditions,
result in ‘case sensitive’ results?
1. validates_length_of :password,
:minimum => 8 # more than 8 characters
:maximum => 16 # no more than 16 characters
:in => 8..16 # between 8 and 16 characters
:too_short => ' password is too short'
:too_long => ‘ password is too long'
2. validates_confirmation_of :password
3. validates_acceptance_of :first_name
B) If all such validations are included, in what order are they carried out?
6. Write 2 custom methods to find out ‘next’ and ‘previous’ records in the model
(record ‘id’ will be passed as an argument).
TIP: When available use the built-in classes and methods, rather than developing your own.