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).
-
class Person < ActiveRecord::Base def before_destroy # is called just before Person#destroy CreditCard.find(credit_card_id).destroy end end class Account < ActiveRecord::Base after_find :eager_load, 'self.class.announce(#{id})' end - Observers for the entire lifecycle
class CommentObserver < ActiveRecord::Observer def after_create(comment) # is called just after Comment#save Notifications.deliver_new_comment("david@loudthinking.com", comment) end end - Inheritance hierarchies
class Company < ActiveRecord::Base; end class Firm < Company; end class Client < Company; end class PriorityClient < Client; end
- Transaction support on both a database and object level. The latter is implemented by using Transaction::Simple
# Just database transaction Account.transaction do david.withdrawal(100) mary.deposit(100) end # Database and object transaction Account.transaction(david, mary) do david.withdrawal(100) mary.deposit(100) end - Reflections on columns, associations, and aggregations
reflection = 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):
long 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:
pkId = 1234 cat = Cat.find(pkId) # something even more interesting involving the same cat... cat.save
- Database abstraction through simple adapters (~100 lines) with a shared connector
ActiveRecord::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
ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Base.logger = Log4r::Logger.new("Application Log") -
Below 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.
# 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
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
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
class Person < ActiveRecord::Base
belongs_to :company
end
Simple example (2/2): Using the domain
Picking a database connection for all the Active Records
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "localhost",
:username => "me",
:password => "secret",
:database => "activerecord"
)
Create some fixtures
firm = Firm.new("name" => "Next Angle") # SQL: INSERT INTO companies (name, type) VALUES("Next Angle", "Firm") firm.save 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
# 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
The supertype, Company, will return subtype instances
Firm === next_angle
All the dynamic methods added by the has_many macro
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
# 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
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




