Skip to content

Instantly share code, notes, and snippets.

@johan--
Forked from rstacruz/index.md
Created April 23, 2014 12:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johan--/11213727 to your computer and use it in GitHub Desktop.
Save johan--/11213727 to your computer and use it in GitHub Desktop.

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

Callbacks

after_create
after_initialize
after_validation
after_save
after_commit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment