Rails 5 Retrieving info of failed validations

Abhishek Jain

By Abhishek Jain

on May 3, 2016

This blog is part of our  Rails 5 series.

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

1class User < ActiveRecord::Base
2  validates :email, presence: true
3end
4
5>> user = User.new
6>> user.valid?
7=> false
8
9>> user.errors.messages
10=> {:email=>["can't be blank"]}
11

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.

1
2class User < ApplicationRecord
3  validates :email, presence: true
4end
5
6>> user = User.new
7>> user.valid?
8=> false
9
10>> user.errors.details
11=> {:email=>[{:error=>:blank}]}
12

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

1
2# Custom validator type
3>> user = User.new
4>> user.errors.add(:name, :not_valid, message: "The name appears invalid")
5
6>> user.errors.details
7=> {:name=>[{:error=>:not_valid}]}
8
9# Custom error with default validator type :invalid
10
11>> user = User.new
12>> user.errors.add(:name)
13
14>> user.errors.details
15=> {:name=>[{:error=>:invalid}]}
16
17# More than one error on one attribute
18
19>> user = User.new
20>> user.errors.add(:password, :invalid_format, message: "Password must start with an alphabet")
21>> user.errors.add(:password, :invalid_length, message: "Password must have at least 8 characters")
22
23>> user.errors.details
24=> {:password=>[{:error=>:invalid_format}, {:error=>:invalid_length}]}
25

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.

1
2class User < ApplicationRecord
3  validate :password_cannot_have_invalid_character
4
5  def password_cannot_have_invalid_character
6    if password.scan("!").present?
7      errors.add(:password, :invalid_character, not_allowed: "!")
8    end
9  end
10end
11
12>> user = User.create(name: 'Mark', password: 'Ra!ls')
13>> user.errors.details
14=> {:password=>[{:error=>:invalid_character, :not_allowed=>"!"}]}
15

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

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.