Better Rails Active Record Validation Messages
Tags: rails, activerecord, validations, errormessages
Published: 2018-07-17 22:07:06

---
title: Better Rails Active Record Validation Messages
published: true
description: Rails Active Record Validations with lambdas and localisation
tags: #rails, #active_record, #validations, #error_messages
cover_image: https://thepracticaldev.s3.amazonaws.com/i/3z470kpbpa0qmq6t6e13.png
---

Validations are used to ensure that only valid data is saved to database and no business rules are violated. For example, it may be important for business to ensure that users provides a valid email address and phone number. Model-level validations are the most common way (db-level constraints is another option) to achieve this. Model-level validations are database agnostic, simple to test and maintain. Rails makes this more fun with built-in helpers and support for custom validations.

All that being done, the next crucial step is to provide users with immediate feedback as they use your site. Rails allows to do just that with the `:message` option. It specifies the customised error message that will be added to the errors collection when the validation fails.

The `:message` option accepts a `string` or a `proc`.
- When using strings, rails provides placeholder/interpolation variables to help add context and reduce cognitive load.
    - %{value}
    - %{attribute}
    - %{model}
Use them in the message string as, 

```
validates :age, numericality: { message: "%{attribute} must be a number" }
```

`Age must be a number` feels much better than `must be a number`.


- Using procs adds more flexibility and logic to the message string. Proc has access to the object being validated and a data hash with keys `model`, `attribute` and `value`, same as the interpolation variable above. 

```
validates :username,
    uniqueness: {
      message: ->(object, data) do
        "Hey #{object.name}!, #{data[:value]} is taken already!"
      end
    }
```

```
validates :score,
    presence: {
      message: ->(object, data) do
        if object.member?
          "something"
        else
          "Something else"
        end    
      end
    }
```


Magic does not end here, rails also allows overriding default validation messages via i18n-localization files.

```
en:
  activerecord:
    errors:
      models:
        user:
          attributes:
            firstname:
              blank: "custom message, can\'t be blank"

```

![Presence validation error on firstname](https://thepracticaldev.s3.amazonaws.com/i/3z470kpbpa0qmq6t6e13.png)
Active Record provides couple of namespaces where message translations can be placed in order to provide different messages and translation for certain models, attributes, and/or validations. It also transparently takes single table inheritance into account.

Also placeholder/interpolation variables are still available.
```
en:
  activerecord:
    errors:
      models:
        user:
          attributes:
            firstname:
              blank: "Please fill in your %{attribute}"
```
[http://guides.rubyonrails.org](http://guides.rubyonrails.org/i18n.html#translations-for-active-record-models)


Further, ActiveModel::Errors#full_messages prepends the attribute name to the error message using a separator that is looked up from `errors.format` (and defaults to `%{attribute} %{message}`). This too can be customised as

```
en:
  errors:
    # format to use in full error messages.
    format: "%{message} %{attribute}"
```