Related posts: fix up gutters

This commit is contained in:
Rico Sta. Cruz 2017-09-03 02:57:25 +08:00
parent 1c778d88aa
commit 4a4cb658f6
No known key found for this signature in database
GPG Key ID: CAAD38AE2962619A
6 changed files with 409 additions and 225 deletions

View File

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

View File

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

View File

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

View File

@ -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%;
} }

View File

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

View File

@ -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 thatll raise an You can also specify a class option as the second parameter thatll 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