Welcome to Working With Rails

 

Discussion Forums

Discuss all things Ruby on Rails with perhaps the web's most vibrant group of Ruby on Rails enthusiasts.
Customizing error_messages, shared solution
1 Post
Customizing error_messages, shared solution

I needed to fix the default error_messages mechanism which was bad in one particular instance, which lead to "Secret answers answer can't be blank". However, I did not want to reinvent or have to provide error messages for every validate, which were some of the solutions I found around the net. I started with an idea I read about overriding error_messages_for, which is what I did. I left it largely intact because I did not want to mess with the default behavior.

You should be able to paste this function into any of your module helpers and anywhere you use

it should work exactly as it did before. Now for the changes...

The header_message can be changed but I did not want to lose the count so you can provide any string you want and use the $COUNT place holder. This is not case sensitive.

"my form had $COUNT errors" %>

That is helpful but now for the major fix. If you prepend the :message text with an ! (exclamation point) it will take that string literally. If you do not prepend it with an ! it works as it always did.

validates_presence_of :answer, :message => '!Answers must be provided for each security question'

Still want to use the attribute name but not at the beginning of the string, then use the $attr place holder.

validates_presence_of :answer, :message => '!There were problems with $attr, so fix it!'

The code to paste...

def error_messages_for(*params)

options = params.extract_options!.symbolize_keys

if object = options.delete(:object)
    objects = [object].flatten
else
    objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
end

count  = objects.inject(0) {|sum, object| sum + object.errors.count }
unless count.zero?
    html = {}
    [:id, :class].each do |key|
        if options.include?(key)
            value = options[key]
            html[key] = value unless value.blank?
        else
            html[key] = 'errorExplanation'
        end
    end
    options[:object_name] ||= params.first

    I18n.with_options :locale => options[:locale], :scope => [:activerecord, :errors, :template] do |locale|
        header_message = if options.include?(:header_message)
            options[:header_message].gsub(/\$COUNT/i, count.to_s)
        else
            object_name = options[:object_name].to_s.gsub('_', ' ')
            object_name = I18n.t(object_name, :default => object_name, :scope => [:activerecord, :models], :count => 1)
            locale.t :header, :count => count, :model => object_name
        end

        message = options.include?(:message) ? options[:message] : locale.t(:body)

        error_messages = objects.map do |obj|
            obj.errors.collect do |attr, msg|

                if msg.starts_with?('!')
                    msg[0] = ''
                    fmt_msg = msg.gsub(/\$ATTR/i, attr.humanize.downcase)
                else
                    fmt_msg = [attr.humanize, msg].join(' ')
                end

                content_tag( :li, fmt_msg )
            end
        end

        contents = ''
        contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
        contents << content_tag(:p, message) unless message.blank?
        contents << content_tag(:ul, error_messages)

        content_tag(:div, contents, html)
    end
else
    ''
end

end

1 Post
Login to add your message