5.4. Active Record5.4.1. Automated MappingAutomatically maps:
Table to class mapping uses English plurals:
Learn more: http://api.rubyonrails.com/classes/ActiveRecord/Base.html. 5.4.2. AssociationsFour ways of associating models (Figures B-1 and B-2): Figure B-1. One-to-one and one-to-many relationshipsFigure B-2. Many-to-many relationshipshas_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 :
5.4.3. Association Join Models (Figure B-3)Figure B-3. Through modelclass 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. Validationsvalidates_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. CalculationsPerson.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. Findersfind(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 findersPerson.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. ScopeEmployee.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:
5.4.7. Actsacts_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:
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. CallbacksCallbacks 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
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. ObserversThe 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
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:
|