Use irb code fences where applicable [ci-skip]
Using `irb` code fences and the appropriate prompt syntax results in better syntax highlighting.
This commit is contained in:
parent
82ab903653
commit
3c9d7a268f
|
@ -520,8 +520,7 @@ XmlMini.backend = 'LibXML'
|
|||
The `Time` and `TimeWithZone` classes include an `xmlschema` method to return the time in an XML-friendly string. As of Rails 2.3, `TimeWithZone` supports the same argument for specifying the number of digits in the fractional second part of the returned string that `Time` does:
|
||||
|
||||
```ruby
|
||||
>> Time.zone.now.xmlschema(6)
|
||||
=> "2009-01-16T13:00:06.13653Z"
|
||||
Time.zone.now.xmlschema(6) # => "2009-01-16T13:00:06.13653Z"
|
||||
```
|
||||
|
||||
* Lead Contributor: [Nicholas Dainty](http://www.workingwithrails.com/person/13536-nicholas-dainty)
|
||||
|
|
|
@ -144,8 +144,7 @@ The `direct` method allows creation of custom URL helpers.
|
|||
``` ruby
|
||||
direct(:homepage) { "http://www.rubyonrails.org" }
|
||||
|
||||
>> homepage_url
|
||||
=> "http://www.rubyonrails.org"
|
||||
homepage_url # => "http://www.rubyonrails.org"
|
||||
```
|
||||
|
||||
The return value of the block must be a valid argument for the `url_for`
|
||||
|
|
|
@ -49,12 +49,17 @@ class Person
|
|||
send(attribute) > 100
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new
|
||||
person.age = 110
|
||||
person.age_highest? # => true
|
||||
person.reset_age # => 0
|
||||
person.age_highest? # => false
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.age = 110
|
||||
irb> person.age_highest?
|
||||
=> true
|
||||
irb> person.reset_age
|
||||
=> 0
|
||||
irb> person.age_highest?
|
||||
=> false
|
||||
```
|
||||
|
||||
### Callbacks
|
||||
|
@ -102,11 +107,16 @@ class Person
|
|||
nil
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new
|
||||
person.to_model == person # => true
|
||||
person.to_key # => nil
|
||||
person.to_param # => nil
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.to_model == person
|
||||
=> true
|
||||
irb> person.to_key
|
||||
=> nil
|
||||
irb> person.to_param
|
||||
=> nil
|
||||
```
|
||||
|
||||
### Dirty
|
||||
|
@ -149,51 +159,62 @@ end
|
|||
|
||||
#### Querying object directly for its list of all changed attributes.
|
||||
|
||||
```ruby
|
||||
person = Person.new
|
||||
person.changed? # => false
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.changed?
|
||||
=> false
|
||||
|
||||
person.first_name = "First Name"
|
||||
person.first_name # => "First Name"
|
||||
irb> person.first_name = "First Name"
|
||||
irb> person.first_name
|
||||
=> "First Name"
|
||||
|
||||
# returns true if any of the attributes have unsaved changes.
|
||||
person.changed? # => true
|
||||
# Returns true if any of the attributes have unsaved changes.
|
||||
irb> person.changed?
|
||||
=> true
|
||||
|
||||
# returns a list of attributes that have changed before saving.
|
||||
person.changed # => ["first_name"]
|
||||
# Returns a list of attributes that have changed before saving.
|
||||
irb> person.changed
|
||||
=> ["first_name"]
|
||||
|
||||
# returns a Hash of the attributes that have changed with their original values.
|
||||
person.changed_attributes # => {"first_name"=>nil}
|
||||
# Returns a Hash of the attributes that have changed with their original values.
|
||||
irb> person.changed_attributes
|
||||
=> {"first_name"=>nil}
|
||||
|
||||
# returns a Hash of changes, with the attribute names as the keys, and the
|
||||
# values as an array of the old and new values for that field.
|
||||
person.changes # => {"first_name"=>[nil, "First Name"]}
|
||||
# Returns a Hash of changes, with the attribute names as the keys, and the values as an array of the old and new values for that field.
|
||||
irb> person.changes
|
||||
=> {"first_name"=>[nil, "First Name"]}
|
||||
```
|
||||
|
||||
#### Attribute based accessor methods
|
||||
|
||||
Track whether the particular attribute has been changed or not.
|
||||
|
||||
```ruby
|
||||
```irb
|
||||
irb> person.first_name
|
||||
=> "First Name"
|
||||
|
||||
# attr_name_changed?
|
||||
person.first_name # => "First Name"
|
||||
person.first_name_changed? # => true
|
||||
irb> person.first_name_changed?
|
||||
=> true
|
||||
```
|
||||
|
||||
Track the previous value of the attribute.
|
||||
|
||||
```ruby
|
||||
```irb
|
||||
# attr_name_was accessor
|
||||
person.first_name_was # => nil
|
||||
irb> person.first_name_was
|
||||
=> nil
|
||||
```
|
||||
|
||||
Track both previous and current value of the changed attribute. Returns an array
|
||||
if changed, otherwise returns nil.
|
||||
|
||||
```ruby
|
||||
```irb
|
||||
# attr_name_change
|
||||
person.first_name_change # => [nil, "First Name"]
|
||||
person.last_name_change # => nil
|
||||
irb> person.first_name_change
|
||||
=> [nil, "First Name"]
|
||||
irb> person.last_name_change
|
||||
=> nil
|
||||
```
|
||||
|
||||
### Validations
|
||||
|
@ -211,17 +232,23 @@ class Person
|
|||
validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i
|
||||
validates! :token, presence: true
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new
|
||||
person.token = "2b1f325"
|
||||
person.valid? # => false
|
||||
person.name = 'vishnu'
|
||||
person.email = 'me'
|
||||
person.valid? # => false
|
||||
person.email = 'me@vishnuatrai.com'
|
||||
person.valid? # => true
|
||||
person.token = nil
|
||||
person.valid? # => raises ActiveModel::StrictValidationFailed
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.token = "2b1f325"
|
||||
irb> person.valid?
|
||||
=> false
|
||||
irb> person.name = 'vishnu'
|
||||
irb> person.email = 'me'
|
||||
irb> person.valid?
|
||||
=> false
|
||||
irb> person.email = 'me@vishnuatrai.com'
|
||||
irb> person.valid?
|
||||
=> true
|
||||
irb> person.token = nil
|
||||
irb> person.valid?
|
||||
ActiveModel::StrictValidationFailed
|
||||
```
|
||||
|
||||
### Naming
|
||||
|
@ -277,14 +304,16 @@ When including `ActiveModel::Model` you get some features like:
|
|||
It also gives you the ability to initialize an object with a hash of attributes,
|
||||
much like any Active Record object.
|
||||
|
||||
```ruby
|
||||
email_contact = EmailContact.new(name: 'David',
|
||||
email: 'david@example.com',
|
||||
message: 'Hello World')
|
||||
email_contact.name # => 'David'
|
||||
email_contact.email # => 'david@example.com'
|
||||
email_contact.valid? # => true
|
||||
email_contact.persisted? # => false
|
||||
```irb
|
||||
irb> email_contact = EmailContact.new(name: 'David', email: 'david@example.com', message: 'Hello World')
|
||||
irb> email_contact.name
|
||||
=> "David"
|
||||
irb> email_contact.email
|
||||
=> "david@example.com"
|
||||
irb> email_contact.valid?
|
||||
=> true
|
||||
irb> email_contact.persisted?
|
||||
=> false
|
||||
```
|
||||
|
||||
Any class that includes `ActiveModel::Model` can be used with `form_with`,
|
||||
|
@ -311,11 +340,13 @@ end
|
|||
|
||||
Now you can access a serialized Hash of your object using the `serializable_hash` method.
|
||||
|
||||
```ruby
|
||||
person = Person.new
|
||||
person.serializable_hash # => {"name"=>nil}
|
||||
person.name = "Bob"
|
||||
person.serializable_hash # => {"name"=>"Bob"}
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.serializable_hash
|
||||
=> {"name"=>nil}
|
||||
irb> person.name = "Bob"
|
||||
irb> person.serializable_hash
|
||||
=> {"name"=>"Bob"}
|
||||
```
|
||||
|
||||
#### ActiveModel::Serializers
|
||||
|
@ -344,11 +375,13 @@ end
|
|||
The `as_json` method, similar to `serializable_hash`, provides a Hash representing
|
||||
the model.
|
||||
|
||||
```ruby
|
||||
person = Person.new
|
||||
person.as_json # => {"name"=>nil}
|
||||
person.name = "Bob"
|
||||
person.as_json # => {"name"=>"Bob"}
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.as_json
|
||||
=> {"name"=>nil}
|
||||
irb> person.name = "Bob"
|
||||
irb> person.as_json
|
||||
=> {"name"=>"Bob"}
|
||||
```
|
||||
|
||||
You can also define the attributes for a model from a JSON string.
|
||||
|
@ -374,11 +407,13 @@ end
|
|||
|
||||
Now it is possible to create an instance of `Person` and set attributes using `from_json`.
|
||||
|
||||
```ruby
|
||||
json = { name: 'Bob' }.to_json
|
||||
person = Person.new
|
||||
person.from_json(json) # => #<Person:0x00000100c773f0 @name="Bob">
|
||||
person.name # => "Bob"
|
||||
```irb
|
||||
irb> json = { name: 'Bob' }.to_json
|
||||
irb> person = Person.new
|
||||
irb> person.from_json(json)
|
||||
=> #<Person:0x00000100c773f0 @name="Bob">
|
||||
irb> person.name
|
||||
=> "Bob"
|
||||
```
|
||||
|
||||
### Translation
|
||||
|
@ -483,39 +518,54 @@ class Person
|
|||
|
||||
attr_accessor :password_digest, :recovery_password_digest
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
|
||||
# When password is blank.
|
||||
person.valid? # => false
|
||||
irb> person.valid?
|
||||
=> false
|
||||
|
||||
# When the confirmation doesn't match the password.
|
||||
person.password = 'aditya'
|
||||
person.password_confirmation = 'nomatch'
|
||||
person.valid? # => false
|
||||
irb> person.password = 'aditya'
|
||||
irb> person.password_confirmation = 'nomatch'
|
||||
irb> person.valid?
|
||||
=> false
|
||||
|
||||
# When the length of password exceeds 72.
|
||||
person.password = person.password_confirmation = 'a' * 100
|
||||
person.valid? # => false
|
||||
irb> person.password = person.password_confirmation = 'a' * 100
|
||||
irb> person.valid?
|
||||
=> false
|
||||
|
||||
# When only password is supplied with no password_confirmation.
|
||||
person.password = 'aditya'
|
||||
person.valid? # => true
|
||||
irb> person.password = 'aditya'
|
||||
irb> person.valid?
|
||||
=> true
|
||||
|
||||
# When all validations are passed.
|
||||
person.password = person.password_confirmation = 'aditya'
|
||||
person.valid? # => true
|
||||
irb> person.password = person.password_confirmation = 'aditya'
|
||||
irb> person.valid?
|
||||
=> true
|
||||
|
||||
person.recovery_password = "42password"
|
||||
irb> person.recovery_password = "42password"
|
||||
|
||||
person.authenticate('aditya') # => person
|
||||
person.authenticate('notright') # => false
|
||||
person.authenticate_password('aditya') # => person
|
||||
person.authenticate_password('notright') # => false
|
||||
irb> person.authenticate('aditya')
|
||||
=> #<Person> # == person
|
||||
irb> person.authenticate('notright')
|
||||
=> false
|
||||
irb> person.authenticate_password('aditya')
|
||||
=> #<Person> # == person
|
||||
irb> person.authenticate_password('notright')
|
||||
=> false
|
||||
|
||||
person.authenticate_recovery_password('42password') # => person
|
||||
person.authenticate_recovery_password('notright') # => false
|
||||
irb> person.authenticate_recovery_password('42password')
|
||||
=> #<Person> # == person
|
||||
irb> person.authenticate_recovery_password('notright')
|
||||
=> false
|
||||
|
||||
person.password_digest # => "$2a$04$gF8RfZdoXHvyTjHhiU4ZsO.kQqV9oonYZu31PRE4hLQn3xM2qkpIy"
|
||||
person.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uXkTG65HR.qpW.bNhEjFP3ftli3o5DQC"
|
||||
irb> person.password_digest
|
||||
=> "$2a$04$gF8RfZdoXHvyTjHhiU4ZsO.kQqV9oonYZu31PRE4hLQn3xM2qkpIy"
|
||||
irb> person.recovery_password_digest
|
||||
=> "$2a$04$iOfhwahFymCs5weB3BNH/uXkTG65HR.qpW.bNhEjFP3ftli3o5DQC"
|
||||
```
|
||||
|
|
|
@ -339,10 +339,14 @@ A quick example to illustrate:
|
|||
class User < ApplicationRecord
|
||||
validates :name, presence: true
|
||||
end
|
||||
```
|
||||
|
||||
user = User.new
|
||||
user.save # => false
|
||||
user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
||||
```irb
|
||||
irb> user = User.new
|
||||
irb> user.save
|
||||
=> false
|
||||
irb> user.save!
|
||||
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
||||
```
|
||||
|
||||
You can learn more about validations in the [Active Record Validations
|
||||
|
|
|
@ -141,12 +141,14 @@ class User < ApplicationRecord
|
|||
puts "You have found an object!"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
>> User.new
|
||||
```irb
|
||||
irb> User.new
|
||||
You have initialized an object!
|
||||
=> #<User id: nil>
|
||||
|
||||
>> User.first
|
||||
irb> User.first
|
||||
You have found an object!
|
||||
You have initialized an object!
|
||||
=> #<User id: 1>
|
||||
|
@ -162,11 +164,13 @@ class User < ApplicationRecord
|
|||
puts "You have touched an object"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
>> u = User.create(name: 'Kuldeep')
|
||||
```irb
|
||||
irb> u = User.create(name: 'Kuldeep')
|
||||
=> #<User id: 1, name: "Kuldeep", created_at: "2013-11-25 12:17:49", updated_at: "2013-11-25 12:17:49">
|
||||
|
||||
>> u.touch
|
||||
irb> u.touch
|
||||
You have touched an object
|
||||
=> true
|
||||
```
|
||||
|
@ -190,12 +194,13 @@ class Company < ApplicationRecord
|
|||
puts 'Employee/Company was touched'
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
>> @employee = Employee.last
|
||||
```irb
|
||||
irb> @employee = Employee.last
|
||||
=> #<Employee id: 1, company_id: 1, created_at: "2013-11-25 17:04:22", updated_at: "2013-11-25 17:05:05">
|
||||
|
||||
# triggers @employee.company.touch
|
||||
>> @employee.touch
|
||||
irb> @employee.touch # triggers @employee.company.touch
|
||||
An Employee was touched
|
||||
Employee/Company was touched
|
||||
=> true
|
||||
|
@ -293,12 +298,14 @@ class Article < ApplicationRecord
|
|||
puts 'Article destroyed'
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
>> user = User.first
|
||||
```irb
|
||||
irb> user = User.first
|
||||
=> #<User id: 1>
|
||||
>> user.articles.create!
|
||||
irb> user.articles.create!
|
||||
=> #<Article id: 1, user_id: 1>
|
||||
>> user.destroy
|
||||
irb> user.destroy
|
||||
Article destroyed
|
||||
=> #<User id: 1>
|
||||
```
|
||||
|
@ -475,13 +482,13 @@ class User < ApplicationRecord
|
|||
puts 'User was saved to database'
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
# prints nothing
|
||||
>> @user = User.create
|
||||
```irb
|
||||
irb> @user = User.create # prints nothing
|
||||
|
||||
# updating @user
|
||||
>> @user.save
|
||||
=> User was saved to database
|
||||
irb> @user.save # updating @user
|
||||
User was saved to database
|
||||
```
|
||||
|
||||
There is also an alias for using the `after_commit` callback for both create and update together:
|
||||
|
@ -497,12 +504,12 @@ class User < ApplicationRecord
|
|||
puts 'User was saved to database'
|
||||
end
|
||||
end
|
||||
|
||||
# creating a User
|
||||
>> @user = User.create
|
||||
=> User was saved to database
|
||||
|
||||
# updating @user
|
||||
>> @user.save
|
||||
=> User was saved to database
|
||||
```
|
||||
|
||||
```irb
|
||||
irb> @user = User.create # creating a User
|
||||
User was saved to database
|
||||
|
||||
irb> @user.save # updating @user
|
||||
User was saved to database
|
||||
```
|
||||
|
|
|
@ -96,22 +96,26 @@ ActiveRecord::Schema.define do
|
|||
t.hstore 'settings'
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/profile.rb
|
||||
class Profile < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
Profile.create(settings: { "color" => "blue", "resolution" => "800x600" })
|
||||
```irb
|
||||
irb> Profile.create(settings: { "color" => "blue", "resolution" => "800x600" })
|
||||
|
||||
profile = Profile.first
|
||||
profile.settings # => {"color"=>"blue", "resolution"=>"800x600"}
|
||||
irb> profile = Profile.first
|
||||
irb> profile.settings
|
||||
=> {"color"=>"blue", "resolution"=>"800x600"}
|
||||
|
||||
profile.settings = {"color" => "yellow", "resolution" => "1280x1024"}
|
||||
profile.save!
|
||||
irb> profile.settings = {"color" => "yellow", "resolution" => "1280x1024"}
|
||||
irb> profile.save!
|
||||
|
||||
Profile.where("settings->'color' = ?", "yellow")
|
||||
# => #<ActiveRecord::Relation [#<Profile id: 1, settings: {"color"=>"yellow", "resolution"=>"1280x1024"}>]>
|
||||
irb> Profile.where("settings->'color' = ?", "yellow")
|
||||
=> #<ActiveRecord::Relation [#<Profile id: 1, settings: {"color"=>"yellow", "resolution"=>"1280x1024"}>]>
|
||||
```
|
||||
|
||||
### JSON and JSONB
|
||||
|
@ -129,20 +133,24 @@ end
|
|||
create_table :events do |t|
|
||||
t.jsonb 'payload'
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/event.rb
|
||||
class Event < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
Event.create(payload: { kind: "user_renamed", change: ["jack", "john"]})
|
||||
```irb
|
||||
irb> Event.create(payload: { kind: "user_renamed", change: ["jack", "john"]})
|
||||
|
||||
event = Event.first
|
||||
event.payload # => {"kind"=>"user_renamed", "change"=>["jack", "john"]}
|
||||
irb> event = Event.first
|
||||
irb> event.payload
|
||||
=> {"kind"=>"user_renamed", "change"=>["jack", "john"]}
|
||||
|
||||
## Query based on JSON document
|
||||
# The -> operator returns the original JSON type (which might be an object), whereas ->> returns text
|
||||
Event.where("payload->>'kind' = ?", "user_renamed")
|
||||
irb> Event.where("payload->>'kind' = ?", "user_renamed")
|
||||
```
|
||||
|
||||
### Range Types
|
||||
|
@ -157,27 +165,31 @@ This type is mapped to Ruby [`Range`](https://ruby-doc.org/core-2.5.0/Range.html
|
|||
create_table :events do |t|
|
||||
t.daterange 'duration'
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/event.rb
|
||||
class Event < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
Event.create(duration: Date.new(2014, 2, 11)..Date.new(2014, 2, 12))
|
||||
```irb
|
||||
irb> Event.create(duration: Date.new(2014, 2, 11)..Date.new(2014, 2, 12))
|
||||
|
||||
event = Event.first
|
||||
event.duration # => Tue, 11 Feb 2014...Thu, 13 Feb 2014
|
||||
irb> event = Event.first
|
||||
irb> event.duration
|
||||
=> Tue, 11 Feb 2014...Thu, 13 Feb 2014
|
||||
|
||||
## All Events on a given date
|
||||
Event.where("duration @> ?::date", Date.new(2014, 2, 12))
|
||||
irb> Event.where("duration @> ?::date", Date.new(2014, 2, 12))
|
||||
|
||||
## Working with range bounds
|
||||
event = Event.
|
||||
select("lower(duration) AS starts_at").
|
||||
select("upper(duration) AS ends_at").first
|
||||
irb> event = Event.select("lower(duration) AS starts_at").select("upper(duration) AS ends_at").first
|
||||
|
||||
event.starts_at # => Tue, 11 Feb 2014
|
||||
event.ends_at # => Thu, 13 Feb 2014
|
||||
irb> event.starts_at
|
||||
=> Tue, 11 Feb 2014
|
||||
irb> event.ends_at
|
||||
=> Thu, 13 Feb 2014
|
||||
```
|
||||
|
||||
### Composite Types
|
||||
|
@ -207,17 +219,21 @@ SQL
|
|||
create_table :contacts do |t|
|
||||
t.column :address, :full_address
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/contact.rb
|
||||
class Contact < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
Contact.create address: "(Paris,Champs-Élysées)"
|
||||
contact = Contact.first
|
||||
contact.address # => "(Paris,Champs-Élysées)"
|
||||
contact.address = "(Paris,Rue Basse)"
|
||||
contact.save!
|
||||
```irb
|
||||
irb> Contact.create address: "(Paris,Champs-Élysées)"
|
||||
irb> contact = Contact.first
|
||||
irb> contact.address
|
||||
=> "(Paris,Champs-Élysées)"
|
||||
irb> contact.address = "(Paris,Rue Basse)"
|
||||
irb> contact.save!
|
||||
```
|
||||
|
||||
### Enumerated Types
|
||||
|
@ -246,18 +262,22 @@ def down
|
|||
DROP TYPE article_status;
|
||||
SQL
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/article.rb
|
||||
class Article < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
Article.create status: "draft"
|
||||
article = Article.first
|
||||
article.status # => "draft"
|
||||
```irb
|
||||
irb> Article.create status: "draft"
|
||||
irb> article = Article.first
|
||||
irb> article.status
|
||||
=> "draft"
|
||||
|
||||
article.status = "published"
|
||||
article.save!
|
||||
irb> article.status = "published"
|
||||
irb> article.save!
|
||||
```
|
||||
|
||||
To add a new value before/after existing one you should use [ALTER TYPE](https://www.postgresql.org/docs/current/static/sql-altertype.html):
|
||||
|
@ -301,16 +321,20 @@ extension to use uuid.
|
|||
create_table :revisions do |t|
|
||||
t.uuid :identifier
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/revision.rb
|
||||
class Revision < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
Revision.create identifier: "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11"
|
||||
```irb
|
||||
irb> Revision.create identifier: "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11"
|
||||
|
||||
revision = Revision.first
|
||||
revision.identifier # => "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
|
||||
irb> revision = Revision.first
|
||||
irb> revision.identifier
|
||||
=> "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
|
||||
```
|
||||
|
||||
You can use `uuid` type to define references in migrations:
|
||||
|
@ -348,18 +372,23 @@ See [this section](#uuid-primary-keys) for more details on using UUIDs as primar
|
|||
create_table :users, force: true do |t|
|
||||
t.column :settings, "bit(8)"
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/user.rb
|
||||
class User < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
User.create settings: "01010011"
|
||||
user = User.first
|
||||
user.settings # => "01010011"
|
||||
user.settings = "0xAF"
|
||||
user.settings # => 10101111
|
||||
user.save!
|
||||
```irb
|
||||
irb> User.create settings: "01010011"
|
||||
irb> user = User.first
|
||||
irb> user.settings
|
||||
=> "01010011"
|
||||
irb> user.settings = "0xAF"
|
||||
irb> user.settings
|
||||
=> 10101111
|
||||
irb> user.save!
|
||||
```
|
||||
|
||||
### Network Address Types
|
||||
|
@ -377,24 +406,25 @@ create_table(:devices, force: true) do |t|
|
|||
t.cidr 'network'
|
||||
t.macaddr 'address'
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/device.rb
|
||||
class Device < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
macbook = Device.create(ip: "192.168.1.12",
|
||||
network: "192.168.2.0/24",
|
||||
address: "32:01:16:6d:05:ef")
|
||||
```irb
|
||||
irb> macbook = Device.create(ip: "192.168.1.12", network: "192.168.2.0/24", address: "32:01:16:6d:05:ef")
|
||||
|
||||
macbook.ip
|
||||
# => #<IPAddr: IPv4:192.168.1.12/255.255.255.255>
|
||||
irb> macbook.ip
|
||||
=> #<IPAddr: IPv4:192.168.1.12/255.255.255.255>
|
||||
|
||||
macbook.network
|
||||
# => #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
|
||||
irb> macbook.network
|
||||
=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
|
||||
|
||||
macbook.address
|
||||
# => "32:01:16:6d:05:ef"
|
||||
irb> macbook.address
|
||||
=> "32:01:16:6d:05:ef"
|
||||
```
|
||||
|
||||
### Geometric Types
|
||||
|
@ -416,16 +446,20 @@ This type is mapped to [`ActiveSupport::Duration`](http://api.rubyonrails.org/cl
|
|||
create_table :events do |t|
|
||||
t.interval 'duration'
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/event.rb
|
||||
class Event < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
Event.create(duration: 2.days)
|
||||
```irb
|
||||
irb> Event.create(duration: 2.days)
|
||||
|
||||
event = Event.first
|
||||
event.duration # => 2 days
|
||||
irb> event = Event.first
|
||||
irb> event.duration
|
||||
=> 2 days
|
||||
```
|
||||
|
||||
UUID Primary Keys
|
||||
|
@ -440,14 +474,18 @@ enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')
|
|||
create_table :devices, id: :uuid do |t|
|
||||
t.string :kind
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/device.rb
|
||||
class Device < ApplicationRecord
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
device = Device.create
|
||||
device.id # => "814865cd-5a1d-4771-9306-4268f188fe9e"
|
||||
```ruby
|
||||
irb> device = Device.create
|
||||
irb> device.id
|
||||
=> "814865cd-5a1d-4771-9306-4268f188fe9e"
|
||||
```
|
||||
|
||||
NOTE: `gen_random_uuid()` (from `pgcrypto`) is assumed if no `:default` option was
|
||||
|
@ -514,7 +552,9 @@ CREATE VIEW articles AS
|
|||
FROM "TBL_ART"
|
||||
WHERE "BL_ARCH" = 'f'
|
||||
SQL
|
||||
```
|
||||
|
||||
```ruby
|
||||
# app/models/article.rb
|
||||
class Article < ApplicationRecord
|
||||
self.primary_key = "id"
|
||||
|
@ -522,18 +562,17 @@ class Article < ApplicationRecord
|
|||
update_attribute :archived, true
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
# Usage
|
||||
first = Article.create! title: "Winter is coming",
|
||||
status: "published",
|
||||
published_at: 1.year.ago
|
||||
second = Article.create! title: "Brace yourself",
|
||||
status: "draft",
|
||||
published_at: 1.month.ago
|
||||
```irb
|
||||
irb> first = Article.create! title: "Winter is coming", status: "published", published_at: 1.year.ago
|
||||
irb> second = Article.create! title: "Brace yourself", status: "draft", published_at: 1.month.ago
|
||||
|
||||
Article.count # => 2
|
||||
first.archive!
|
||||
Article.count # => 1
|
||||
irb> Article.count
|
||||
=> 2
|
||||
irb> first.archive!
|
||||
irb> Article.count
|
||||
=> 1
|
||||
```
|
||||
|
||||
NOTE: This application only cares about non-archived `Articles`. A view also
|
||||
|
|
|
@ -138,10 +138,10 @@ Active Record provides several different ways of retrieving a single object.
|
|||
|
||||
Using the `find` method, you can retrieve the object corresponding to the specified _primary key_ that matches any supplied options. For example:
|
||||
|
||||
```ruby
|
||||
```irb
|
||||
# Find the customer with primary key (id) 10.
|
||||
customer = Customer.find(10)
|
||||
# => #<Customer id: 10, first_name: "Ryan">
|
||||
irb> customer = Customer.find(10)
|
||||
=> #<Customer id: 10, first_name: "Ryan">
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -154,10 +154,10 @@ The `find` method will raise an `ActiveRecord::RecordNotFound` exception if no m
|
|||
|
||||
You can also use this method to query for multiple objects. Call the `find` method and pass in an array of primary keys. The return will be an array containing all of the matching records for the supplied _primary keys_. For example:
|
||||
|
||||
```ruby
|
||||
```irb
|
||||
# Find the customers with primary keys 1 and 10.
|
||||
customers = Customer.find([1, 10]) # Or even Customer.find(1, 10)
|
||||
# => [#<Customer id: 1, first_name: "Lifo">, #<Customer id: 10, first_name: "Ryan">]
|
||||
irb> customers = Customer.find([1, 10]) # OR Customer.find(1, 10)
|
||||
=> [#<Customer id: 1, first_name: "Lifo">, #<Customer id: 10, first_name: "Ryan">]
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -172,9 +172,9 @@ WARNING: The `find` method will raise an `ActiveRecord::RecordNotFound` exceptio
|
|||
|
||||
The `take` method retrieves a record without any implicit ordering. For example:
|
||||
|
||||
```ruby
|
||||
customer = Customer.take
|
||||
# => #<Customer id: 1, first_name: "Lifo">
|
||||
```irb
|
||||
irb> customer = Customer.take
|
||||
=> #<Customer id: 1, first_name: "Lifo">
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -187,12 +187,9 @@ The `take` method returns `nil` if no record is found and no exception will be r
|
|||
|
||||
You can pass in a numerical argument to the `take` method to return up to that number of results. For example
|
||||
|
||||
```ruby
|
||||
customers = Customer.take(2)
|
||||
# => [
|
||||
# #<Customer id: 1, first_name: "Lifo">,
|
||||
# #<Customer id: 220, first_name: "Sara">
|
||||
# ]
|
||||
```irb
|
||||
irb> customers = Customer.take(2)
|
||||
=> [#<Customer id: 1, first_name: "Lifo">, #<Customer id: 220, first_name: "Sara">]
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -209,9 +206,9 @@ TIP: The retrieved record may vary depending on the database engine.
|
|||
|
||||
The `first` method finds the first record ordered by primary key (default). For example:
|
||||
|
||||
```ruby
|
||||
customer = Customer.first
|
||||
# => #<Customer id: 1, first_name: "Lifo">
|
||||
```irb
|
||||
irb> customer = Customer.first
|
||||
=> #<Customer id: 1, first_name: "Lifo">
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -226,13 +223,9 @@ If your [default scope](active_record_querying.html#applying-a-default-scope) co
|
|||
|
||||
You can pass in a numerical argument to the `first` method to return up to that number of results. For example
|
||||
|
||||
```ruby
|
||||
customers = Customer.first(3)
|
||||
# => [
|
||||
# #<Customer id: 1, first_name: "Lifo">,
|
||||
# #<Customer id: 2, first_name: "Fifo">,
|
||||
# #<Customer id: 3, first_name: "Filo">
|
||||
# ]
|
||||
```irb
|
||||
irb> customers = Customer.first(3)
|
||||
=> [#<Customer id: 1, first_name: "Lifo">, #<Customer id: 2, first_name: "Fifo">, #<Customer id: 3, first_name: "Filo">]
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -243,9 +236,9 @@ SELECT * FROM customers ORDER BY customers.id ASC LIMIT 3
|
|||
|
||||
On a collection that is ordered using `order`, `first` will return the first record ordered by the specified attribute for `order`.
|
||||
|
||||
```ruby
|
||||
customer = Customer.order(:first_name).first
|
||||
# => #<Customer id: 2, first_name: "Fifo">
|
||||
```irb
|
||||
irb> customer = Customer.order(:first_name).first
|
||||
=> #<Customer id: 2, first_name: "Fifo">
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -260,9 +253,9 @@ The `first!` method behaves exactly like `first`, except that it will raise `Act
|
|||
|
||||
The `last` method finds the last record ordered by primary key (default). For example:
|
||||
|
||||
```ruby
|
||||
customer = Customer.last
|
||||
# => #<Customer id: 221, first_name: "Russel">
|
||||
```irb
|
||||
irb> customer = Customer.last
|
||||
=> #<Customer id: 221, first_name: "Russel">
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -277,13 +270,9 @@ If your [default scope](active_record_querying.html#applying-a-default-scope) co
|
|||
|
||||
You can pass in a numerical argument to the `last` method to return up to that number of results. For example
|
||||
|
||||
```ruby
|
||||
customers = Customer.last(3)
|
||||
# => [
|
||||
# #<Customer id: 219, first_name: "James">,
|
||||
# #<Customer id: 220, first_name: "Sara">,
|
||||
# #<Customer id: 221, first_name: "Russel">
|
||||
# ]
|
||||
```irb
|
||||
irb> customers = Customer.last(3)
|
||||
=> [#<Customer id: 219, first_name: "James">, #<Customer id: 220, first_name: "Sara">, #<Customer id: 221, first_name: "Russel">]
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -294,9 +283,9 @@ SELECT * FROM customers ORDER BY customers.id DESC LIMIT 3
|
|||
|
||||
On a collection that is ordered using `order`, `last` will return the last record ordered by the specified attribute for `order`.
|
||||
|
||||
```ruby
|
||||
customer = Customer.order(:first_name).last
|
||||
# => #<Customer id: 220, first_name: "Sara">
|
||||
```irb
|
||||
irb> customer = Customer.order(:first_name).last
|
||||
=> #<Customer id: 220, first_name: "Sara">
|
||||
```
|
||||
|
||||
The SQL equivalent of the above is:
|
||||
|
@ -311,12 +300,12 @@ The `last!` method behaves exactly like `last`, except that it will raise `Activ
|
|||
|
||||
The `find_by` method finds the first record matching some conditions. For example:
|
||||
|
||||
```ruby
|
||||
Customer.find_by first_name: 'Lifo'
|
||||
# => #<Customer id: 1, first_name: "Lifo">
|
||||
```irb
|
||||
irb> Customer.find_by first_name: 'Lifo'
|
||||
=> #<Customer id: 1, first_name: "Lifo">
|
||||
|
||||
Customer.find_by first_name: 'Jon'
|
||||
# => nil
|
||||
irb> Customer.find_by first_name: 'Jon'
|
||||
=> nil
|
||||
```
|
||||
|
||||
It is equivalent to writing:
|
||||
|
@ -333,9 +322,9 @@ SELECT * FROM customers WHERE (customers.first_name = 'Lifo') LIMIT 1
|
|||
|
||||
The `find_by!` method behaves exactly like `find_by`, except that it will raise `ActiveRecord::RecordNotFound` if no matching record is found. For example:
|
||||
|
||||
```ruby
|
||||
Customer.find_by! first_name: 'does not exist'
|
||||
# => ActiveRecord::RecordNotFound
|
||||
```irb
|
||||
irb> Customer.find_by! first_name: 'does not exist'
|
||||
ActiveRecord::RecordNotFound
|
||||
```
|
||||
|
||||
This is equivalent to writing:
|
||||
|
@ -681,9 +670,9 @@ Customer.order("orders_count ASC", "created_at DESC")
|
|||
|
||||
If you want to call `order` multiple times, subsequent orders will be appended to the first:
|
||||
|
||||
```ruby
|
||||
Customer.order("orders_count ASC").order("created_at DESC")
|
||||
# SELECT * FROM customers ORDER BY orders_count ASC, created_at DESC
|
||||
```irb
|
||||
irb> Customer.order("orders_count ASC").order("created_at DESC")
|
||||
SELECT * FROM customers ORDER BY orders_count ASC, created_at DESC
|
||||
```
|
||||
|
||||
WARNING: In most database systems, on selecting fields with `distinct` from a result set using methods like `select`, `pluck` and `ids`; the `order` method will raise an `ActiveRecord::StatementInvalid` exception unless the field(s) used in `order` clause are included in the select list. See the next section for selecting fields from the result set.
|
||||
|
@ -732,11 +721,11 @@ SELECT DISTINCT last_name FROM customers
|
|||
You can also remove the uniqueness constraint:
|
||||
|
||||
```ruby
|
||||
# Returns unique last_names
|
||||
query = Customer.select(:last_name).distinct
|
||||
# => Returns unique last_names
|
||||
|
||||
# Returns all last_names, even if there are duplicates
|
||||
query.distinct(false)
|
||||
# => Returns all last_names, even if there are duplicates
|
||||
```
|
||||
|
||||
Limit and Offset
|
||||
|
@ -793,9 +782,9 @@ GROUP BY created_at
|
|||
|
||||
To get the total of grouped items on a single query, call `count` after the `group`.
|
||||
|
||||
```ruby
|
||||
Order.group(:status).count
|
||||
# => { 'being_packed' => 7, 'shipped' => 12 }
|
||||
```irb
|
||||
irb> Order.group(:status).count
|
||||
=> {"being_packed"=>7, "shipped"=>12}
|
||||
```
|
||||
|
||||
The SQL that would be executed would be something like this:
|
||||
|
@ -1387,15 +1376,17 @@ end
|
|||
|
||||
To call this `out_of_print` scope we can call it on either the class:
|
||||
|
||||
```ruby
|
||||
Book.out_of_print # => [all books out of print]
|
||||
```irb
|
||||
irb> Book.out_of_print
|
||||
=> #<ActiveRecord::Relation> # all out of print books
|
||||
```
|
||||
|
||||
Or on an association consisting of `Book` objects:
|
||||
|
||||
```ruby
|
||||
author = Author.first
|
||||
author.books.out_of_print # => [all out of print books by this author]
|
||||
```irb
|
||||
irb> author = Author.first
|
||||
irb> author.books.out_of_print
|
||||
=> #<ActiveRecord::Relation> # all out of print books by `author`
|
||||
```
|
||||
|
||||
Scopes are also chainable within scopes:
|
||||
|
@ -1419,8 +1410,8 @@ end
|
|||
|
||||
Call the scope as if it were a class method:
|
||||
|
||||
```ruby
|
||||
Book.costs_more_than(100.10)
|
||||
```irb
|
||||
irb> Book.costs_more_than(100.10)
|
||||
```
|
||||
|
||||
However, this is just duplicating the functionality that would be provided to you by a class method.
|
||||
|
@ -1435,8 +1426,8 @@ end
|
|||
|
||||
These methods will still be accessible on the association objects:
|
||||
|
||||
```ruby
|
||||
author.books.costs_more_than(100.10)
|
||||
```irb
|
||||
irb> author.books.costs_more_than(100.10)
|
||||
```
|
||||
|
||||
### Using conditionals
|
||||
|
@ -1498,9 +1489,13 @@ updating a record. E.g.:
|
|||
class Book < ApplicationRecord
|
||||
default_scope { where(out_of_print: false) }
|
||||
end
|
||||
```
|
||||
|
||||
Book.new # => #<Book id: nil, out_of_print: false>
|
||||
Book.unscoped.new # => #<Book id: nil, out_of_print: nil>
|
||||
```irb
|
||||
irb> Book.new
|
||||
=> #<Book id: nil, out_of_print: false>
|
||||
irb> Book.unscoped.new
|
||||
=> #<Book id: nil, out_of_print: nil>
|
||||
```
|
||||
|
||||
Be aware that, when given in the `Array` format, `default_scope` query arguments
|
||||
|
@ -1510,8 +1505,11 @@ cannot be converted to a `Hash` for default attribute assignment. E.g.:
|
|||
class Book < ApplicationRecord
|
||||
default_scope { where("out_of_print = ?", false) }
|
||||
end
|
||||
```
|
||||
|
||||
Book.new # => #<Book id: nil, out_of_print: nil>
|
||||
```irb
|
||||
irb> Book.new
|
||||
=> #<Book id: nil, out_of_print: nil>
|
||||
```
|
||||
|
||||
### Merging of scopes
|
||||
|
@ -1526,25 +1524,27 @@ class Book < ApplicationRecord
|
|||
scope :recent, -> { where('year_published >= ?', Date.current.year - 50 )}
|
||||
scope :old, -> { where('year_published < ?', Date.current.year - 50 )}
|
||||
end
|
||||
```
|
||||
|
||||
Book.out_of_print.old
|
||||
# SELECT books.* FROM books WHERE books.out_of_print = 'true' AND books.year_published < 1969
|
||||
```irb
|
||||
irb> Book.out_of_print.old
|
||||
SELECT books.* FROM books WHERE books.out_of_print = 'true' AND books.year_published < 1969
|
||||
```
|
||||
|
||||
We can mix and match `scope` and `where` conditions and the final SQL
|
||||
will have all conditions joined with `AND`.
|
||||
|
||||
```ruby
|
||||
Book.in_print.where('price < 100')
|
||||
# SELECT books.* FROM books WHERE books.out_of_print = 'false' AND books.price < 100
|
||||
```irb
|
||||
irb> Book.in_print.where('price < 100')
|
||||
SELECT books.* FROM books WHERE books.out_of_print = 'false' AND books.price < 100
|
||||
```
|
||||
|
||||
If we do want the last `where` clause to win then `Relation#merge` can
|
||||
be used.
|
||||
|
||||
```ruby
|
||||
Book.in_print.merge(Book.out_of_print)
|
||||
# SELECT books.* FROM books WHERE books.out_of_print = true
|
||||
```irb
|
||||
irb> Book.in_print.merge(Book.out_of_print)
|
||||
SELECT books.* FROM books WHERE books.out_of_print = true
|
||||
```
|
||||
|
||||
One important caveat is that `default_scope` will be prepended in
|
||||
|
@ -1557,15 +1557,17 @@ class Book < ApplicationRecord
|
|||
scope :in_print, -> { where(out_of_print: false) }
|
||||
scope :out_of_print, -> { where(out_of_print: true) }
|
||||
end
|
||||
```
|
||||
|
||||
Book.all
|
||||
# SELECT books.* FROM books WHERE (year_published >= 1969)
|
||||
```irb
|
||||
irb> Book.all
|
||||
SELECT books.* FROM books WHERE (year_published >= 1969)
|
||||
|
||||
Book.in_print
|
||||
# SELECT books.* FROM books WHERE (year_published >= 1969) AND books.out_of_print = true
|
||||
irb> Book.in_print
|
||||
SELECT books.* FROM books WHERE (year_published >= 1969) AND books.out_of_print = true
|
||||
|
||||
Book.where('price > 50')
|
||||
# SELECT books.* FROM books WHERE (year_published >= 1969) AND (price > 50)
|
||||
irb> Book.where('price > 50')
|
||||
SELECT books.* FROM books WHERE (year_published >= 1969) AND (price > 50)
|
||||
```
|
||||
|
||||
As you can see above the `default_scope` is being merged in both
|
||||
|
@ -1583,21 +1585,19 @@ Book.unscoped.load
|
|||
|
||||
This method removes all scoping and will do a normal query on the table.
|
||||
|
||||
```ruby
|
||||
Book.unscoped.all
|
||||
# SELECT books.* FROM books
|
||||
```irb
|
||||
irb> Book.unscoped.all
|
||||
SELECT books.* FROM books
|
||||
|
||||
Book.where(out_of_print: true).unscoped.all
|
||||
# SELECT books.* FROM books
|
||||
irb> Book.where(out_of_print: true).unscoped.all
|
||||
SELECT books.* FROM books
|
||||
```
|
||||
|
||||
`unscoped` can also accept a block:
|
||||
|
||||
```ruby
|
||||
Book.unscoped {
|
||||
Book.out_of_print
|
||||
}
|
||||
# SELECT books.* FROM books WHERE books.out_of_print
|
||||
```irb
|
||||
irb> Book.unscoped { Book.out_of_print }
|
||||
SELECT books.* FROM books WHERE books.out_of_print
|
||||
```
|
||||
|
||||
Dynamic Finders
|
||||
|
@ -1637,34 +1637,31 @@ end
|
|||
|
||||
These [scopes](#scopes) are created automatically and can be used to find all objects with or wihout a particular value for `status`:
|
||||
|
||||
```ruby
|
||||
Order.shipped
|
||||
# finds all orders with status == :shipped
|
||||
Order.not_shipped
|
||||
# finds all orders with status != :shipped
|
||||
...
|
||||
```irb
|
||||
irb> Order.shipped
|
||||
=> #<ActiveRecord::Relation> # all orders with status == :shipped
|
||||
irb> Order.not_shipped
|
||||
=> #<ActiveRecord::Relation> # all orders with status != :shipped
|
||||
```
|
||||
|
||||
These instace methods are created automatically and query whether the model has that value for the `status` enum:
|
||||
|
||||
```ruby
|
||||
order = Order.first
|
||||
order.shipped?
|
||||
# Returns true if status == :shipped
|
||||
order.complete?
|
||||
# Returns true if status == :complete
|
||||
...
|
||||
```irb
|
||||
irb> order = Order.shipped.first
|
||||
irb> order.shipped?
|
||||
=> true
|
||||
irb> order.complete?
|
||||
=> false
|
||||
```
|
||||
|
||||
These instance methods are created automatically and will first update the value of `status` to the named value
|
||||
and then query whether or not the status has been successfully set to the value:
|
||||
|
||||
```ruby
|
||||
order = Order.first
|
||||
order.shipped!
|
||||
# => UPDATE "orders" SET "status" = ?, "updated_at" = ? WHERE "orders"."id" = ? [["status", 0], ["updated_at", "2019-01-24 07:13:08.524320"], ["id", 1]]
|
||||
# => true
|
||||
...
|
||||
```irb
|
||||
irb> order = Order.first
|
||||
irb> order.shipped!
|
||||
UPDATE "orders" SET "status" = ?, "updated_at" = ? WHERE "orders"."id" = ? [["status", 0], ["updated_at", "2019-01-24 07:13:08.524320"], ["id", 1]]
|
||||
=> true
|
||||
```
|
||||
|
||||
Full documentation about enums can be found [here](https://api.rubyonrails.org/classes/ActiveRecord/Enum.html).
|
||||
|
@ -1737,9 +1734,9 @@ The `find_or_create_by` method checks whether a record with the specified attrib
|
|||
|
||||
Suppose you want to find a customer named 'Andy', and if there's none, create one. You can do so by running:
|
||||
|
||||
```ruby
|
||||
Customer.find_or_create_by(first_name: 'Andy')
|
||||
# => #Customer id: 5, first_name: "Andy", last_name: nil, title: nil, visits: 0, orders_count: nil, lock_version: 0, created_at: "2019-01-17 07:06:45", updated_at: "2019-01-17 07:06:45"
|
||||
```irb
|
||||
irb> Customer.find_or_create_by(first_name: 'Andy')
|
||||
=> #<Customer id: 5, first_name: "Andy", last_name: nil, title: nil, visits: 0, orders_count: nil, lock_version: 0, created_at: "2019-01-17 07:06:45", updated_at: "2019-01-17 07:06:45">
|
||||
```
|
||||
|
||||
The SQL generated by this method looks like this:
|
||||
|
@ -1787,9 +1784,9 @@ validates :orders_count, presence: true
|
|||
|
||||
to your `Customer` model. If you try to create a new `Customer` without passing an `orders_count`, the record will be invalid and an exception will be raised:
|
||||
|
||||
```ruby
|
||||
Customer.find_or_create_by!(first_name: 'Andy')
|
||||
# => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank
|
||||
```irb
|
||||
irb> Customer.find_or_create_by!(first_name: 'Andy')
|
||||
ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank
|
||||
```
|
||||
|
||||
### `find_or_initialize_by`
|
||||
|
@ -1800,15 +1797,15 @@ means that a new model instance will be created in memory but won't be
|
|||
saved to the database. Continuing with the `find_or_create_by` example, we
|
||||
now want the customer named 'Nina':
|
||||
|
||||
```ruby
|
||||
nina = Customer.find_or_initialize_by(first_name: 'Nina')
|
||||
# => #<Customer id: nil, first_name: "Nina", orders_count: 0, locked: true, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
|
||||
```irb
|
||||
irb> nina = Customer.find_or_initialize_by(first_name: 'Nina')
|
||||
=> #<Customer id: nil, first_name: "Nina", orders_count: 0, locked: true, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
|
||||
|
||||
nina.persisted?
|
||||
# => false
|
||||
irb> nina.persisted?
|
||||
=> false
|
||||
|
||||
nina.new_record?
|
||||
# => true
|
||||
irb> nina.new_record?
|
||||
=> true
|
||||
```
|
||||
|
||||
Because the object is not yet stored in the database, the SQL generated looks like this:
|
||||
|
@ -1819,9 +1816,9 @@ SELECT * FROM customers WHERE (customers.first_name = 'Nina') LIMIT 1
|
|||
|
||||
When you want to save it to the database, just call `save`:
|
||||
|
||||
```ruby
|
||||
nina.save
|
||||
# => true
|
||||
```irb
|
||||
irb> nina.save
|
||||
=> true
|
||||
```
|
||||
|
||||
Finding by SQL
|
||||
|
@ -1829,15 +1826,9 @@ Finding by SQL
|
|||
|
||||
If you'd like to use your own SQL to find records in a table you can use `find_by_sql`. The `find_by_sql` method will return an array of objects even if the underlying query returns just a single record. For example you could run this query:
|
||||
|
||||
```ruby
|
||||
Customer.find_by_sql("SELECT * FROM customers
|
||||
INNER JOIN orders ON customers.id = orders.customer_id
|
||||
ORDER BY customers.created_at desc")
|
||||
# => [
|
||||
# #<Customer id: 1, first_name: "Lucas" ...>,
|
||||
# #<Customer id: 2, first_name: "Jan" ...>,
|
||||
# ...
|
||||
# ]
|
||||
```irb
|
||||
irb> Customer.find_by_sql("SELECT * FROM customers INNER JOIN orders ON customers.id = orders.customer_id ORDER BY customers.created_at desc")
|
||||
=> [#<Customer id: 1, first_name: "Lucas" ...>, #<Customer id: 2, first_name: "Jan" ...>, ...]
|
||||
```
|
||||
|
||||
`find_by_sql` provides you with a simple way of making custom calls to the database and retrieving instantiated objects.
|
||||
|
@ -1849,30 +1840,27 @@ objects from the database using custom SQL just like `find_by_sql` but will not
|
|||
This method will return an instance of `ActiveRecord::Result` class and calling `to_a` on this
|
||||
object would return you an array of hashes where each hash indicates a record.
|
||||
|
||||
```ruby
|
||||
Customer.connection.select_all("SELECT first_name, created_at FROM customers WHERE id = '1'").to_hash
|
||||
# => [
|
||||
# {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
|
||||
# {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
|
||||
# ]
|
||||
```irb
|
||||
irb> Customer.connection.select_all("SELECT first_name, created_at FROM customers WHERE id = '1'").to_hash
|
||||
=> [{"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"}, {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}]
|
||||
```
|
||||
|
||||
### `pluck`
|
||||
|
||||
`pluck` can be used to query single or multiple columns from the underlying table of a model. It accepts a list of column names as an argument and returns an array of values of the specified columns with the corresponding data type.
|
||||
|
||||
```ruby
|
||||
Book.where(out_of_print: true).pluck(:id)
|
||||
# SELECT id FROM books WHERE out_of_print = false
|
||||
# => [1, 2, 3]
|
||||
```irb
|
||||
irb> Book.where(out_of_print: true).pluck(:id)
|
||||
SELECT id FROM books WHERE out_of_print = false
|
||||
=> [1, 2, 3]
|
||||
|
||||
Order.distinct.pluck(:status)
|
||||
# SELECT DISTINCT status FROM orders
|
||||
# => ['shipped', 'being_packed', 'cancelled']
|
||||
irb> Order.distinct.pluck(:status)
|
||||
SELECT DISTINCT status FROM orders
|
||||
=> ["shipped", "being_packed", "cancelled"]
|
||||
|
||||
Customer.pluck(:id, :first_name)
|
||||
# SELECT customers.id, customers.name FROM customers
|
||||
# => [[1, 'David'], [2, 'Fran'], [3, 'Jose']]
|
||||
irb> Customer.pluck(:id, :first_name)
|
||||
SELECT customers.id, customers.name FROM customers
|
||||
=> [[1, "David"], [2, "Fran"], [3, "Jose"]]
|
||||
```
|
||||
|
||||
`pluck` makes it possible to replace code like:
|
||||
|
@ -1904,63 +1892,66 @@ class Customer < ApplicationRecord
|
|||
"I am #{first_name}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Customer.select(:first_name).map &:name
|
||||
# => ["I am David", "I am Jeremy", "I am Jose"]
|
||||
```irb
|
||||
irb> Customer.select(:first_name).map &:name
|
||||
=> ["I am David", "I am Jeremy", "I am Jose"]
|
||||
|
||||
Customer.pluck(:first_name)
|
||||
# => ["David", "Jeremy", "Jose"]
|
||||
irb> Customer.pluck(:first_name)
|
||||
=> ["David", "Jeremy", "Jose"]
|
||||
```
|
||||
|
||||
You are not limited to querying fields from a single table, you can query multiple tables as well.
|
||||
|
||||
```
|
||||
Order.joins(:customer, :books).pluck("orders.created_at, customers.email, books.title")
|
||||
```irb
|
||||
irb> Order.joins(:customer, :books).pluck("orders.created_at, customers.email, books.title")
|
||||
```
|
||||
|
||||
Furthermore, unlike `select` and other `Relation` scopes, `pluck` triggers an immediate
|
||||
query, and thus cannot be chained with any further scopes, although it can work with
|
||||
scopes already constructed earlier:
|
||||
|
||||
```ruby
|
||||
Customer.pluck(:first_name).limit(1)
|
||||
# => NoMethodError: undefined method `limit' for #<Array:0x007ff34d3ad6d8>
|
||||
```irb
|
||||
irb> Customer.pluck(:first_name).limit(1)
|
||||
NoMethodError: undefined method `limit' for #<Array:0x007ff34d3ad6d8>
|
||||
|
||||
Customer.limit(1).pluck(:first_name)
|
||||
# => ["David"]
|
||||
irb> Customer.limit(1).pluck(:first_name)
|
||||
=> ["David"]
|
||||
```
|
||||
|
||||
NOTE: You should also know that using `pluck` will trigger eager loading if the relation object contains include values, even if the eager loading is not necessary for the query. For example:
|
||||
|
||||
```ruby
|
||||
# store association for reusing it
|
||||
assoc = Customer.includes(:reviews)
|
||||
assoc.pluck(:id)
|
||||
# SELECT "customers"."id" FROM "customers" LEFT OUTER JOIN "reviews" ON "reviews"."id" = "customers"."review_id"
|
||||
```irb
|
||||
irb> assoc = Customer.includes(:reviews)
|
||||
irb> assoc.pluck(:id)
|
||||
SELECT "customers"."id" FROM "customers" LEFT OUTER JOIN "reviews" ON "reviews"."id" = "customers"."review_id"
|
||||
```
|
||||
|
||||
One way to avoid this is to `unscope` the includes:
|
||||
|
||||
```ruby
|
||||
assoc.unscope(:includes).pluck(:id)
|
||||
```irb
|
||||
irb> assoc.unscope(:includes).pluck(:id)
|
||||
```
|
||||
|
||||
### `ids`
|
||||
|
||||
`ids` can be used to pluck all the IDs for the relation using the table's primary key.
|
||||
|
||||
```ruby
|
||||
Customer.ids
|
||||
# SELECT id FROM customers
|
||||
```irb
|
||||
irb> Customer.ids
|
||||
SELECT id FROM customers
|
||||
```
|
||||
|
||||
```ruby
|
||||
class Customer < ApplicationRecord
|
||||
self.primary_key = "customer_id"
|
||||
end
|
||||
```
|
||||
|
||||
Customer.ids
|
||||
# SELECT customer_id FROM customers
|
||||
```irb
|
||||
irb> Customer.ids
|
||||
SELECT customer_id FROM customers
|
||||
```
|
||||
|
||||
Existence of Objects
|
||||
|
@ -2025,22 +2016,22 @@ This section uses `count` as an example method in this preamble, but the options
|
|||
|
||||
All calculation methods work directly on a model:
|
||||
|
||||
```ruby
|
||||
Customer.count
|
||||
# SELECT COUNT(*) FROM customers
|
||||
```irb
|
||||
irb> Customer.count
|
||||
SELECT COUNT(*) FROM customers
|
||||
```
|
||||
|
||||
Or on a relation:
|
||||
|
||||
```ruby
|
||||
Customer.where(first_name: 'Ryan').count
|
||||
# SELECT COUNT(*) FROM customers WHERE (first_name = 'Ryan')
|
||||
```irb
|
||||
irb> Customer.where(first_name: 'Ryan').count
|
||||
SELECT COUNT(*) FROM customers WHERE (first_name = 'Ryan')
|
||||
```
|
||||
|
||||
You can also use various finder methods on a relation for performing complex calculations:
|
||||
|
||||
```ruby
|
||||
Customer.includes("orders").where(first_name: 'Ryan', orders: { status: 'shipped' }).count
|
||||
```irb
|
||||
irb> Customer.includes("orders").where(first_name: 'Ryan', orders: { status: 'shipped' }).count
|
||||
```
|
||||
|
||||
Which will execute:
|
||||
|
|
|
@ -23,9 +23,13 @@ Here's an example of a very simple validation:
|
|||
class Person < ApplicationRecord
|
||||
validates :name, presence: true
|
||||
end
|
||||
```
|
||||
|
||||
Person.create(name: "John Doe").valid? # => true
|
||||
Person.create(name: nil).valid? # => false
|
||||
```irb
|
||||
irb> Person.create(name: "John Doe").valid?
|
||||
=> true
|
||||
irb> Person.create(name: nil).valid?
|
||||
=> false
|
||||
```
|
||||
|
||||
As you can see, our validation lets us know that our `Person` is not valid
|
||||
|
@ -85,17 +89,17 @@ end
|
|||
|
||||
We can see how it works by looking at some `bin/rails console` output:
|
||||
|
||||
```ruby
|
||||
>> p = Person.new(name: "John Doe")
|
||||
```irb
|
||||
irb> p = Person.new(name: "John Doe")
|
||||
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>
|
||||
|
||||
>> p.new_record?
|
||||
irb> p.new_record?
|
||||
=> true
|
||||
|
||||
>> p.save
|
||||
irb> p.save
|
||||
=> true
|
||||
|
||||
>> p.new_record?
|
||||
irb> p.new_record?
|
||||
=> false
|
||||
```
|
||||
|
||||
|
@ -168,9 +172,13 @@ As you saw above:
|
|||
class Person < ApplicationRecord
|
||||
validates :name, presence: true
|
||||
end
|
||||
```
|
||||
|
||||
Person.create(name: "John Doe").valid? # => true
|
||||
Person.create(name: nil).valid? # => false
|
||||
```irb
|
||||
irb> Person.create(name: "John Doe").valid?
|
||||
=> true
|
||||
irb> Person.create(name: nil).valid?
|
||||
=> false
|
||||
```
|
||||
|
||||
After Active Record has performed validations, any errors found can be accessed
|
||||
|
@ -182,34 +190,36 @@ Note that an object instantiated with `new` will not report errors
|
|||
even if it's technically invalid, because validations are automatically run
|
||||
only when the object is saved, such as with the `create` or `save` methods.
|
||||
|
||||
```ruby
|
||||
```
|
||||
class Person < ApplicationRecord
|
||||
validates :name, presence: true
|
||||
end
|
||||
```
|
||||
|
||||
>> p = Person.new
|
||||
# => #<Person id: nil, name: nil>
|
||||
>> p.errors.size
|
||||
# => 0
|
||||
```irb
|
||||
irb> p = Person.new
|
||||
=> #<Person id: nil, name: nil>
|
||||
irb> p.errors.size
|
||||
=> 0
|
||||
|
||||
>> p.valid?
|
||||
# => false
|
||||
>> p.errors.objects.first.full_message
|
||||
# => "Name can't be blank"
|
||||
irb> p.valid?
|
||||
=> false
|
||||
irb> p.errors.objects.first.full_message
|
||||
=> "Name can't be blank"
|
||||
|
||||
>> p = Person.create
|
||||
# => #<Person id: nil, name: nil>
|
||||
>> p.errors.objects.first.full_message
|
||||
# => "Name can't be blank"
|
||||
irb> p = Person.create
|
||||
=> #<Person id: nil, name: nil>
|
||||
irb> p.errors.objects.first.full_message
|
||||
=> "Name can't be blank"
|
||||
|
||||
>> p.save
|
||||
# => false
|
||||
irb> p.save
|
||||
=> false
|
||||
|
||||
>> p.save!
|
||||
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
||||
irb> p.save!
|
||||
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
||||
|
||||
>> Person.create!
|
||||
# => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
||||
irb> Person.create!
|
||||
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
||||
```
|
||||
|
||||
`invalid?` is the inverse of `valid?`. It triggers your validations,
|
||||
|
@ -232,9 +242,13 @@ whether there are errors found on an individual attribute of the object.
|
|||
class Person < ApplicationRecord
|
||||
validates :name, presence: true
|
||||
end
|
||||
```
|
||||
|
||||
>> Person.new.errors[:name].any? # => false
|
||||
>> Person.create.errors[:name].any? # => true
|
||||
```irb
|
||||
irb> Person.new.errors[:name].any?
|
||||
=> false
|
||||
irb> Person.create.errors[:name].any?
|
||||
=> true
|
||||
```
|
||||
|
||||
We'll cover validation errors in greater depth in the [Working with Validation
|
||||
|
@ -774,9 +788,13 @@ empty string for example.
|
|||
class Topic < ApplicationRecord
|
||||
validates :title, length: { is: 5 }, allow_blank: true
|
||||
end
|
||||
```
|
||||
|
||||
Topic.create(title: "").valid? # => true
|
||||
Topic.create(title: nil).valid? # => true
|
||||
```irb
|
||||
irb> Topic.create(title: "").valid?
|
||||
=> true
|
||||
irb> Topic.create(title: nil).valid?
|
||||
=> true
|
||||
```
|
||||
|
||||
### `:message`
|
||||
|
@ -847,12 +865,16 @@ class Person < ApplicationRecord
|
|||
validates :email, uniqueness: true, on: :account_setup
|
||||
validates :age, numericality: true, on: :account_setup
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new(age: 'thirty-three')
|
||||
person.valid? # => true
|
||||
person.valid?(:account_setup) # => false
|
||||
person.errors.messages
|
||||
# => {:email=>["has already been taken"], :age=>["is not a number"]}
|
||||
```irb
|
||||
irb> person = Person.new(age: 'thirty-three')
|
||||
irb> person.valid?
|
||||
=> true
|
||||
irb> person.valid?(:account_setup)
|
||||
=> false
|
||||
irb> person.errors.messages
|
||||
=> {:email=>["has already been taken"], :age=>["is not a number"]}
|
||||
```
|
||||
|
||||
`person.valid?(:account_setup)` executes both the validations without saving
|
||||
|
@ -868,11 +890,14 @@ class Person < ApplicationRecord
|
|||
validates :age, numericality: true, on: :account_setup
|
||||
validates :name, presence: true
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new
|
||||
person.valid?(:account_setup) # => false
|
||||
person.errors.messages
|
||||
# => {:email=>["has already been taken"], :age=>["is not a number"], :name=>["can't be blank"]}
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.valid?(:account_setup)
|
||||
=> false
|
||||
irb> person.errors.messages
|
||||
=> {:email=>["has already been taken"], :age=>["is not a number"], :name=>["can't be blank"]}
|
||||
```
|
||||
|
||||
Strict Validations
|
||||
|
@ -885,8 +910,11 @@ You can also specify validations to be strict and raise
|
|||
class Person < ApplicationRecord
|
||||
validates :name, presence: { strict: true }
|
||||
end
|
||||
```
|
||||
|
||||
Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank
|
||||
```irb
|
||||
irb> Person.new.valid?
|
||||
ActiveModel::StrictValidationFailed: Name can't be blank
|
||||
```
|
||||
|
||||
There is also the ability to pass a custom exception to the `:strict` option.
|
||||
|
@ -895,8 +923,11 @@ There is also the ability to pass a custom exception to the `:strict` option.
|
|||
class Person < ApplicationRecord
|
||||
validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
|
||||
end
|
||||
```
|
||||
|
||||
Person.new.valid? # => TokenGenerationException: Token can't be blank
|
||||
```irb
|
||||
irb> Person.new.valid?
|
||||
TokenGenerationException: Token can't be blank
|
||||
```
|
||||
|
||||
Conditional Validation
|
||||
|
@ -1099,15 +1130,20 @@ each error is represented by an `ActiveModel::Error` object.
|
|||
class Person < ApplicationRecord
|
||||
validates :name, presence: true, length: { minimum: 3 }
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new
|
||||
person.valid? # => false
|
||||
person.errors.full_messages
|
||||
# => ["Name can't be blank", "Name is too short (minimum is 3 characters)"]
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.valid?
|
||||
=> false
|
||||
irb> person.errors.full_messages
|
||||
=> ["Name can't be blank", "Name is too short (minimum is 3 characters)"]
|
||||
|
||||
person = Person.new(name: "John Doe")
|
||||
person.valid? # => true
|
||||
person.errors.full_messages # => []
|
||||
irb> person = Person.new(name: "John Doe")
|
||||
irb> person.valid?
|
||||
=> true
|
||||
irb> person.errors.full_messages
|
||||
=> []
|
||||
```
|
||||
|
||||
### `errors[]`
|
||||
|
@ -1118,19 +1154,26 @@ person.errors.full_messages # => []
|
|||
class Person < ApplicationRecord
|
||||
validates :name, presence: true, length: { minimum: 3 }
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new(name: "John Doe")
|
||||
person.valid? # => true
|
||||
person.errors[:name] # => []
|
||||
```irb>
|
||||
irb> person = Person.new(name: "John Doe")
|
||||
irb> person.valid?
|
||||
=> true
|
||||
irb> person.errors[:name]
|
||||
=> []
|
||||
|
||||
person = Person.new(name: "JD")
|
||||
person.valid? # => false
|
||||
person.errors[:name] # => ["is too short (minimum is 3 characters)"]
|
||||
irb> person = Person.new(name: "JD")
|
||||
irb> person.valid?
|
||||
=> false
|
||||
irb> person.errors[:name]
|
||||
=> ["is too short (minimum is 3 characters)"]
|
||||
|
||||
person = Person.new
|
||||
person.valid? # => false
|
||||
person.errors[:name]
|
||||
# => ["can't be blank", "is too short (minimum is 3 characters)"]
|
||||
irb> person = Person.new
|
||||
irb> person.valid?
|
||||
=> false
|
||||
irb> person.errors[:name]
|
||||
=> ["can't be blank", "is too short (minimum is 3 characters)"]
|
||||
```
|
||||
|
||||
### `errors.where` and error object
|
||||
|
@ -1143,27 +1186,41 @@ Sometimes we may need more information about each error beside its message. Each
|
|||
class Person < ApplicationRecord
|
||||
validates :name, presence: true, length: { minimum: 3 }
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new
|
||||
person.valid? # => false
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.valid?
|
||||
=> false
|
||||
|
||||
>> person.errors.where(:name) # errors linked to :name attribute
|
||||
>> person.errors.where(:name, :too_short) # further filtered to only :too_short type error
|
||||
irb> person.errors.where(:name)
|
||||
=> [ ... ] # all errors for :name attribute
|
||||
|
||||
irb> person.errors.where(:name, :too_short)
|
||||
=> [ ... ] # :too_short errors for :name attribute
|
||||
```
|
||||
|
||||
You can read various information from these error objects:
|
||||
|
||||
```ruby
|
||||
>> error = person.errors.where(:name).last
|
||||
>> error.attribute # => :name
|
||||
>> error.type # => :too_short
|
||||
>> error.options[:count] # => 3
|
||||
```irb
|
||||
irb> error = person.errors.where(:name).last
|
||||
|
||||
irb> error.attribute
|
||||
=> :name
|
||||
irb> error.type
|
||||
=> :too_short
|
||||
irb> error.options[:count]
|
||||
=> 3
|
||||
```
|
||||
|
||||
You can also generate the error message:
|
||||
|
||||
>> error.message # => "is too short (minimum is 3 characters)"
|
||||
>> error.full_message # => "Name is too short (minimum is 3 characters)"
|
||||
```irb
|
||||
irb> error.message
|
||||
=> "is too short (minimum is 3 characters)"
|
||||
irb> error.full_message
|
||||
=> "Name is too short (minimum is 3 characters)"
|
||||
```
|
||||
|
||||
The `full_message` method generates a more user-friendly message, with the capitalized attribute name prepended.
|
||||
|
||||
|
@ -1177,10 +1234,14 @@ class Person < ApplicationRecord
|
|||
errors.add :name, :too_plain, message: "is not cool enough"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.create
|
||||
person.errors.where(:name).first.type # => :too_plain
|
||||
person.errors.where(:name).first.full_message # => "Name is not cool enough"
|
||||
```irb
|
||||
irb> person = Person.create
|
||||
irb> person.errors.where(:name).first.type
|
||||
=> :too_plain
|
||||
irb> person.errors.where(:name).first.full_message
|
||||
=> "Name is not cool enough"
|
||||
```
|
||||
|
||||
### `errors[:base]`
|
||||
|
@ -1193,9 +1254,12 @@ class Person < ApplicationRecord
|
|||
errors.add :base, :invalid, message: "This person is invalid because ..."
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.create
|
||||
person.errors.where(:base).first.full_message # => "This person is invalid because ..."
|
||||
```irb
|
||||
irb> person = Person.create
|
||||
irb> person.errors.where(:base).first.full_message
|
||||
=> "This person is invalid because ..."
|
||||
```
|
||||
|
||||
### `errors.clear`
|
||||
|
@ -1206,17 +1270,24 @@ The `clear` method is used when you intentionally want to clear the `errors` col
|
|||
class Person < ApplicationRecord
|
||||
validates :name, presence: true, length: { minimum: 3 }
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new
|
||||
person.valid? # => false
|
||||
person.errors.empty? # => false
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.valid?
|
||||
=> false
|
||||
irb> person.errors.empty?
|
||||
=> false
|
||||
|
||||
person.errors.clear
|
||||
person.errors.empty? # => true
|
||||
irb> person.errors.clear
|
||||
irb> person.errors.empty?
|
||||
=> true
|
||||
|
||||
person.save # => false
|
||||
irb> person.save
|
||||
=> false
|
||||
|
||||
person.errors.empty? # => false
|
||||
irb> person.errors.empty?
|
||||
=> false
|
||||
```
|
||||
|
||||
### `errors.size`
|
||||
|
@ -1227,14 +1298,20 @@ The `size` method returns the total number of errors for the object.
|
|||
class Person < ApplicationRecord
|
||||
validates :name, presence: true, length: { minimum: 3 }
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.new
|
||||
person.valid? # => false
|
||||
person.errors.size # => 2
|
||||
```irb
|
||||
irb> person = Person.new
|
||||
irb> person.valid?
|
||||
=> false
|
||||
irb> person.errors.size
|
||||
=> 2
|
||||
|
||||
person = Person.new(name: "Andrea", email: "andrea@example.com")
|
||||
person.valid? # => true
|
||||
person.errors.size # => 0
|
||||
irb> person = Person.new(name: "Andrea", email: "andrea@example.com")
|
||||
irb> person.valid?
|
||||
=> true
|
||||
irb> person.errors.size
|
||||
=> 0
|
||||
```
|
||||
|
||||
Displaying Validation Errors in Views
|
||||
|
|
|
@ -414,9 +414,8 @@ more information regarding this).
|
|||
Other plugins may add additional modules. You can get a list of all modules
|
||||
included into `ActionController::API` in the rails console:
|
||||
|
||||
```bash
|
||||
$ bin/rails c
|
||||
>> ActionController::API.ancestors - ActionController::Metal.ancestors
|
||||
```irb
|
||||
irb> ActionController::API.ancestors - ActionController::Metal.ancestors
|
||||
=> [ActionController::API,
|
||||
ActiveRecord::Railties::ControllerRuntime,
|
||||
ActionDispatch::Routing::RouteSet::MountedHelpers,
|
||||
|
|
|
@ -749,12 +749,14 @@ end
|
|||
|
||||
Active Record will attempt to automatically identify that these two models share a bi-directional association based on the association name. In this way, Active Record will only load one copy of the `Author` object, making your application more efficient and preventing inconsistent data:
|
||||
|
||||
```ruby
|
||||
a = Author.first
|
||||
b = a.books.first
|
||||
a.first_name == b.author.first_name # => true
|
||||
a.first_name = 'David'
|
||||
a.first_name == b.author.first_name # => true
|
||||
```irb
|
||||
irb> a = Author.first
|
||||
irb> b = a.books.first
|
||||
irb> a.first_name == b.author.first_name
|
||||
=> true
|
||||
irb> a.first_name = 'David'
|
||||
irb> a.first_name == b.author.first_name
|
||||
=> true
|
||||
```
|
||||
|
||||
Active Record supports automatic identification for most associations with standard names. However, Active Record will not automatically identify bi-directional associations that contain a scope or any of the following options:
|
||||
|
@ -776,12 +778,14 @@ end
|
|||
|
||||
Active Record will no longer automatically recognize the bi-directional association:
|
||||
|
||||
```ruby
|
||||
a = Author.first
|
||||
b = a.books.first
|
||||
a.first_name == b.writer.first_name # => true
|
||||
a.first_name = 'David'
|
||||
a.first_name == b.writer.first_name # => false
|
||||
```irb
|
||||
irb> a = Author.first
|
||||
irb> b = a.books.first
|
||||
irb> a.first_name == b.writer.first_name
|
||||
=> true
|
||||
irb> a.first_name = 'David'
|
||||
irb> a.first_name == b.writer.first_name
|
||||
=> false
|
||||
```
|
||||
|
||||
Active Record provides the `:inverse_of` option so you can explicitly declare bi-directional associations:
|
||||
|
@ -798,12 +802,14 @@ end
|
|||
|
||||
By including the `:inverse_of` option in the `has_many` association declaration, Active Record will now recognize the bi-directional association:
|
||||
|
||||
```ruby
|
||||
a = Author.first
|
||||
b = a.books.first
|
||||
a.first_name == b.writer.first_name # => true
|
||||
a.first_name = 'David'
|
||||
a.first_name == b.writer.first_name # => true
|
||||
```irb
|
||||
irb> a = Author.first
|
||||
irb> b = a.books.first
|
||||
irb> a.first_name == b.writer.first_name
|
||||
=> true
|
||||
irb> a.first_name = 'David'
|
||||
irb> a.first_name == b.writer.first_name
|
||||
=> true
|
||||
```
|
||||
|
||||
Detailed Association Reference
|
||||
|
@ -1952,13 +1958,17 @@ class Person < ApplicationRecord
|
|||
has_many :readings
|
||||
has_many :articles, through: :readings
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.create(name: 'John')
|
||||
article = Article.create(name: 'a1')
|
||||
person.articles << article
|
||||
person.articles << article
|
||||
person.articles.inspect # => [#<Article id: 5, name: "a1">, #<Article id: 5, name: "a1">]
|
||||
Reading.all.inspect # => [#<Reading id: 12, person_id: 5, article_id: 5>, #<Reading id: 13, person_id: 5, article_id: 5>]
|
||||
```irb
|
||||
irb> person = Person.create(name: 'John')
|
||||
irb> article = Article.create(name: 'a1')
|
||||
irb> person.articles << article
|
||||
irb> person.articles << article
|
||||
irb> person.articles.to_a
|
||||
=> [#<Article id: 5, name: "a1">, #<Article id: 5, name: "a1">]
|
||||
irb> Reading.all.to_a
|
||||
=> [#<Reading id: 12, person_id: 5, article_id: 5>, #<Reading id: 13, person_id: 5, article_id: 5>]
|
||||
```
|
||||
|
||||
In the above case there are two readings and `person.articles` brings out both of
|
||||
|
@ -1971,13 +1981,17 @@ class Person
|
|||
has_many :readings
|
||||
has_many :articles, -> { distinct }, through: :readings
|
||||
end
|
||||
```
|
||||
|
||||
person = Person.create(name: 'Honda')
|
||||
article = Article.create(name: 'a1')
|
||||
person.articles << article
|
||||
person.articles << article
|
||||
person.articles.inspect # => [#<Article id: 7, name: "a1">]
|
||||
Reading.all.inspect # => [#<Reading id: 16, person_id: 7, article_id: 7>, #<Reading id: 17, person_id: 7, article_id: 7>]
|
||||
```irb
|
||||
irb> person = Person.create(name: 'Honda')
|
||||
irb> article = Article.create(name: 'a1')
|
||||
irb> person.articles << article
|
||||
irb> person.articles << article
|
||||
irb> person.articles.to_a
|
||||
=> [#<Article id: 7, name: "a1">]
|
||||
irb> Reading.all.to_a
|
||||
=> [#<Reading id: 16, person_id: 7, article_id: 7>, #<Reading id: 17, person_id: 7, article_id: 7>]
|
||||
```
|
||||
|
||||
In the above case there are still two readings. However `person.articles` shows
|
||||
|
@ -1997,11 +2011,12 @@ add_index :readings, [:person_id, :article_id], unique: true
|
|||
Once you have this unique index, attempting to add the article to a person twice
|
||||
will raise an `ActiveRecord::RecordNotUnique` error:
|
||||
|
||||
```ruby
|
||||
person = Person.create(name: 'Honda')
|
||||
article = Article.create(name: 'a1')
|
||||
person.articles << article
|
||||
person.articles << article # => ActiveRecord::RecordNotUnique
|
||||
```irb
|
||||
irb> person = Person.create(name: 'Honda')
|
||||
irb> article = Article.create(name: 'a1')
|
||||
irb> person.articles << article
|
||||
irb> person.articles << article
|
||||
ActiveRecord::RecordNotUnique
|
||||
```
|
||||
|
||||
Note that checking for uniqueness using something like `include?` is subject
|
||||
|
|
|
@ -133,9 +133,7 @@ In a Rails console there is no file watcher active regardless of the value of `c
|
|||
|
||||
However, you can force a reload in the console by executing `reload!`:
|
||||
|
||||
```bash
|
||||
$ bin/rails c
|
||||
Loading development environment (Rails 6.0.0)
|
||||
```irb
|
||||
irb(main):001:0> User.object_id
|
||||
=> 70136277390120
|
||||
irb(main):002:0> reload!
|
||||
|
@ -172,12 +170,12 @@ Let's see other situations that involve stale class or module objects.
|
|||
|
||||
Check this Rails console session:
|
||||
|
||||
```ruby
|
||||
> joe = User.new
|
||||
> reload!
|
||||
> alice = User.new
|
||||
> joe.class == alice.class
|
||||
false
|
||||
```irb
|
||||
irb> joe = User.new
|
||||
irb> reload!
|
||||
irb> alice = User.new
|
||||
irb> joe.class == alice.class
|
||||
=> false
|
||||
```
|
||||
|
||||
`joe` is an instance of the original `User` class. When there is a reload, the `User` constant evaluates to a different, reloaded class. `alice` is an instance of the current one, but `joe` is not, his class is stale. You may define `joe` again, start an IRB subsession, or just launch a new console instead of calling `reload!`.
|
||||
|
|
|
@ -822,8 +822,8 @@ constants.
|
|||
For example, if you're in a console session and edit some file behind the
|
||||
scenes, the code can be reloaded with the `reload!` command:
|
||||
|
||||
```
|
||||
> reload!
|
||||
```irb
|
||||
irb> reload!
|
||||
```
|
||||
|
||||
When the application runs, code is reloaded when something relevant to this
|
||||
|
@ -1248,10 +1248,10 @@ warning: toplevel constant Image referenced by Hotel::Image
|
|||
|
||||
This surprising constant resolution can be observed with any qualifying class:
|
||||
|
||||
```
|
||||
2.1.5 :001 > String::Array
|
||||
```irb
|
||||
irb(main):001:0> String::Array
|
||||
(irb):1: warning: toplevel constant Array referenced by String::Array
|
||||
=> Array
|
||||
=> Array
|
||||
```
|
||||
|
||||
WARNING. To find this gotcha the qualifying namespace has to be a class,
|
||||
|
|
|
@ -368,22 +368,22 @@ Inside the `bin/rails console` you have access to the `app` and `helper` instanc
|
|||
|
||||
With the `app` method you can access named route helpers, as well as do requests.
|
||||
|
||||
```ruby
|
||||
>> app.root_path
|
||||
```irb
|
||||
irb> app.root_path
|
||||
=> "/"
|
||||
|
||||
>> app.get _
|
||||
irb> app.get _
|
||||
Started GET "/" for 127.0.0.1 at 2014-06-19 10:41:57 -0300
|
||||
...
|
||||
```
|
||||
|
||||
With the `helper` method it is possible to access Rails and your application's helpers.
|
||||
|
||||
```ruby
|
||||
>> helper.time_ago_in_words 30.days.ago
|
||||
```irb
|
||||
irb> helper.time_ago_in_words 30.days.ago
|
||||
=> "about 1 month"
|
||||
|
||||
>> helper.my_custom_helper
|
||||
irb> helper.my_custom_helper
|
||||
=> "my custom helper"
|
||||
```
|
||||
|
||||
|
|
|
@ -1077,8 +1077,7 @@ development:
|
|||
This will connect to the database named `blog_development` using the `postgresql` adapter. This same information can be stored in a URL and provided via an environment variable like this:
|
||||
|
||||
```ruby
|
||||
> puts ENV['DATABASE_URL']
|
||||
postgresql://localhost/blog_development?pool=5
|
||||
ENV['DATABASE_URL'] # => "postgresql://localhost/blog_development?pool=5"
|
||||
```
|
||||
|
||||
The `config/database.yml` file contains sections for three different environments in which Rails can run by default:
|
||||
|
|
|
@ -439,8 +439,8 @@ If you'd rather play around in the console, `bin/rails console` will also work j
|
|||
like a Rails application. Remember: the `Article` model is namespaced, so to
|
||||
reference it you must call it as `Blorgh::Article`.
|
||||
|
||||
```ruby
|
||||
>> Blorgh::Article.find(1)
|
||||
```irb
|
||||
irb> Blorgh::Article.find(1)
|
||||
=> #<Blorgh::Article id: 1 ...>
|
||||
```
|
||||
|
||||
|
|
|
@ -137,8 +137,8 @@ To test that your method does what it says it does, run the unit tests with `bin
|
|||
|
||||
To see this in action, change to the `test/dummy` directory, start `bin/rails console`, and commence squawking:
|
||||
|
||||
```ruby
|
||||
>> "Hello World".to_squawk
|
||||
```irb
|
||||
irb> "Hello World".to_squawk
|
||||
=> "squawk! Hello World"
|
||||
```
|
||||
|
||||
|
|
|
@ -1357,9 +1357,13 @@ class FooBar
|
|||
{ foo: 'bar' }
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
>> FooBar.new.to_json # => "{\"foo\":\"bar\"}"
|
||||
>> JSON.generate(FooBar.new, quirks_mode: true) # => "\"#<FooBar:0x007fa80a481610>\""
|
||||
```irb
|
||||
irb> FooBar.new.to_json
|
||||
=> "{\"foo\":\"bar\"}"
|
||||
irb> JSON.generate(FooBar.new, quirks_mode: true)
|
||||
=> "\"#<FooBar:0x007fa80a481610>\""
|
||||
```
|
||||
|
||||
#### New JSON encoder
|
||||
|
|
Loading…
Reference in New Issue