Rails modified the way errors
are represented
when a model save
, create
or update
action fails.
Before
Let’s say we have a User
model, with columns like first_name
, last_name
, contact_number
,
email
and all of them are mandatory. If we try to create a User
object without passing first_name
and contact_number
as a string
, the #errors
function will show the errors as below
class User < ApplicationRecord
validates :contact_number,
presence: true,
numericality: true,
length: { :minimum => 10, :maximum => 15 }
end
user = User.create(email: "sam@example.com", last_name: "Example", contact_number: "abcdefghijk")
user.errors
=> #<ActiveModel::Errors:0x00007fe42c1650b8
@base=
#<User:0x00007fe42c1676d8
....
....
@details={:first_name=>[{:error=>:blank}], :contact_number=>[{:error=>:not_a_number}]}
@messages=
{:first_name=>["First Name is required."],
:contact_number=>["Contact number is not a number."]
}>
The error message for a particular field can be accessed using the []
method as below
user.errors[:first_name]
=> ["First Name is required."]
We could also use #messages
or #full_messages
to see the list of all errors.
user.errors.messages
{
:first_name=>["First Name is required."],
:contact_number=>["Contact number is not a number."]
}
user.errors.full_messages
[
"First name is required.",
"Contact number is not a number."
]
Accessing errors in the above manner is not object oriented. If a particular field has multiple errors associated, it has to be accessed using array indexes.
user = User.create(email: "sam@example.com", last_name: "Example")
user.errors[:contact_number]
=> ["Contact Name is required.", "Contact Name is not a number."]
user.errors[:contact_number][0]
=> "Contact Name is required."
user.errors[:contact_number][1]
=> "Contact Name is not a number."
After
With recent changes in ActiveModel#errors
class the above errors will appear as object of Error class
instead of hash.
user = User.create(email: "sam@example.com", last_name: "Example", contact_number: "abcdefghijk")
user.errors
=> #<ActiveModel::Errors:0x00007ff5ba2be5a0 @base=#<User id: nil, first_name: nil, last_name: "Example", email: "sam@example.com", contact_number: "abcdefghijk", created_at: nil, updated_at: nil>,
@errors=[<#ActiveModel::Error attribute=first_name, type=blank, options={}>, <#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>"abcdefghijk"}>]>
We can now use where
clause to fetch the error(s) related to a particular attribute.
user.errors.where(:contact_number)
=> [<#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>"abcdefghijk}>]
We have methods like add
, added?
, delete
, match?
which have similar signature to
where
=> (attribute, type, options).
user.errors.add(:contact_number, :too_short, count: 10)
=> <#ActiveModel::Error attribute=contact_number, type=too_short, options={:count=>10}>
user.errors.where(:contact_number)
=> [<#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>nil}>, <#ActiveModel::Error
attribute=contact_number, type=too_short, options={:count=>10}>]
user.errors.added?(:contact_number, :too_short, count: 10)
=> true
user.errors.added?(:contact_number, :too_short)
=> false
user.errors.delete(:contact_number, :too_short, count: 10)
user.errors.where(:contact_number)
=> [<#ActiveModel::Error attribute=contact_number, type=not_a_number, options={:value=>nil}>]
user.errors.match?(:contact_number, :not_a_number)
=> true
user.errors.match?(:contact_number, :too_long)
=> false
As seen above, we can verify presence of a specific error in a given attribute by using added?
method.
With the new changes added, message
or full_message
method can be accessed using where
clause.
user.errors.where(:first_name, :blank).last.message
=> "can't be blank"
user.errors.where(:contact_number, :not_a_number).last.full_message
=> "Contact number is not a number"
NOTE
Below methods are deprecated which will start showing deprecation warnings if used -
[]
each{|attr, msgs|}
generate_message
has_key?
key?
keys
values
user.errors.keys
DEPRECATION WARNING: ActiveModel::Errors#keys is deprecated and will be removed in Rails 6.2.
To achieve the same use:
errors.attribute_names
Below methods have not been changed -
as_json
,blank?
,clear
,count
,empty?
add
added?
full_messages
full_messages_for
include?
,size
,to_hash
,to_xml
,to_a
messages
details