My Active Record Tests
start gem server and browse to the full documentation http://localhost:8808/doc_root/activerecord-2.0.2/rdoc/index.html Sample table used CREATE TABLE test.users ( id int(10) unsigned NOT NULL auto_increment, name varchar(45) NOT NULL, age int(11) default NULL, PRIMARY KEY (id) ) ENGINE=MyISAM; Table has one record id=1 name=”Sreeprakash”, age=51 Model used user.rb class User < ActiveRecord::Base end Controller used users_controller.rb class UsersController < ApplicationController # we will fill test functions to test the ActiveRecord methods end No Views used Browser URLhttp://localhost:3000/users/test
- Read one record/field with ID = 1 def test render :text => “Hello ” + User.find(1).name end Results: Hello Sreeprakash
- Add one record using hash u = User.new( :name => “David”, :age => 42) u.save
- Using block user = User.new do |u| u.name = “David” u.age= 42 end user.save
- Creating a bare record user = User.new user.name = “David” user.age= 42 user.save
- Update an exiting record # 1 User.update(1, :name => “SreeprakashABC”, :age => 51)
- Find a URL passed id URLhttp://localhost:3000/users/test/2 u = User.find(params[:id]) render :text => “Hello ” + u.name (routes.rb must have the line map.connect ‘:controller/:action/:id’ )
- Associations between objects controlled by simple meta-programming macros. class Firm < ActiveRecord::Base has_many :clients has_one :account belongs_to :conglomorate end
- Aggregations of value objects controlled by simple meta-programming macros. class Account < ActiveRecord::Base composed_of :balance, :class_name => “Money”, :mapping => %w(balance amount) composed_of :address, :mapping => [%w(address_street street), %w(address_city city)] end
- Validation rules that can differ for new or existing objects. class Account < ActiveRecord::Base validates_presence_of :subdomain, :name, :email_address, :password validates_uniqueness_of :subdomain validates_acceptance_of :terms_of_service,
n => :create validates_confirmation_of :password, :email_address,
n => :create end - Callbacks as methods or queues on the entire lifecycle (instantiation, saving, destroying, validating, etc).
-
123456789class Person < ActiveRecord::Basedef before_destroy # is called just before Person#destroyCreditCard.find(credit_card_id).destroyendendclass Account < ActiveRecord::Baseafter_find :eager_load, 'self.class.announce(#{id})'end
- Observers for the entire lifecycle
12345class CommentObserver < ActiveRecord::Observerdef after_create(comment) # is called just after Comment#saveNotifications.deliver_new_comment("david@loudthinking.com", comment)endend
- Inheritance hierarchies
1234class Company < ActiveRecord::Base; endclass Firm < Company; endclass Client < Company; endclass PriorityClient < Client; end
- Transaction support on both a database and object level. The latter is implemented by using Transaction::Simple
1234567891011# Just database transactionAccount.transaction dodavid.withdrawal(100)mary.deposit(100)end# Database and object transactionAccount.transaction(david, mary) dodavid.withdrawal(100)mary.deposit(100)end
- Reflections on columns, associations, and aggregations
123reflection = Firm.reflect_on_association(:clients)reflection.klass # => Client (class)Firm.columns # Returns an array of column descriptors for the firms table
- Direct manipulation (instead of service invocation) So instead of (Hibernate example):
12345long pkId = 1234;DomesticCat pk = (DomesticCat) sess.load( Cat.class, new Long(pkId) );// something interesting involving a cat...sess.save(cat);sess.flush(); // force the SQL INSERT
Active Record lets you:
1234pkId = 1234cat = Cat.find(pkId)# something even more interesting involving the same cat...cat.save - Database abstraction through simple adapters (~100 lines) with a shared connector
123456789ActiveRecord::Base.establish_connection(:adapter => "sqlite", :database => "dbfile")ActiveRecord::Base.establish_connection(:adapter => "mysql",:host => "localhost",:username => "me",:password => "secret",:database => "activerecord")
- Logging support for Log4r and Logger
12ActiveRecord::Base.logger = Logger.new(STDOUT)ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
-
1Below are some of the excerpts from the documentation...
Simple example (1/2): Defining tables and classes (using MySQL)
Data definitions are specified only in the database. Active Record queries the database for the column names (that then serves to determine which attributes are valid) on regular object instantiation through the new constructor and relies on the column names in the rows with the finders.
|
1 2 3 4 5 6 7 |
# CREATE TABLE companies ( # id int(11) unsigned NOT NULL auto_increment, # client_of int(11), # name varchar(255), # type varchar(100), # PRIMARY KEY (id) # ) |
Active Record automatically links the “Company” object to the “companies” table
|
1 2 3 4 5 6 7 8 9 10 11 |
class Company < ActiveRecord::Base has_many :people, :class_name => "Person" end class Firm < Company has_many :clients def people_with_all_clients clients.inject([]) { |people, client| people + client.people } end end |
The foreign_key is only necessary because we didn’t use “firm_id” in the data definition
|
1 2 3 4 5 6 7 8 9 10 |
class Client < Company belongs_to :firm, :foreign_key => "client_of" end # CREATE TABLE people ( # id int(11) unsigned NOT NULL auto_increment, # name text, # company_id text, # PRIMARY KEY (id) # ) |
Active Record will also automatically link the “Person” object to the “people” table
|
1 2 3 |
class Person < ActiveRecord::Base belongs_to :company end |
Simple example (2/2): Using the domain
Picking a database connection for all the Active Records
|
1 2 3 4 5 6 7 |
ActiveRecord::Base.establish_connection( :adapter => "mysql", :host => "localhost", :username => "me", :password => "secret", :database => "activerecord" ) |
Create some fixtures
|
1 2 3 4 5 6 7 |
<span style="background-color: #ffff00;"> firm = Firm.new("name" => "Next Angle")</span> # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm") <span style="background-color: #ffff00;"> firm.save</span> client = Client.new("name" => "37signals", "client_of" => firm.id) # SQL: INSERT INTO companies (name, client_of, type) VALUES("37signals", 1, "Firm") client.save |
Lots of different finders
|
1 2 3 4 5 6 7 8 9 10 11 |
<span style="background-color: #ffff00;"> # SQL: SELECT * FROM companies WHERE id = 1 next_angle = Company.find(1) # SQL: SELECT * FROM companies WHERE id = 1 AND type = 'Firm' next_angle = Firm.find(1) # SQL: SELECT * FROM companies WHERE id = 1 AND name = 'Next Angle' next_angle = Company.find(:first, :conditions => "name = 'Next Angle'") next_angle = Firm.find_by_sql("SELECT * FROM companies WHERE id = 1").first </span> |
The supertype, Company, will return subtype instances
|
1 |
Firm === next_angle |
All the dynamic methods added by the has_many macro
|
1 2 3 |
next_angle.clients.empty? # true next_angle.clients.size # total number of clients all_clients = next_angle.clients |
Constrained finds makes access security easier when ID comes from a web-app
|
1 2 |
# SQL: SELECT * FROM companies WHERE client_of = 1 AND type = 'Client' AND id = 2 thirty_seven_signals = next_angle.clients.find(2) |
Bi-directional associations thanks to the “belongs_to” macro
|
1 |
thirty_seven_signals.firm.nil? # true |
43 Things Tags: Ruby on Rails, Active Record – Object-relation mapping put on rails 43 Things Tags: Ruby on Rails, Active Record – Object-relation mapping put on rails



