|  |  | @@ -0,0 +1,298 @@ | 
    
    |  |  | # Rails Models | 
    
    |  |  | 
 | 
    
    |  |  | ### Generating models | 
    
    |  |  | 
 | 
    
    |  |  | $ rails g model User | 
    
    |  |  | 
 | 
    
    |  |  | ### Associations | 
    
    |  |  | 
 | 
    
    |  |  | belongs_to | 
    
    |  |  | has_one | 
    
    |  |  | has_many | 
    
    |  |  | has_many :through | 
    
    |  |  | has_one :through | 
    
    |  |  | has_and_belongs_to_many | 
    
    |  |  | 
 | 
    
    |  |  | belongs_to :author, | 
    
    |  |  | class_name: 'User', | 
    
    |  |  | dependent: :destroy  // delete this | 
    
    |  |  | 
 | 
    
    |  |  | ### Has many | 
    
    |  |  | 
 | 
    
    |  |  | has_many :comments, :order => "posted_on" | 
    
    |  |  | has_many :comments, :include => :author | 
    
    |  |  | has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name" | 
    
    |  |  | has_many :tracks, :order => "position", :dependent => :destroy | 
    
    |  |  | has_many :comments, :dependent => :nullify | 
    
    |  |  | has_many :tags, :as => :taggable | 
    
    |  |  | has_many :reports, :readonly => true | 
    
    |  |  | has_many :subscribers, :through => :subscriptions, :source => :user | 
    
    |  |  | has_many :subscribers, :class_name => "Person", :finder_sql => | 
    
    |  |  | 'SELECT DISTINCT people.* ' + | 
    
    |  |  | 'FROM people p, post_subscriptions ps ' + | 
    
    |  |  | 'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' + | 
    
    |  |  | 'ORDER BY p.first_name' | 
    
    |  |  | 
 | 
    
    |  |  | ### Many-to-many | 
    
    |  |  | 
 | 
    
    |  |  | If you have a join model: | 
    
    |  |  | 
 | 
    
    |  |  | class Programmer < ActiveRecord::Base | 
    
    |  |  | has_many :assignments | 
    
    |  |  | has_many :projects, :through => :assignments | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | class Project < ActiveRecord::Base | 
    
    |  |  | has_many :assignments | 
    
    |  |  | has_many :programmers, :through => :assignments | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | class Assignment | 
    
    |  |  | belongs_to :project | 
    
    |  |  | belongs_to :programmer | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | Or HABTM: | 
    
    |  |  | 
 | 
    
    |  |  | has_and_belongs_to_many :projects | 
    
    |  |  | has_and_belongs_to_many :projects, :include => [ :milestones, :manager ] | 
    
    |  |  | has_and_belongs_to_many :nations, :class_name => "Country" | 
    
    |  |  | has_and_belongs_to_many :categories, :join_table => "prods_cats" | 
    
    |  |  | has_and_belongs_to_many :categories, :readonly => true | 
    
    |  |  | has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql => | 
    
    |  |  | "DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}" | 
    
    |  |  | 
 | 
    
    |  |  | ### Polymorphic associations | 
    
    |  |  | 
 | 
    
    |  |  | class Post | 
    
    |  |  | has_many :attachments, :as => :parent | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | class Image | 
    
    |  |  | belongs_to :parent, :polymorphic => true | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | And in migrations: | 
    
    |  |  | 
 | 
    
    |  |  | create_table :images do | 
    
    |  |  | t.references :post, :polymorphic => true | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | Migrations | 
    
    |  |  | ---------- | 
    
    |  |  | 
 | 
    
    |  |  | ### Run migrations | 
    
    |  |  | 
 | 
    
    |  |  | $ rake db:migrate | 
    
    |  |  | 
 | 
    
    |  |  | ### Migrations | 
    
    |  |  | 
 | 
    
    |  |  | create_table :users do |t| | 
    
    |  |  | t.string :name | 
    
    |  |  | t.text   :description | 
    
    |  |  | 
 | 
    
    |  |  | t.primary_key :id | 
    
    |  |  | t.string | 
    
    |  |  | t.text | 
    
    |  |  | t.integer | 
    
    |  |  | t.float | 
    
    |  |  | t.decimal | 
    
    |  |  | t.datetime | 
    
    |  |  | t.timestamp | 
    
    |  |  | t.time | 
    
    |  |  | t.date | 
    
    |  |  | t.binary | 
    
    |  |  | t.boolean | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | options: | 
    
    |  |  | :null (boolean) | 
    
    |  |  | :limit (integer) | 
    
    |  |  | :default | 
    
    |  |  | :precision (integer) | 
    
    |  |  | :scale (integer) | 
    
    |  |  | 
 | 
    
    |  |  | ### Tasks | 
    
    |  |  | 
 | 
    
    |  |  | create_table | 
    
    |  |  | change_table | 
    
    |  |  | drop_table | 
    
    |  |  | add_column | 
    
    |  |  | change_column | 
    
    |  |  | rename_column | 
    
    |  |  | remove_column | 
    
    |  |  | add_index | 
    
    |  |  | remove_index | 
    
    |  |  | 
 | 
    
    |  |  | ### Associations | 
    
    |  |  | 
 | 
    
    |  |  | t.references :category   # kinda same as t.integer :category_id | 
    
    |  |  | 
 | 
    
    |  |  | # Can have different types | 
    
    |  |  | t.references :category, polymorphic: true | 
    
    |  |  | 
 | 
    
    |  |  | ### Add/remove columns | 
    
    |  |  | 
 | 
    
    |  |  | $ rails generate migration RemovePartNumberFromProducts part_number:string | 
    
    |  |  | 
 | 
    
    |  |  | class RemovePartNumberFromProducts < ActiveRecord::Migration | 
    
    |  |  | def up | 
    
    |  |  | remove_column :products, :part_number | 
    
    |  |  | end | 
    
    |  |  |  | 
    
    |  |  | def down | 
    
    |  |  | add_column :products, :part_number, :string | 
    
    |  |  | end | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | Validation | 
    
    |  |  | ---------- | 
    
    |  |  | 
 | 
    
    |  |  | ### Validate checkboxes | 
    
    |  |  | 
 | 
    
    |  |  | class Person < ActiveRecord::Base | 
    
    |  |  | validates :terms_of_service, :acceptance => true | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | ### Validate associated records | 
    
    |  |  | 
 | 
    
    |  |  | class Library < ActiveRecord::Base | 
    
    |  |  | has_many :books | 
    
    |  |  | validates_associated :books | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | ### Confirmations (like passwords) | 
    
    |  |  | 
 | 
    
    |  |  | class Person < ActiveRecord::Base | 
    
    |  |  | validates :email, :confirmation => true | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | ### Validate format | 
    
    |  |  | 
 | 
    
    |  |  | class Product < ActiveRecord::Base | 
    
    |  |  | validates :legacy_code, :format => { :with => /\A[a-zA-Z]+\z/, | 
    
    |  |  | :message => "Only letters allowed" } | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | ### Validate length | 
    
    |  |  | 
 | 
    
    |  |  | class Person < ActiveRecord::Base | 
    
    |  |  | validates :name, :length => { :minimum => 2 } | 
    
    |  |  | validates :bio, :length => { :maximum => 500 } | 
    
    |  |  | validates :password, :length => { :in => 6..20 } | 
    
    |  |  | validates :registration_number, :length => { :is => 6 } | 
    
    |  |  | 
 | 
    
    |  |  | validates :content, :length => { | 
    
    |  |  | :minimum   => 300, | 
    
    |  |  | :maximum   => 400, | 
    
    |  |  | :tokenizer => lambda { |str| str.scan(/\w+/) }, | 
    
    |  |  | :too_short => "must have at least %{count} words", | 
    
    |  |  | :too_long  => "must have at most %{count} words" | 
    
    |  |  | } | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | ### Numeric | 
    
    |  |  | 
 | 
    
    |  |  | class Player < ActiveRecord::Base | 
    
    |  |  | validates :points, :numericality => true | 
    
    |  |  | validates :games_played, :numericality => { :only_integer => true } | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | ### Non-empty | 
    
    |  |  | 
 | 
    
    |  |  | class Person < ActiveRecord::Base | 
    
    |  |  | validates :name, :login, :email, :presence => true | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | ### custom | 
    
    |  |  | 
 | 
    
    |  |  | class Person < ActiveRecord::Base | 
    
    |  |  | validate :foo_cant_be_nil | 
    
    |  |  | 
 | 
    
    |  |  | def foo_cant_be_nil | 
    
    |  |  | errors.add(:foo, 'cant be nil')  if foo.nil? | 
    
    |  |  | end | 
    
    |  |  | end | 
    
    |  |  |  | 
    
    |  |  | API | 
    
    |  |  | --- | 
    
    |  |  | 
 | 
    
    |  |  | items = Model.find_by_email(email) | 
    
    |  |  | items = Model.where(first_name: "Harvey") | 
    
    |  |  | 
 | 
    
    |  |  | item = Model.find(id) | 
    
    |  |  | 
 | 
    
    |  |  | item.serialize_hash | 
    
    |  |  | item.new_record? | 
    
    |  |  | 
 | 
    
    |  |  | item.create     # Same an #new then #save | 
    
    |  |  | item.create!    # Same as above, but raises an Exception | 
    
    |  |  | 
 | 
    
    |  |  | item.save | 
    
    |  |  | item.save!      # Same as above, but raises an Exception | 
    
    |  |  | 
 | 
    
    |  |  | item.update | 
    
    |  |  | item.update_attributes | 
    
    |  |  | item.update_attributes! | 
    
    |  |  | 
 | 
    
    |  |  | item.valid? | 
    
    |  |  | item.invalid? | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | http://guides.rubyonrails.org/active_record_validations_callbacks.html | 
    
    |  |  | 
 | 
    
    |  |  | ### Mass updates | 
    
    |  |  | 
 | 
    
    |  |  | # Updates person id 15 | 
    
    |  |  | Person.update 15, name: "John", age: 24 | 
    
    |  |  | Person.update [1,2], [{name: "John"}, {name: "foo"}] | 
    
    |  |  | 
 | 
    
    |  |  | ### Joining | 
    
    |  |  | 
 | 
    
    |  |  | Student.joins(:schools).where(:schools => { :type => 'public' }) | 
    
    |  |  | Student.joins(:schools).where('schools.type' => 'public' ) | 
    
    |  |  | 
 | 
    
    |  |  | ### Serialize | 
    
    |  |  | 
 | 
    
    |  |  | class User < ActiveRecord::Base | 
    
    |  |  | serialize :preferences | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | user = User.create(:preferences => { "background" => "black", "display" => large }) | 
    
    |  |  | 
 | 
    
    |  |  | You can also specify a class option as the second parameter that’ll raise an | 
    
    |  |  | exception if a serialized object is retrieved as a descendant of a class not in | 
    
    |  |  | the hierarchy. | 
    
    |  |  | 
 | 
    
    |  |  | class User < ActiveRecord::Base | 
    
    |  |  | serialize :preferences, Hash | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | user = User.create(:preferences => %w( one two three )) | 
    
    |  |  | User.find(user.id).preferences    # raises SerializationTypeMismatch | 
    
    |  |  | 
 | 
    
    |  |  | Overriding accessors | 
    
    |  |  | -------------------- | 
    
    |  |  | 
 | 
    
    |  |  | class Song < ActiveRecord::Base | 
    
    |  |  | # Uses an integer of seconds to hold the length of the song | 
    
    |  |  | 
 | 
    
    |  |  | def length=(minutes) | 
    
    |  |  | write_attribute(:length, minutes.to_i * 60) | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | def length | 
    
    |  |  | read_attribute(:length) / 60 | 
    
    |  |  | end | 
    
    |  |  | end | 
    
    |  |  | 
 | 
    
    |  |  | * http://api.rubyonrails.org/classes/ActiveRecord/Base.html | 
    
    |  |  | 
 | 
    
    |  |  | Callbacks | 
    
    |  |  | --------- | 
    
    |  |  | 
 | 
    
    |  |  | after_create | 
    
    |  |  | after_initialize | 
    
    |  |  | after_validation | 
    
    |  |  | after_save | 
    
    |  |  | after_commit |