We write about Ruby on Rails, React.js, React Native, remote work, open source, engineering and design.
Rails 6.0 was recently released.
Before Rails 6,
the default format %{attribute} %{message}
is used to display validation error message
for a model's attribute.
1
2> > article = Article.new
3> > => #<Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil>
4> > article.errors.full_message(:title, "cannot be blank")
5> > => "Title cannot be blank"
6> >
The default format can be overridden globally using a language-specific locale file.
1
2# config/locales/en.yml
3
4en:
5errors:
6format:
7"'%{attribute}' %{message}"
With this change, the full error message is changed for all the attributes of all models.
1
2> > article = Article.new
3> > => #<Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil>
4> > article.errors.full_message(:title, "cannot be blank")
5> > => "'Title' cannot be blank"
6
7> > user = User.new
8> > => #<User id: nil, first_name: nil, last_name: nil, country: nil, created_at: nil, updated_at: nil>
9> > user.errors.full_message(:first_name, "cannot be blank")
10> > => "'First name' cannot be blank"
11> >
This trick works in some cases but it doesn't work if we have to customize the error messages on the basis of specific models or attributes.
Before Rails 6, there is no easy way to generate error messages like shown below.
1The article's title cannot be empty
or
1First name of a person cannot be blank
If we change the
errors.format
to
The article's %{attribute} %{message}
in
config/locales/en.yml
then that format
will be unexpectedaly used for other models, too.
1
2# config/locales/en.yml
3
4en:
5errors:
6format:
7"The article's %{attribute} %{message}"
This is what will happen if we make such a change.
1
2> > article = Article.new
3> > => #<Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil>
4> > article.errors.full_message(:title, "cannot be empty")
5> > => "The article's Title cannot be empty"
6
7> > user = User.new
8> > => #<User id: nil, first_name: nil, last_name: nil, country: nil, created_at: nil, updated_at: nil>
9> > user.errors.full_message(:first_name, "cannot be blank")
10> > => "The article's First name cannot be blank"
11> >
Notice the error message generated
for the :first_name
attribute
of User model.
This does not look the way we want, right?
Let's see what is changed in Rails 6 to overcome this problem.
ActiveModel::Errors#full_message
in Rails 6Overriding the format of error message
globally
using errors.format
is still supported in Rails 6.
In addition to that, Rails 6 now also supports overriding the error message's format at the model level and at the attribute level.
In order to enable this support,
we need to explicitly set
config.active_model.i18n_customize_full_message
to
true
in the Rails configuration file,
preferably in config/application.rb
which is implicitly set to false
by default.
We can customize the full error message format for each model separately.
1
2# config/locales/en.yml
3
4en:
5activerecord:
6errors:
7models:
8article:
9format: "`%{attribute}`: %{message}"
10user:
11format: "%{attribute} of the user %{message}"
The full error messages will look like this.
1
2> > article = Article.new
3> > => #<Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil>
4> > article.errors.full_message(:title, "cannot be empty")
5> > => "`Title`: cannot be empty"
6> > article.valid?
7> > => false
8> > article.errors.full_messages
9> > => ["`Title`: can't be blank"]
10
11> > user = User.new
12> > => #<User id: nil, first_name: nil, last_name: nil, country: nil, created_at: nil, updated_at: nil>
13> > user.errors.full_message(:first_name, "cannot be blank")
14> > => "First name of the user cannot be blank"
15
16> > comment = Comment.new
17> > => #<Comment id: nil, message: nil, author_id: nil, created_at: nil, updated_at: nil>
18> > comment.errors.full_message(:message, "is required")
19> > => "Message is required"
20> >
Notice how the default format %{attribute} %{message}
is used
for generating the full error messages
for the Comment
model
since its format is not being overridden.
Since the other methods such as
ActiveModel::Errors#full_messages
,
ActiveModel::Errors#full_messages_for
,
ActiveModel::Errors#to_hash
etc.
use the ActiveModel::Errors#full_message
method under the hood,
we get the full error messages according to the custom format
in the returned values of these methods respectively as expected.
Similar to customizing format at the model level, we can customize the error format for specific attributes of individual models.
1
2# config/locales/en.yml
3
4en:
5activerecord:
6errors:
7models:
8article:
9attributes:
10title:
11format: "The article's title %{message}"
12user:
13attributes:
14first_name:
15format: "%{attribute} of a person %{message}"
With such a configuration,
we get the customized error message
for the title
attribute of the Article model.
1
2> > article = Article.new
3> > => #<Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil>
4> > article.errors.full_message(:title, "cannot be empty")
5> > => "The article's title cannot be empty"
6> > article.errors.full_message(:description, "cannot be empty")
7> > => "Description cannot be empty"
8
9> > user = User.new
10> > => #<User id: nil, first_name: nil, last_name: nil, country: nil, created_at: nil, updated_at: nil>
11> > user.errors.full_message(:first_name, "cannot be blank")
12> > => "First name of a person cannot be blank"
13> > user.errors.full_message(:last_name, "cannot be blank")
14> > => "Last name cannot be blank"
15> >
Note that
the error messages
for the rest of the attributes
were generated using the default %{attribute} %{message}
format
for which we didn't add custom formats
in the config/locales/en.yml
manifest.
1# config/locales/en.yml
2
3en:
4activerecord:
5errors:
6models:
7article/comments/attachments:
8format: "%{message}"
1
2> > article = Article.new
3> > => #<Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil>
4> > article.errors.full_message(:'comments/attachments.file_name', "is required")
5> > => "is required"
6> > article.errors.full_message(:'comments/attachments.path', "cannot be blank")
7> > => "cannot be blank"
8> > article.errors.full_message(:'comments.message', "cannot be blank")
9> > => "Comments message cannot be blank"
10> >
1# config/locales/en.yml
2
3en:
4activerecord:
5errors:
6models:
7article/comments/attachments:
8attributes:
9file_name:
10format: "File name of an attachment %{message}"
1
2> > article = Article.new
3> > => #<Article id: nil, title: nil, description: nil, created_at: nil, updated_at: nil>
4> > article.errors.full_message(:'comments/attachments.file_name', "is required")
5> > => "File name of an attachment is required"
6> > article.errors.full_message(:'comments/attachments.path', "cannot be blank")
7> > => "Comments/attachments path cannot be blank"
8> >
The custom formats specified in the locale file has the following precedence in the high to low order.
activerecord.errors.models.article/comments/attachments.attributes.file_name.format
activerecord.errors.models.article/comments/attachments.format
activerecord.errors.models.article.attributes.title.format
activerecord.errors.models.article.format
errors.format
To learn more, please checkout rails/rails#32956 and rails/rails#35789.