Related posts: fix up gutters
This commit is contained in:
parent
1c778d88aa
commit
4a4cb658f6
|
@ -10,7 +10,7 @@
|
||||||
| where_exp: "page", "page.deprecated != true"
|
| where_exp: "page", "page.deprecated != true"
|
||||||
| sort: "weight", "last"
|
| sort: "weight", "last"
|
||||||
%}
|
%}
|
||||||
<footer class='related-posts' id='related'>
|
<footer class='related-posts-area' id='related'>
|
||||||
<div class='container'>
|
<div class='container'>
|
||||||
<div class='related-posts-section'>
|
<div class='related-posts-section'>
|
||||||
<div class='callout'>
|
<div class='callout'>
|
||||||
|
|
|
@ -1,21 +1,3 @@
|
||||||
/*
|
|
||||||
* Related posts
|
|
||||||
*/
|
|
||||||
|
|
||||||
.related-posts {
|
|
||||||
& {
|
|
||||||
@include section-with-container;
|
|
||||||
padding-top: 16px;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
background: $gray-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 481px) {
|
|
||||||
padding-top: 64px;
|
|
||||||
padding-bottom: 64px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.related-post-list {
|
.related-post-list {
|
||||||
&,
|
&,
|
||||||
& > li {
|
& > li {
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Related posts
|
||||||
|
*/
|
||||||
|
|
||||||
|
.related-posts-area {
|
||||||
|
& {
|
||||||
|
@include section-with-container;
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
background: $gray-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 481px) {
|
||||||
|
padding-top: 64px;
|
||||||
|
padding-bottom: 64px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,13 +6,15 @@
|
||||||
.related-posts-section {
|
.related-posts-section {
|
||||||
& {
|
& {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-left: -16px;
|
@include section-gutter(margin-left, $multiplier: -1);
|
||||||
margin-right: -16px;
|
@include section-gutter(margin-right, $multiplier: -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .callout,
|
& > .callout,
|
||||||
& > .group {
|
& > .group {
|
||||||
margin: 0 16px;
|
margin: 0;
|
||||||
|
@include section-gutter(margin-left);
|
||||||
|
@include section-gutter(margin-right);
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .callout {
|
& > .callout {
|
||||||
|
@ -39,8 +41,8 @@
|
||||||
|
|
||||||
& > .callout,
|
& > .callout,
|
||||||
& > .group {
|
& > .group {
|
||||||
margin-top: 16px;
|
@include section-gutter(margin-top);
|
||||||
margin-bottom: 16px;
|
@include section-gutter(margin-bottom);
|
||||||
flex: 1 1 100%;
|
flex: 1 1 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,8 +56,8 @@
|
||||||
|
|
||||||
& > .callout,
|
& > .callout,
|
||||||
& > .group {
|
& > .group {
|
||||||
margin-top: 16px;
|
@include section-gutter(margin-top);
|
||||||
margin-bottom: 16px;
|
@include section-gutter(margin-bottom);
|
||||||
flex: 1 1 100%;
|
flex: 1 1 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,11 @@
|
||||||
@import './components/pages-list';
|
@import './components/pages-list';
|
||||||
@import './components/pre-footer';
|
@import './components/pre-footer';
|
||||||
@import './components/push-button';
|
@import './components/push-button';
|
||||||
@import './components/related-posts';
|
@import './components/related-posts-area';
|
||||||
@import './components/related-posts-callout';
|
@import './components/related-posts-callout';
|
||||||
@import './components/related-posts-group';
|
@import './components/related-posts-group';
|
||||||
@import './components/related-posts-section';
|
@import './components/related-posts-section';
|
||||||
|
@import './components/related-post-list';
|
||||||
@import './components/related-post-item';
|
@import './components/related-post-item';
|
||||||
@import './components/search-box';
|
@import './components/search-box';
|
||||||
@import './components/search-footer';
|
@import './components/search-footer';
|
||||||
|
|
578
rails-models.md
578
rails-models.md
|
@ -1,112 +1,161 @@
|
||||||
---
|
---
|
||||||
title: Models
|
title: Rails models
|
||||||
category: Rails
|
category: Rails
|
||||||
|
layout: 2017/sheet
|
||||||
---
|
---
|
||||||
|
|
||||||
### [QueryMethods](http://devdocs.io/rails/activerecord/querymethods)
|
Generating
|
||||||
|
----------
|
||||||
|
|
||||||
```rb
|
### Generating
|
||||||
|
|
||||||
|
$ rails g model User
|
||||||
|
|
||||||
|
Using models
|
||||||
|
------------
|
||||||
|
|
||||||
|
### Query methods
|
||||||
|
|
||||||
|
```ruby
|
||||||
items = Model
|
items = Model
|
||||||
.where(first_name: 'Harvey')
|
.where(first_name: 'Harvey')
|
||||||
.where('id = 3')
|
.where('id = 3')
|
||||||
.where('id = ?', 3)
|
.where('id = ?', 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
.order(:title)
|
.order(:title)
|
||||||
.order(title: :desc)
|
.order(title: :desc)
|
||||||
.order("title DESC")
|
.order("title DESC")
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
.reorder(:title) # discards other .order's
|
.reorder(:title) # discards other .order's
|
||||||
.rewhere(...) # discards other .where's
|
.rewhere(...) # discards other .where's
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
.limit(2)
|
.limit(2)
|
||||||
.offset(1)
|
.offset(1)
|
||||||
.uniq
|
.uniq
|
||||||
```
|
```
|
||||||
|
|
||||||
Advanced:
|
See: [QueryMethods](http://devdocs.io/rails/activerecord/querymethods)
|
||||||
|
|
||||||
```rb
|
### Advanced query methods
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
items = Model
|
||||||
.select(:id)
|
.select(:id)
|
||||||
.select([:id, :name])
|
.select([:id, :name])
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
.group(:name) # GROUP BY name
|
.group(:name) # GROUP BY name
|
||||||
.group('name AS grouped_name, age')
|
.group('name AS grouped_name, age')
|
||||||
.having('SUM(price) > 30') # needs to be chained with .group
|
.having('SUM(price) > 30') # needs to be chained with .group
|
||||||
|
|
||||||
.includes(:user)
|
|
||||||
.includes(user: [:articles])
|
|
||||||
|
|
||||||
.references(:posts)
|
|
||||||
# .where("posts.name = 'foo'").references(:posts)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### [FinderMethods](http://devdocs.io/rails/activerecord/findermethods)
|
```ruby
|
||||||
|
.includes(:user)
|
||||||
|
.includes(user: [:articles])
|
||||||
|
```
|
||||||
|
|
||||||
```rb
|
```ruby
|
||||||
|
.references(:posts)
|
||||||
|
# aka: .where("posts.name = 'foo'").references(:posts)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Finder methods
|
||||||
|
|
||||||
|
```ruby
|
||||||
item = Model.find(id)
|
item = Model.find(id)
|
||||||
item = Model.find_by_email(email)
|
item = Model.find_by_email(email)
|
||||||
item = Model.where(email: email).first
|
item = Model.where(email: email).first
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
Model
|
Model
|
||||||
.exists?(5)
|
.exists?(5)
|
||||||
.exists?(name: "David")
|
.exists?(name: "David")
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
.first
|
.first
|
||||||
.last
|
.last
|
||||||
.find_nth(4, [offset])
|
.find_nth(4, [offset])
|
||||||
```
|
```
|
||||||
|
|
||||||
### [Persistence](http://devdocs.io/rails/activerecord/persistence)
|
See: [FinderMethods](http://devdocs.io/rails/activerecord/findermethods)
|
||||||
|
|
||||||
```rb
|
### Persistence
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.new_record?
|
item.new_record?
|
||||||
item.persisted?
|
item.persisted?
|
||||||
item.destroyed?
|
item.destroyed?
|
||||||
|
|
||||||
item.serialize_hash
|
item.serialize_hash
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.save
|
item.save
|
||||||
item.save! # Same as above, but raises an Exception
|
item.save! # Same as above, but raises an Exception
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.update name: 'John' # Saves immediately
|
item.update name: 'John' # Saves immediately
|
||||||
item.update! name: 'John'
|
item.update! name: 'John'
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.update_column :name, 'John' # skips validations and callbacks
|
item.update_column :name, 'John' # skips validations and callbacks
|
||||||
item.update_columns name: 'John'
|
item.update_columns name: 'John'
|
||||||
item.update_columns! name: 'John'
|
item.update_columns! name: 'John'
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.touch # updates :updated_at
|
item.touch # updates :updated_at
|
||||||
item.touch :published_at
|
item.touch :published_at
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.destroy
|
item.destroy
|
||||||
item.delete # skips callbacks
|
item.delete # skips callbacks
|
||||||
```
|
```
|
||||||
|
|
||||||
```rb
|
```ruby
|
||||||
Model.create # Same an #new then #save
|
Model.create # Same an #new then #save
|
||||||
Model.create! # Same as above, but raises an Exception
|
Model.create! # Same as above, but raises an Exception
|
||||||
```
|
```
|
||||||
|
|
||||||
### [AttributeAssignment](http://devdocs.io/rails/activerecord/attributeassignment)
|
See: [Persistence](http://devdocs.io/rails/activerecord/persistence)
|
||||||
|
|
||||||
```rb
|
### Attribute Assignment
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.attributes # #<Hash>
|
item.attributes # #<Hash>
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.attributes = { name: 'John' } # Merges attributes in. Doesn't save.
|
item.attributes = { name: 'John' } # Merges attributes in. Doesn't save.
|
||||||
item.assign_attributes name: 'John' # Same as above
|
item.assign_attributes name: 'John' # Same as above
|
||||||
```
|
```
|
||||||
|
|
||||||
### [Dirty](http://devdocs.io/rails/activemodel/dirty)
|
See: [AttributeAssignment](http://devdocs.io/rails/activerecord/attributeassignment)
|
||||||
|
|
||||||
```rb
|
### Dirty
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.changed?
|
item.changed?
|
||||||
item.changed # ['name']
|
item.changed # ['name']
|
||||||
item.changed_attributes # { 'name' => 'Bob' } - original values
|
item.changed_attributes # { 'name' => 'Bob' } - original values
|
||||||
item.changes # { 'name' => ['Bob', 'Robert'] }
|
item.changes # { 'name' => ['Bob', 'Robert'] }
|
||||||
item.previous_changes # available after #save
|
item.previous_changes # available after #save
|
||||||
item.restore_attributes
|
item.restore_attributes
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.name = 'Robert'
|
item.name = 'Robert'
|
||||||
item.name_was # 'Bob'
|
item.name_was # 'Bob'
|
||||||
item.name_change # [ 'Bob', 'Robert' ]
|
item.name_change # [ 'Bob', 'Robert' ]
|
||||||
|
@ -114,224 +163,318 @@ item.name_changed? # true
|
||||||
item.name_changed?(from: 'Bob', to: 'Robert')
|
item.name_changed?(from: 'Bob', to: 'Robert')
|
||||||
```
|
```
|
||||||
|
|
||||||
### [Validations](http://devdocs.io/rails/activerecord/validations)
|
See: [Dirty](http://devdocs.io/rails/activemodel/dirty)
|
||||||
|
|
||||||
```rb
|
### Validations
|
||||||
|
|
||||||
|
```ruby
|
||||||
item.valid?
|
item.valid?
|
||||||
item.invalid?
|
item.invalid?
|
||||||
```
|
```
|
||||||
|
|
||||||
### [Calculations](http://devdocs.io/rails/activerecord/calculations)
|
See: [Validations](http://devdocs.io/rails/activerecord/validations)
|
||||||
|
|
||||||
```rb
|
### Calculations
|
||||||
|
|
||||||
|
```ruby
|
||||||
Person.count
|
Person.count
|
||||||
Person.count(:age) # counts non-nil's
|
Person.count(:age) # counts non-nil's
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
Person.average(:age)
|
Person.average(:age)
|
||||||
Person.maximum(:age)
|
Person.maximum(:age)
|
||||||
Person.minimum(:age)
|
Person.minimum(:age)
|
||||||
Person.sum('2 * age')
|
Person.sum('2 * age')
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
Person.calculate(:count, :all)
|
Person.calculate(:count, :all)
|
||||||
```
|
```
|
||||||
|
|
||||||
Advanced:
|
Advanced:
|
||||||
|
|
||||||
```
|
```ruby
|
||||||
Person.distinct.count
|
Person.distinct.count
|
||||||
Person.group(:city).count
|
Person.group(:city).count
|
||||||
```
|
```
|
||||||
|
|
||||||
Generating
|
See: [Calculations](http://devdocs.io/rails/activerecord/calculations)
|
||||||
----------
|
|
||||||
|
|
||||||
$ rails g model User
|
### Dynamic attribute-based finders
|
||||||
|
|
||||||
|
Given a field called `name`:
|
||||||
|
{: .-setup}
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Returns one record
|
||||||
|
Person.find_by_name(name)
|
||||||
|
Person.find_last_by_name(name)
|
||||||
|
Person.find_or_create_by_name(name)
|
||||||
|
Person.find_or_initialize_by_name(name)
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Returns a list of records
|
||||||
|
Person.find_all_by_name(name)
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Add a bang to make it raise an exception
|
||||||
|
Person.find_by_name!(name)
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# You may use `scoped` instead of `find`
|
||||||
|
Person.scoped_by_user_name
|
||||||
|
```
|
||||||
|
|
||||||
Associations
|
Associations
|
||||||
------------
|
------------
|
||||||
|
|
||||||
belongs_to
|
### Associations
|
||||||
has_one
|
|
||||||
has_many
|
- `belongs_to`
|
||||||
has_many :through
|
- `has_one`
|
||||||
has_one :through
|
- `has_many`
|
||||||
has_and_belongs_to_many
|
- `has_many :through`
|
||||||
|
- `has_one :through`
|
||||||
|
- `has_and_belongs_to_many`
|
||||||
|
|
||||||
### Has many
|
### Has many
|
||||||
|
|
||||||
belongs_to :parent, :foreign_key => 'parent_id' class_name: 'Folder'
|
```ruby
|
||||||
has_many :folders, :foreign_key => 'parent_id', class_name: 'Folder'
|
belongs_to :parent, :foreign_key => 'parent_id' class_name: 'Folder'
|
||||||
|
has_many :folders, :foreign_key => 'parent_id', class_name: 'Folder'
|
||||||
|
|
||||||
has_many :comments, :order => "posted_on"
|
has_many :comments, :order => "posted_on"
|
||||||
has_many :comments, :include => :author
|
has_many :comments, :include => :author
|
||||||
has_many :people, :class_name => "Person"
|
has_many :people, :class_name => "Person"
|
||||||
has_many :people, :conditions => "deleted = 0"
|
has_many :people, :conditions => "deleted = 0"
|
||||||
has_many :tracks, :order => "position"
|
has_many :tracks, :order => "position"
|
||||||
has_many :comments, :dependent => :nullify
|
has_many :comments, :dependent => :nullify
|
||||||
has_many :comments, :dependent => :destroy
|
has_many :comments, :dependent => :destroy
|
||||||
has_many :tags, :as => :taggable
|
has_many :tags, :as => :taggable
|
||||||
has_many :reports, :readonly => true
|
has_many :reports, :readonly => true
|
||||||
has_many :subscribers, :through => :subscriptions, class_name: "User", :source => :user
|
has_many :subscribers, :through => :subscriptions, class_name: "User", :source => :user
|
||||||
has_many :subscribers, :finder_sql =>
|
has_many :subscribers, :finder_sql =>
|
||||||
'SELECT DISTINCT people.* ' +
|
'SELECT DISTINCT people.* ' +
|
||||||
'FROM people p, post_subscriptions ps ' +
|
'FROM people p, post_subscriptions ps ' +
|
||||||
'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
|
'WHERE ps.post_id = #{id} AND ps.person_id = p.id ' +
|
||||||
'ORDER BY p.first_name'
|
'ORDER BY p.first_name'
|
||||||
|
```
|
||||||
|
|
||||||
### belongs to
|
### belongs to
|
||||||
|
|
||||||
belongs_to :author,
|
```ruby
|
||||||
:dependent => :destroy # or :delete
|
belongs_to :author,
|
||||||
|
:dependent => :destroy # or :delete
|
||||||
|
|
||||||
:class_name => "Person"
|
:class_name => "Person"
|
||||||
:select => "*"
|
:select => "*"
|
||||||
:counter_cache => true
|
:counter_cache => true
|
||||||
:counter_cache => :custom_counter
|
:counter_cache => :custom_counter
|
||||||
:include => "Book"
|
:include => "Book"
|
||||||
:readonly => true
|
:readonly => true
|
||||||
|
|
||||||
:conditions => 'published = true'
|
:conditions => 'published = true'
|
||||||
|
|
||||||
:touch => true
|
:touch => true
|
||||||
:touch => :authors_last_updated_at
|
:touch => :authors_last_updated_at
|
||||||
|
|
||||||
:primary_key => "name"
|
:primary_key => "name"
|
||||||
:foreign_key => "author_name"
|
:foreign_key => "author_name"
|
||||||
|
```
|
||||||
|
|
||||||
### Many-to-many
|
### Many-to-many
|
||||||
|
|
||||||
If you have a join model:
|
If you have a join model:
|
||||||
|
{: .-setup}
|
||||||
|
|
||||||
class Programmer < ActiveRecord::Base
|
```ruby
|
||||||
has_many :assignments
|
class Programmer < ActiveRecord::Base
|
||||||
has_many :projects, :through => :assignments
|
has_many :assignments
|
||||||
end
|
has_many :projects, :through => :assignments
|
||||||
|
end
|
||||||
|
```
|
||||||
|
{: data-line="2,3"}
|
||||||
|
|
||||||
class Project < ActiveRecord::Base
|
```ruby
|
||||||
has_many :assignments
|
class Project < ActiveRecord::Base
|
||||||
has_many :programmers, :through => :assignments
|
has_many :assignments
|
||||||
end
|
has_many :programmers, :through => :assignments
|
||||||
|
end
|
||||||
|
```
|
||||||
|
{: data-line="2,3"}
|
||||||
|
|
||||||
class Assignment
|
```ruby
|
||||||
belongs_to :project
|
class Assignment
|
||||||
belongs_to :programmer
|
belongs_to :project
|
||||||
end
|
belongs_to :programmer
|
||||||
|
end
|
||||||
|
```
|
||||||
|
{: data-line="2,3"}
|
||||||
|
|
||||||
Or HABTM:
|
### Many-to-many (HABTM)
|
||||||
|
|
||||||
has_and_belongs_to_many :projects
|
```ruby
|
||||||
has_and_belongs_to_many :projects, :include => [ :milestones, :manager ]
|
has_and_belongs_to_many :projects
|
||||||
has_and_belongs_to_many :nations, :class_name => "Country"
|
has_and_belongs_to_many :projects, :include => [ :milestones, :manager ]
|
||||||
has_and_belongs_to_many :categories, :join_table => "prods_cats"
|
has_and_belongs_to_many :nations, :class_name => "Country"
|
||||||
has_and_belongs_to_many :categories, :readonly => true
|
has_and_belongs_to_many :categories, :join_table => "prods_cats"
|
||||||
has_and_belongs_to_many :active_projects, :join_table => 'developers_projects', :delete_sql =>
|
has_and_belongs_to_many :categories, :readonly => true
|
||||||
"DELETE FROM developers_projects WHERE active=1 AND developer_id = #{id} AND project_id = #{record.id}"
|
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
|
### Polymorphic associations
|
||||||
|
|
||||||
class Post
|
```ruby
|
||||||
has_many :attachments, :as => :parent
|
class Post
|
||||||
end
|
has_many :attachments, as: :parent
|
||||||
|
end
|
||||||
|
```
|
||||||
|
{: data-line="2"}
|
||||||
|
|
||||||
class Image
|
```ruby
|
||||||
belongs_to :parent, :polymorphic => true
|
class Image
|
||||||
end
|
belongs_to :parent, polymorphic: true
|
||||||
|
end
|
||||||
|
```
|
||||||
|
{: data-line="2"}
|
||||||
|
|
||||||
And in migrations:
|
And in migrations:
|
||||||
|
|
||||||
create_table :images do |t|
|
```ruby
|
||||||
t.references :post, :polymorphic => true
|
create_table :images do |t|
|
||||||
end
|
t.references :post, polymorphic: true
|
||||||
|
end
|
||||||
### Dynamic attribute-based finders
|
```
|
||||||
|
{: data-line="2"}
|
||||||
# Returns one record
|
|
||||||
Person.find_by_name(name)
|
|
||||||
Person.find_last_by_name(name)
|
|
||||||
Person.find_or_create_by_name(name)
|
|
||||||
Person.find_or_initialize_by_name(name)
|
|
||||||
|
|
||||||
# Returns a list of recordns
|
|
||||||
Person.find_all_by_name(name)
|
|
||||||
|
|
||||||
# Add a bang to make it raise an exception
|
|
||||||
Person.find_by_name!(name)
|
|
||||||
|
|
||||||
# You may use `scoped` instead of `find`
|
|
||||||
Person.scoped_by_user_name
|
|
||||||
|
|
||||||
Validation
|
Validation
|
||||||
----------
|
----------
|
||||||
|
|
||||||
class Person < ActiveRecord::Base
|
### Validation
|
||||||
|
|
||||||
validates :name, presence: true
|
```ruby
|
||||||
|
class Person < ActiveRecord::Base
|
||||||
|
```
|
||||||
|
{:.-setup}
|
||||||
|
|
||||||
validates :terms, acceptance: true
|
```ruby
|
||||||
|
# Presence
|
||||||
|
validates :name, presence: true
|
||||||
|
```
|
||||||
|
{: data-line="2"}
|
||||||
|
|
||||||
validates :email, confirmation: true
|
```ruby
|
||||||
|
# Acceptance
|
||||||
|
validates :terms, acceptance: true
|
||||||
|
```
|
||||||
|
|
||||||
validates :slug, uniqueness: true
|
```ruby
|
||||||
validates :slug, uniqueness: { case_sensitive: false }
|
# Confirm
|
||||||
validates :holiday, uniqueness: { scope: :year, :message => "only once a year" }
|
validates :email, confirmation: true
|
||||||
|
```
|
||||||
|
|
||||||
validates :code, format: /regex/
|
```ruby
|
||||||
validates :code, format: { with: /regex/ }
|
# Unique
|
||||||
|
validates :slug, uniqueness: true
|
||||||
|
validates :slug, uniqueness: { case_sensitive: false }
|
||||||
|
validates :holiday, uniqueness: { scope: :year, message: 'yearly only' }
|
||||||
|
```
|
||||||
|
|
||||||
validates :name, length: { minimum: 2 }
|
```ruby
|
||||||
validates :bio, length: { maximum: 500 }
|
# Format
|
||||||
validates :password, length: { in: => 6..20 }
|
validates :code, format: /regex/
|
||||||
validates :number, length: { is: => 6 }
|
validates :code, format: { with: /regex/ }
|
||||||
|
```
|
||||||
|
|
||||||
validates :gender, inclusion: %w(male female)
|
```ruby
|
||||||
validates :gender, inclusion: { in: %w(male female) }
|
# Length
|
||||||
validates :lol, exclusion: %w(xyz)
|
validates :name, length: { minimum: 2 }
|
||||||
|
validates :bio, length: { maximum: 500 }
|
||||||
|
validates :password, length: { in: => 6..20 }
|
||||||
|
validates :number, length: { is: => 6 }
|
||||||
|
```
|
||||||
|
|
||||||
validates :points, numericality: true
|
```ruby
|
||||||
validates :played, numericality: { only_integer: true }
|
# Include/exclude
|
||||||
# ... greater_than, greater_than_or_equal_to,
|
validates :gender, inclusion: %w(male female)
|
||||||
# ... less_than, less_than_or_equal_to
|
validates :gender, inclusion: { in: %w(male female) }
|
||||||
# ... odd, even, equal_to
|
validates :lol, exclusion: %w(xyz)
|
||||||
|
```
|
||||||
|
|
||||||
# Validate the associated records to ensure they're valid as well
|
```ruby
|
||||||
has_many :books
|
# Numeric
|
||||||
validates_associated :books
|
validates :points, numericality: true
|
||||||
|
validates :played, numericality: { only_integer: true }
|
||||||
|
# ... greater_than, greater_than_or_equal_to,
|
||||||
|
# ... less_than, less_than_or_equal_to
|
||||||
|
# ... odd, even, equal_to
|
||||||
|
```
|
||||||
|
|
||||||
# Length (full enchalada)
|
```ruby
|
||||||
validates :content, length: {
|
# Validate the associated records to ensure they're valid as well
|
||||||
minimum: 300,
|
has_many :books
|
||||||
maximum: 400,
|
validates_associated :books
|
||||||
tokenizer: lambda { |str| str.scan(/\w+/) },
|
```
|
||||||
too_short: "must have at least %{count} words",
|
|
||||||
too_long: "must have at most %{count} words" }
|
|
||||||
|
|
||||||
# Multiple
|
```ruby
|
||||||
validates :login, :email, presence: true
|
# Length (full options)
|
||||||
|
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" }
|
||||||
|
```
|
||||||
|
|
||||||
# Conditional
|
```ruby
|
||||||
validates :description, presence: true, if: :published?
|
# Multiple
|
||||||
validates :description, presence: true, if: lambda { |obj| .. }
|
validates :login, :email, presence: true
|
||||||
|
```
|
||||||
|
|
||||||
validates :title, presence: true, on: :save # :save | :create | :update
|
```ruby
|
||||||
end
|
# Conditional
|
||||||
|
validates :description, presence: true, if: :published?
|
||||||
|
validates :description, presence: true, if: lambda { |obj| .. }
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
validates :title, presence: true, on: :save # :save | :create | :update
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
end
|
||||||
|
```
|
||||||
|
{: .-setup}
|
||||||
|
|
||||||
### Custom validations
|
### Custom validations
|
||||||
|
|
||||||
class Person < ActiveRecord::Base
|
```ruby
|
||||||
validate :foo_cant_be_nil
|
class Person < ActiveRecord::Base
|
||||||
|
validate :foo_cant_be_nil
|
||||||
|
|
||||||
def foo_cant_be_nil
|
def foo_cant_be_nil
|
||||||
errors.add(:foo, 'cant be nil') if foo.nil?
|
errors.add(:foo, 'cant be nil') if foo.nil?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
{: data-line="2"}
|
||||||
|
|
||||||
### Errors
|
### Errors
|
||||||
|
|
||||||
record.errors.valid? #=> false
|
```ruby
|
||||||
record.errors #=> { :name => ["can't be blank"] }
|
record.errors.valid? # → false
|
||||||
record.errors.messages #=> { :name => ["can't be blank"] }
|
record.errors # → { :name => ["can't be blank"] }
|
||||||
|
record.errors.messages # → { :name => ["can't be blank"] }
|
||||||
|
```
|
||||||
|
|
||||||
record.errors[:name].any?
|
```ruby
|
||||||
|
record.errors[:name].any?
|
||||||
|
```
|
||||||
|
|
||||||
Other API
|
Other API
|
||||||
---------
|
---------
|
||||||
|
@ -342,70 +485,109 @@ Other API
|
||||||
|
|
||||||
### Mass updates
|
### Mass updates
|
||||||
|
|
||||||
# Updates person id 15
|
```ruby
|
||||||
Person.update 15, name: "John", age: 24
|
# Updates person id 15
|
||||||
Person.update [1,2], [{name: "John"}, {name: "foo"}]
|
Person.update 15, name: "John", age: 24
|
||||||
|
Person.update [1,2], [{name: "John"}, {name: "foo"}]
|
||||||
|
```
|
||||||
|
|
||||||
### Joining
|
### Joining
|
||||||
|
|
||||||
Student.joins(:schools).where(schools: { type: 'public' })
|
```ruby
|
||||||
Student.joins(:schools).where('schools.type' => 'public' )
|
# Basic joins
|
||||||
|
Student.joins(:schools).where(schools: { type: 'public' })
|
||||||
|
Student.joins(:schools).where('schools.type' => 'public' )
|
||||||
|
```
|
||||||
|
|
||||||
# multiple associations
|
```ruby
|
||||||
Article.joins(:category, :comments)
|
# Multiple associations
|
||||||
|
Article.joins(:category, :comments)
|
||||||
|
```
|
||||||
|
|
||||||
# nested assocations
|
```ruby
|
||||||
Article.joins(comments: :guest)
|
# Nested assocations
|
||||||
|
Article.joins(comments: :guest)
|
||||||
|
```
|
||||||
|
|
||||||
# sql
|
```ruby
|
||||||
Author.joins("INNER JOIN posts ON posts.author_id = authors.id AND posts.published = 't'")
|
# SQL
|
||||||
|
Author.joins(
|
||||||
|
'INNER JOIN posts ' +
|
||||||
|
'ON posts.author_id = authors.id ' +
|
||||||
|
'AND posts.published = "t"'
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
### Where interpolation
|
### Where interpolation
|
||||||
|
|
||||||
where("name = ?", "John")
|
```ruby
|
||||||
where(["name = :name", { name: "John" }])
|
where('name = ?', 'John')
|
||||||
|
where(['name = :name', { name: 'John' }])
|
||||||
|
```
|
||||||
|
|
||||||
### Serialize
|
### Serialize
|
||||||
|
|
||||||
class User < ActiveRecord::Base
|
```ruby
|
||||||
serialize :preferences
|
class User < ActiveRecord::Base
|
||||||
end
|
serialize :preferences
|
||||||
|
end
|
||||||
|
```
|
||||||
|
{: data-line="2"}
|
||||||
|
|
||||||
user = User.create(:preferences => { "background" => "black", "display" => large })
|
```ruby
|
||||||
|
user = User.create(
|
||||||
|
preferences: {
|
||||||
|
'background' => 'black',
|
||||||
|
'display' => 'large'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
You can also specify a class option as the second parameter that’ll raise an
|
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
|
exception if a serialized object is retrieved as a descendant of a class not in
|
||||||
the hierarchy.
|
the hierarchy.
|
||||||
|
|
||||||
class User < ActiveRecord::Base
|
```ruby
|
||||||
serialize :preferences, Hash
|
# Only Hash allowed!
|
||||||
end
|
class User < ActiveRecord::Base
|
||||||
|
serialize :preferences, Hash
|
||||||
|
end
|
||||||
|
```
|
||||||
|
{: data-line="3"}
|
||||||
|
|
||||||
user = User.create(:preferences => %w( one two three ))
|
```ruby
|
||||||
User.find(user.id).preferences # raises SerializationTypeMismatch
|
# Reading it raises SerializationTypeMismatch
|
||||||
|
user = User.create(preferences: %w(one two three))
|
||||||
|
User.find(user.id).preferences
|
||||||
|
```
|
||||||
|
|
||||||
Overriding accessors
|
Other tricks
|
||||||
--------------------
|
------------
|
||||||
|
|
||||||
class Song < ActiveRecord::Base
|
### Overriding accessors
|
||||||
# Uses an integer of seconds to hold the length of the song
|
|
||||||
|
|
||||||
def length=(minutes)
|
```ruby
|
||||||
write_attribute(:length, minutes.to_i * 60)
|
class Song < ActiveRecord::Base
|
||||||
end
|
# Uses an integer of seconds to hold the length of the song
|
||||||
|
|
||||||
def length
|
def length=(minutes)
|
||||||
read_attribute(:length) / 60
|
write_attribute(:length, minutes.to_i * 60)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
* http://api.rubyonrails.org/classes/ActiveRecord/Base.html
|
def length
|
||||||
|
read_attribute(:length) / 60
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
{: data-line="4,8"}
|
||||||
|
|
||||||
|
See: <http://api.rubyonrails.org/classes/ActiveRecord/Base.html>
|
||||||
|
|
||||||
Callbacks
|
Callbacks
|
||||||
---------
|
---------
|
||||||
|
|
||||||
after_create
|
- after_create
|
||||||
after_initialize
|
- after_initialize
|
||||||
after_validation
|
- after_validation
|
||||||
after_save
|
- after_save
|
||||||
after_commit
|
- after_commit
|
||||||
|
|
Loading…
Reference in New Issue