|
|
@@ -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 |