Section 5.4. Active Record


5.4. Active Record

5.4.1. Automated Mapping

Automatically maps:

  • Tables classes

  • Rows objects (instances of model classes)

  • Columns object attributes

Table to class mapping uses English plurals:

  • An Invoice model class maps to an invoices table.

  • A Person model class maps to a people table.

  • A Country model class maps to a countries table.

  • A SecurityLevel model class maps to a security_levels table.

Learn more: http://api.rubyonrails.com/classes/ActiveRecord/Base.html.

5.4.2. Associations

Four ways of associating models (Figures B-1 and B-2):

Figure B-1. One-to-one and one-to-many relationships

Figure B-2. Many-to-many relationships

 has_one has_many belongs_to has_and_belongs_to_many def Order < ActiveRecord::Base   has_many :line_items   belongs_to :customer   # there's a column "customer_id" in the db table end def LineItem < ActiveRecord::Base   belongs_to :order # there's a column "order_id" in the db table end def Customer < ActiveRecord::Base   has_many :orders   has_one :address end def Address < ActiveRecord::Base   belongs_to :customer end belongs_to  :some_model,         :class_name  => 'MyClass',      # specifies other class name         :foreign_key => 'my_real_id',   # and primary key         :conditions  => 'column = 0'    # only finds when this condition met has_one :some_model,         # as belongs_to and additionally:         :dependent   => :destroy        # deletes associated object         :order       => 'name ASC'      # SQL fragment for sorting has_many :some_model         # as has_one and additionally:         :dependent => :destroy          # deletes all dependent data                                         # calling each objects destroy         :dependent => :delete_all       # deletes all dependent data                                         # without calling the destroy methods         :dependent => :nullify          # set association to null, not                                         # destroying objects         :group => 'name'                # adds GROUP BY fragment         :finder_sql => 'select ....'    # instead of the Rails finders         :counter_sql => 'select ...'    # instead of the Rails counters def Category < ActiveRecord::Base   has_and_belongs_to_many :products end def Product < ActiveRecord::Base   has_and_belongs_to_many :categories end 

Table categories_products :

  • Has category_id column

  • Has product_id column

  • Does not have id column

5.4.3. Association Join Models (Figure B-3)

Figure B-3. Through model

 class Author < ActiveRecord::Base   has_many :authorships   has_many :books, :through => :authorships end class Authorship < ActiveRecord::Base   belongs_to :author   belongs_to :book end class Book < ActiveRecord::Base   has_one :authorship end @author = Author.find :first @author.authorships.collect { a a.book } # selects all books that the author's                                            # authorships belong to. @author.books                              # selects all books by using the Authorship                                            # join model 

Also works through has_many associations:

 class Firm < ActiveRecord::Base   has_many   :clients   has_many   :invoices, :through => :clients   has_many   :paid_invoices, :through => :clients, :source => :invoice end class Client < ActiveRecord::Base   belongs_to :firm   has_many   :invoices end class Invoice < ActiveRecord::Base   belongs_to :client end @firm = Firm.find :first @firm.clients.collect { c c.invoices }.flatten # select all invoices for all clients                                                  # of the firm @firm.invoices                                   # selects all invoices by going                                                  # through the Client join model. 

Learn more at the following address: http://api.rubyonrails.com/classes/ActiveRecord/Associations/ClassMethods.html.

5.4.4. Validations

 validates_presence_of :firstname, :lastname     # must be filled out validates_length_of :password,                     :minimum => 8           # more than 8 characters                     :maximum => 16          # shorter than 16 characters                     :in => 8..16            # between 8 and 16 characters                     :too_short => 'way too short'                     :too_long => 'way to long' validates_acceptance_of :eula               # Must accept a condition                         :accept => 'Y'      # default: 1 (ideal for a checkbox) validates_confirmation_of :password # the fields password and password_confirmation must match validates_uniqueness_of :user_name              # user_name has to be unique                         :scope => 'account_id'  # Condition:                                                 # account_id = user.account_id validates_format_of :email          # field must match a regular expression                     :with => /^(+)@((?:[-a-z0-9]+/.)+[a-z]{2,})$/i validates_numericality_of   :value              # value is numeric                             :only_integer => true                             :allow_nil => true validates_inclusion_in  :gender,    # value is in enumeration                         :in => %w( m, f ) validates_exclusion_of  :age            # value is not in Enumeration                         :in => 13..19   # don't want any teenagers validates_associated :relation # validates that the associated object is valid 

Validation options:

 :message => 'my own errormessage' :on      => :create                 # or :update (validates only then) :if      => ...                     # call method oder Proc 

Learn more: http://api.rubyonrails.com/classes/ActiveRecord/Validations.html.

5.4.5. Calculations

 Person.average :age Person.minimum :age Person.maximum :age Person.count Person.count(:conditions => "age > 26") Person.sum :salary, :group => :last_name 

Learn more at the following address: http://api.rubyonrails.com/classes/ActiveRecord/Calculations/ClassMethods.html.

5.4.6. Finders

 find(42)                                     # object with ID 42 find([37, 42])                               # Array with the objects with id 37, 42 find :all find :first,      :conditions => [ "name = ?", "Hans" ]   # finds the first record                                              # with matching condition 

More parameters for find :

 :order => 'name DESC'               # sql fragment for sorting :offset => 20                       # starts with entry 20 :limit => 10                        # only return 10 objects :group => 'name'                    # sql fragment GROUP BY :joins => 'LEFT JOIN ...'           # additional LEFT JOIN (rarely used) :include => [:account, :friends]    # LEFT OUTER JOIN with these model :include => { :groups => { :members=> { :favorites } } } :select => [:name, :adress]         # instead of SELECT * FROM :readonly => true                   # objects are write protected 

5.4.6.1. Dynamic attribute-based finders
 Person.find_by_user_name(user_name) Person.find_all_by_last_name(last_name) Person.find_by_user_name_and_password(user_name, password) Order.find_by_name("Joe Blow") Order.find_by_email("jb@gmail.com") Slideshow.find_or_create_by_name("Winter") 

Learn more: http://api.rubyonrails.com/classes/ActiveRecord/Base.html.

5.4.6.2. Scope
 Employee.with_scope(     :find => { :conditions => "salary > 10000",                :limit => 10 }) do   Employee.find(:all)     # => SELECT * FROM employees                           #             WHERE (salary > 10000)                           #             LIMIT 10   # scope is cumulative   Employee.with_scope(     :find => { :conditions => "name = 'Jamis'" }) do     Employee.find(:all)   # => SELECT * FROM employees                           #             WHERE ( salary > 10000 )                           #             AND ( name = 'Jamis' ))                           #             LIMIT 10   end   # all previous scope is ignored   Employee.with_exclusive_scope(     :find => { :conditions => "name = 'Jamis'" }) do     Employee.find(:all)   # => SELECT * FROM employees                           #             WHERE (name = 'Jamis')   end end 

Learn more:

  • http://www.codyfauser.com/articles/2006/02/01/using-with_scope-to-refactor-messy-finders

  • http://blog.caboo.se/articles/2006/02/22/nested-with_scope

5.4.7. Acts

acts_as_l ist:

 class TodoList < ActiveRecord::Base     has_many :todo_items, :order => "position"   end   class TodoItem < ActiveRecord::Base     belongs_to :todo_list     acts_as_list :scope => :todo_list   end   todo_list.first.move_to_bottom   todo_list.last.move_higher 

Learn more:

  • http://api.rubyonrails.com/classes/ActiveRecord/Acts/List/ClassMethods.html

  • http://api.rubyonrails.com/classes/ActiveRecord/Acts/List/InstanceMethods.html

acts_as_tree:

 class Category < ActiveRecord::Base     acts_as_tree :order => "name"   end   Example :   root    /_ child1         /_ subchild1         /_ subchild2   root      = Category.create("name" => "root")   child1    = root.children.create("name" => "child1")   subchild1 = child1.children.create("name" => "subchild1")   root.parent   # => nil   child1.parent # => root   root.children # => [child1]   root.children.first.children.first # => subchild1 

Learn more: http://api.rubyonrails.com/classes/ActiveRecord/Acts/Tree/ClassMethods.html.

5.4.8. Callbacks

Callbacks are hooks into the life cycle of an Active Record object that allows you to trigger logic before or after an alteration of the object state (Table B-1).

Table B-1. Active Record object life cycle

Object state

Callback

save

 

valid?

 
 

before_validation

 

before_validation_on_create

validate

 

validate_on_create

 
 

after_validation

 

after_validation_on_create

 

before_save

 

before_create

create

 
 

after_create

 

after_save


Example:

 class Subscription < ActiveRecord::Base   before_create :record_signup private   def record_signup     self.signed_up_on = Date.today   end end class Firm < ActiveRecord::Base   # Destroys the associated clients and people when the firm is destroyed   before_destroy { record Person.destroy_all "firm_id = #{record.id}"   }   before_destroy { record Client.destroy_all "client_of = #{record.id}" } end 

Learn more: http://api.rubyonrails.com/classes/ActiveRecord/Callbacks.html.

5.4.9. Observers

The Observer classes let you extract the functionality of the callbacks:

 class CommentObserver < ActiveRecord::Observer   def after_save(comment)     Notifications.deliver_comment("admin@do.com", "New comment was posted", comment)   end end 

  • Store observers in app/model/model_observer.rb .

  • Enable observer by putting this in config/environment.rb:

 config.active_record.observers = :comment_observer, :signup_observer 

Learn more: http://api.rubyonrails.com/classes/ActiveRecord/Observer.html.

5.4.10. Migration

 >  ruby script/generate migration   MyAddTables    

Creates the file db/migrations/001_my_add_tables.rb . The methods up( ) and down( ) change the db schema:

 def self.up     # brings db schema to the next version   create_table :table, :force => true do t     t.column :name, :string     t.column :age, :integer, { :default => 42 }     t.column :description, :text     # :string, :text, :integer, :float, :datetime, :timestamp, :time, :date,     # :binary, :boolean   end   add_column :table, :column, :type   rename_column :table, :old_name, :new_name   change_column :table, :column, :new_type   execute "SQL Statement"   add_index :table, :column, :unique => true, :name => 'some_name'   add_index :table, [ :column1, :column2 ] end def self.down   # rollbacks changes   rename_column :table, :new_name, :old_name   remove_column :table, :column   drop_table :table   remove_index :table, :column end 

To execute the migration:

 >  rake db:migrate  >  rake db:migrate VERSION=1  4 >  rake db:migrate RAILS_ENV=production  

Learn more:

  • http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

  • http://glu.ttono.us/articles/2005/10/27/the-joy-of-migrations

  • http://jamis.jamisbuck.org/articles/2005/09/27/ getting-started -with-activerecord-migrations



Ruby on Rails[c] Up and Running
Ruby on Rails[c] Up and Running
ISBN: B003D3OGCY
EAN: N/A
Year: 2006
Pages: 94

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net