Using Rails 3.1.3. I have Accounts and Users. One Account can have many Users. I set this up using accepts_nested_attributes_for as described in this answer.
I have a new.html.erb view that accepts data for one account and one user at the same time. (The user’s data goes into a “subform.”) The form works fine. However if there is an error, the messages for the subform’s fields are pluralized, even though they should be singular. For example, I get
Users password doesn't match confirmation
Users password is too short (minimum is 8 characters)
instead of
User password doesn't match confirmation
User password is too short (minimum is 8 characters)
I don’t think this is an inflection issue, since “User” follows standard pluralization rules. Rather it must have to do with the use of nested attributes in a subform. In my case, the subform returns an array containing one user, but theoretically it could return data for multiple users.
How/where can I tell Rails not to pluralize when referring to only one element of an array?
Edit 3/14/2012 to show controller and view:
app/controllers/accounts_controller.rb (“New” action only):
class AccountsController < ApplicationController
before_filter :authenticate_user!, :except => [:new, :create]
before_filter :user_signed_out, :only => [:new, :create]
load_and_authorize_resource # CanCan - does a standard load for each action, and authorizes user
def new
# CanCan: @account = Account.new (and tests each attribute for ability)
@account.users.build
@title = "Sign Up for a New Account"
@header = "Sign up for a new account"
end
end
app/views/accounts/new.html.erb:
<h2><%= @header %></h2>
<%= form_for(@account) do |f| %>
<%= render 'account_fields', :f => f %>
<%= f.fields_for :users do |user_form| %>
<div class="field"><%= user_form.label :email %><br />
<%= user_form.email_field :email %></div>
<div class="field"><%= user_form.label :password %><br />
<%= user_form.password_field :password %></div>
<div class="field"><%= user_form.label :password_confirmation %><br />
<%= user_form.password_field :password_confirmation %></div>
<% end %>
<div class="actions">
<%= f.submit "Create Account" %>
</div>
<% end %>
I was trying error_messages_for inside the f.fields_for block above.
app/views/shared/_error_messages.html.erb rendered by layout before all views:
<% if object.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(object.errors.count, "error") %>
prohibited this <%= object.class.to_s.underscore.humanize.downcase %>
from being saved:</h2>
<p>There were problems with the following fields:</p>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
As @Marc suggested, the messages are generated based on the model. This gets tricky when
accepts_nested_attributes_foris involved, as it is in my Account model:To get more insight into the error object, I inserted
<%= object.errors.inspect %>into _error_messages.html.erb. This showed me that the full error object is, for example:When Rails generates the full_messages array, it just feeds each attribute and message into the full_message method. If the attribute is “users.password”, for example, then that will simply be “humanized” (without respect to the number of objects in the array) and concatenated with the message(s) to return
The crux of any solution is to avoid using full_messages:
Although I couldn’t get
error_messages_forto work, it wouldn’t be toohard to write a small helper that allows specifying a custom object
name to prepend to the message.
Avoid the problem of the displaying object name altogether by highlighting the
specific field that is in error and placing the error message suffix(es) next
to the field. This works nicely with Twitter Bootstrap and SimpleForm.
This article make a good case for providing explicit
translations for each message, claiming that any attempt to
concatenate strings is bound to cause problems.