If validations failed for a record, and you want to find out if a specific validation failed, you can leverage ActiveModel's error objects.
You rarely need this in application code (you usually just want to print error messages), but it can be useful when writing tests.
As an example, consider the following model which uses two validations on the email
attribute.
class User < ApplicationRecord
validates :email, presence: true, uniqueness: true
end
Accessing errors
Let's assume we have a blank user:
user = User.new
user.valid? # => false
The record's errors
then contains all validation errors.
>> user.errors
=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=email, type=blank, options={}>]>
You can access errors[:email]
to get the error message itself ("can't be blank"
).
However, your code may be more robust and readable if you check for the error type itself.
You can use the
added?
Show archive.org snapshot
or
of_kind?
Show archive.org snapshot
methods for that.
>> user.errors.added?(:email, :blank)
=> true
>> user.errors.of_kind?(:email, :blank)
=> true
Note how error types don't necessarily match the validation name (e.g. :blank
for :presence
, :taken
for :uniqueness
).
You may also use where
to see all errors of an attribute:
>> user.errors.where(:email)
=> [#<ActiveModel::Error attribute=email, type=blank, options={}>]
Difference between added?
and of_kind?
While added?
might feel more natural to use, it behaves differently than of_kind?
because it also compares options from the validation error. of_kind?
does not do that.
This is is relevant for validations that provide extra context, like length or uniqueness validations.
Consider a User
that can not be saved because there already is a record with the same email address.
user = User.new(email: "user@example.com")
user.valid? # => false
>> user.errors
=> #<ActiveModel::Errors [#<ActiveModel::Error attribute=email, type=taken, options={:value=>"user@example.com"}>]>
If you do not care about the options, use of_kind?
:
>> user.errors.of_kind?(:email, :taken)
=> true
Using added?
without specifying options will return false:
>> user.errors.added?(:email, :taken)
=> false
>> user.errors.added?(:email, :taken, value: "user@example.com")
=> true
Most often, you probably want to use of_kind?
.