Rails 5 Retrieving info of failed validations

Abhishek Jain

Abhishek Jain

May 3, 2016

This blog is part of our  Rails 5 series.

Let's look at a validation example in Rails 4.x.

class User < ActiveRecord::Base
  validates :email, presence: true
end

>> user = User.new
>> user.valid?
=> false

>> user.errors.messages
=> {:email=>["can't be blank"]}

In this case, we do not get any information about the type of failed validation as ActiveModel#Errors only gives the attribute name and the translated error message.

This works out well for normal apps. But in case of API only applications, sometimes we want to allow the client consuming the API to generate customized error message as per their needs. We don't want to send the final translated messages in such cases. Instead if we could just send details that presence validation failed for :name attribute, the client app would be able to customize the error message based on that information.

In Rails 5, it is now possible to get such details about which validations failed for a given attribute.

We can check this by calling details method on the ActiveModel#Errors instance.


class User < ApplicationRecord
  validates :email, presence: true
end

>> user = User.new
>> user.valid?
=> false

>> user.errors.details
=> {:email=>[{:error=>:blank}]}

We can also add custom validator types as per our need.


# Custom validator type
>> user = User.new
>> user.errors.add(:name, :not_valid, message: "The name appears invalid")

>> user.errors.details
=> {:name=>[{:error=>:not_valid}]}

# Custom error with default validator type :invalid

>> user = User.new
>> user.errors.add(:name)

>> user.errors.details
=> {:name=>[{:error=>:invalid}]}

# More than one error on one attribute

>> user = User.new
>> user.errors.add(:password, :invalid_format, message: "Password must start with an alphabet")
>> user.errors.add(:password, :invalid_length, message: "Password must have at least 8 characters")

>> user.errors.details
=> {:password=>[{:error=>:invalid_format}, {:error=>:invalid_length}]}

Passing contextual information about the errors

We can also send contextual data for the validation to the Errors#add method. This data can be later accessed via Errors#details method because Errors#add method forwards all options except :message, :if, :unless, and :on to details.

For eg. we can say that the password is invalid because ! is not allowed, as follows.


class User < ApplicationRecord
  validate :password_cannot_have_invalid_character

  def password_cannot_have_invalid_character
    if password.scan("!").present?
      errors.add(:password, :invalid_character, not_allowed: "!")
    end
  end
end

>> user = User.create(name: 'Mark', password: 'Ra!ls')
>> user.errors.details
=> {:password=>[{:error=>:invalid_character, :not_allowed=>"!"}]}

We can also use this feature in our Rails 4.x apps by simply installing gem active_model-errors_details.

If this blog was helpful, check out our full blog archive.

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.