Enhancing Rails Errors 22

Posted by Bob Silva Fri, 27 Jan 2006 01:52:00 GMT

After the first day of playing with the Rails framework, I decided the error messages generated by the validation routines were a bit lacking for my taste. It would be nice if the errors displayed followed the same order as your form elements and the humanizing only works if you name your table columns with this in mind. Knowing Ruby and Rails are all-powerful, I figured there must be an elegant way to fix this without writing my own custom error handling for each view. Rails Plugins to the rescue.

So here is my "improved" error_messages_for function.

<%= error_messages_for 'client', { :priority => ['first_name', 'last_name'], :attr_names => {'first_name' => 'Surely, your mother gave you a name?', 'last_name' => 'Suffering from Alzheimers are you?', 'gender' => 'Now I understand why your mother did not name you'}, :defaults=> false, :header => 'You need to correct {count} error(s) before we can save this {object}', :sub_header => 'What on earth were you thinking?' } %>

This will print out:



I admit, this is a bit cumbersome, but it solved the problem I had and for anyone else thats anal about little things like this, it will solve your problems too!


You can download the plugin here. To use the plugin, just extract it to your vendor/plugins directory in your Rails Project. View the README and add some spice to your errors. Let me know if you have ideas for improvement or you have any problems. Enjoy!


UPDATE: Just released version 0.2. This adds the ability to pass in an array of object names and have the errors consolidated into one container.

Comments

Leave a response

  1. iqram 5 months later:
    Very useful function. Saved me a lot of thought and time! Appreciate it.
  2. Herman 5 months later:
    Thanks a lot ! Works absolutely great for me.
  3. kris 6 months later:
    Used this, good stuff. Might be nice to put in the readme that you need to copy the folder to vendor/plugins for those who have never done so before :)
  4. Chris 6 months later:
    First of all, I really like your plugin. I'm big on the error messages and your plugin makes them very easy to customize. Just one small thing: * If you use errors.add_to_base, it'll by default display an attr_name of 'Base' at the beginning of your error message. Sure, you could override the error message completely in error_messages_for... but I made a small tweak to the way errors are displayed to get it to play a little nicer: #{value[0].humanize unless value[0] == 'base'}
  5. Sebastien 6 months later:
    Thanks for the amazingly helpful plugin! Do you have some more detailed instructions for a newbie such as myself? I've changed the Header & subheader but the priority implementation eludes me. Thanks for any and all help :)
  6. vr_mex 7 months later:
    it would be very usefull if you could add validating and error message handling when using observe_field
  7. Petr 9 months later:
    This must be included in future version of Rails. Very good.
  8. Petr 9 months later:
    This must be included in future version of Rails. Very good.
  9. kyle 9 months later:
    Beautiful. Great job.
  10. Inanc Gumus 9 months later:
    Thanks for the plug-in. Notice that if there is no error an exception occurs. This patch beats the nut:

    # put this at the top of the error_messages_for method

    
            object = instance_variable_get("@#{object_names}")
            return "" if !object || object.errors.empty?
            
  11. Matt Allen 10 months later:
    Inanc; You method doesn't work if you hand in an array of modules. Try this one. Change line 10 to read
    object_names.each { |name| objects << instance_variable_get("@#{name}") if !instance_variable_get("@#{name}").errors.nil? }
    
  12. jdub 10 months later:
    This works great! Thank you! A question: Is there an elegant way to handle multiple errors generated by the errors.add_to_base method? Right now, the error is returned as an attribute named 'base', which can be overridden, but I ran into trouble with more than one possible Base error.
  13. piter 10 months later:
    Could you add to your plugin a small override of class Base from ActionView module? There is hardcoded "div" usage, but i don't think that "div" is the best tag in that case. I think "span" will be better.
  14. Diego 11 months later:
    Thanks, saved my day. It seems this is a weak point in ruby. Thanks to your help I found a solution
  15. dodah 11 months later:
    {'guest' = 'Gäst', 'title' = 'Titel', 'comment' = 'Kommentar'} would be nice if we could do: {:guest = 'Gäst', :title = 'Titel', :comment = 'Kommentar'}
  16. Jürgen 12 months later:
    I don't know why, but the plugin is not picked up automatically, I dropped the plugin correctly into the vendor directory. So when I call the error_messages_for method it will still call the standard rails one. Anyone can help?
  17. Spidou about 1 year later:
    try restart the server!
  18. Alexey about 1 year later:
    Exxxellent pluging !
  19. FiFouille about 1 year later:
    Just made a little modification to imitate comportement of this plugin http://rubyforge.org/projects/custom-err-msg/ So if your validation message starts with a '^', the humanized column name is not display before your custom message You'll only have to specify order, header and sub_header in your view, errors messages are still held in the model in error_messages_for.rb changed : content_tag("ul", errors.collect { |value| content_tag("li", ((options[:attr_names].has_key?(value[0])) ? "#{options[:attr_names][value[0]]}" + (options[:defaults] ? " #{value[1]}" : '') : "#{value[0].humanize} #{value[1]}")) }),-- to : content_tag("ul", errors.collect { |value| content_tag("li", ((options[:attr_names].has_key?(value[0])) ? "#{options[:attr_names][value[0]]}" + (options[:defaults] ? " #{value[1]}" : '') : true ? "#{value[1][1..-1]}" : "#{value[0].humanize} #{value[1]}")) }), this is my first 'patch' in the source of Rails and Rails plugins, so don't take this as 100% bullet proof PS : I can't paste the diff file, impossible to get proper form with this commenty box
  20. FiFouille about 1 year later:
    and I made a mistake ... true must be replaced by
    value[1] =~ /^\^/
    giving this line :
    content_tag("ul", errors.collect { |value| content_tag("li", ((options[:attr_names].has_key?(value[0])) ? "#{options[:attr_names][value[0]]}" + (options[:defaults] ? " #{value[1]}" : '') : value[1] =~ /^\^/ ? "#{value[1][1..-1]}" : "#{value[0].humanize} #{value[1]}")) }),
    replacing the 'old' one :
    content_tag("ul", errors.collect { |value| content_tag("li", ((options[:attr_names].has_key?(value[0])) ? "#{options[:attr_names][value[0]]}" + (options[:defaults] ? " #{value[1]}" : '') : "#{value[0].humanize} #{value[1]}")) }),
  21. Hernan about 1 year later:
    I couldn't make it work for Rails 1.2.1; someone else has the same problem? Thanks in advance!
  22. Mike about 1 year later:
    Matt Allen: Your fix didn't quite work for me until I modified it to make sure the variable isn't nil before checking that *.error.nil?:
    object_names.each { |name| objects  instance_variable_get("@#{name}") if !instance_variable_get("@#{name}").nil? && !instance_variable_get("@#{name}").errors.nil? }
Comments