Read more

Rails: How to check if a certain validation failed

Arne Hartherz
August 06, 2018Software engineer at makandra GmbH

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.

Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

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
August 06, 2018Software engineer at makandra GmbH
Posted by Arne Hartherz to makandra dev (2018-08-06 14:09)