Rails: How to check if a certain validation failed

Updated . Posted . Visible to the public.

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?.

Arne Hartherz
Last edit
Arne Hartherz
Keywords
ActiveRecord, ActiveModel, attributes, fields, errors, present
License
Source code in this card is licensed under the MIT License.
Posted by Arne Hartherz to makandra dev (2018-08-06 12:09)