28 Aug, 2009

Published at 01:16AM

Tagged with development, golftrac, programming, rails, ruby, and validations

This post has 5 comments

A better validates_associated

As you may very well know, validates_associated is good, but not great. Yes, it does ensure that your related models adhere to the rules in your multi-model form. But there are two fundamental problems with it:

  1. The actual error messages are useless (i.e. “[Model] is invalid”) — I’d like to know why it’s invalid
  2. Redundant messages for each and every invalid associated model

The first, everyone can agree on. So let’s fix that one right away:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# config/initializers/validation_fixes.rb
module ValidatesAssociatedAttributes
  module ActiveRecord::Validations::ClassMethods
    def validates_associated(*associations)
      class_eval do
        validates_each(associations) do |record, associate_name, value|
          (value.respond_to?(:each) ? value : [value]).each do |rec|
            if rec && !rec.valid?
              rec.errors.each do |key, value|
                record.errors.add(key, value)
              end
            end
          end
        end
      end
    end
  end
end

There, now you’ll get the true error messages. For example, “[Model] is invalid” now becomes “[Model] is not formatted properly”, or the like.

Now, onto the second one. I find that most of my multi-model forms have more than one nested model. To save you from a contrived example, let’s use Golf Trac. Well, a course has 4 different sets of 18 yardages (72 yardages), 2 sets of 18 handicaps, 18 par ratings, then 1 rating and 1 slope for each of the 4 possible tees (black, blue, white, red). That’s a total of (I think) 134 fields on a course (I know, that’s why courses are now reusable).

So, the course form, when submitted, gets split out into three tables: courses, tees, and holes, each with their own validation rules. Let’s say a user submits a blank form. Originally, validates_associated would say “Hole is invalid” a gajillion times. That’s stupid in my case. I’d rather it just say “Par cannot be blank” and/or “Par must be a number” one time each. So, combined with step #1, I did something like this:

1
2
3
4
5
6
7
# config/initializers/validation_fixes.rb
class ActiveRecord::Errors
  alias old_full_messages full_messages
  def full_messages
    old_full_messages.uniq
  end
end

Now I get much better error messages and I’m only yelling at the user one time.

Comments

sandeep Monday, 07 Sep, 2009 Posted at 09:10AM

hey buddy
Thanx for your post.
i got the following error when i implemented your solution

{:message=>"Address is incomplete or invalid"} is not a symbol

Please help………..

Sarah Allen Tuesday, 15 Sep, 2009 Posted at 08:13AM

Nice. You should file a lighthouse ticket and submit a patch. I would +1 it. BugMash is coming up in a couple of weeks if are new to the rails contribution process :)

Eric A. Saturday, 31 Oct, 2009 Posted at 10:29PM

Great post, exactly what’s needed to enhance the validates_associated method.

@sandeep
I initially encountered a similar issue. However, the easy fix is to remove the :message => “…” part of the validates_associated line, then specify your error messages on the associated models accordingly.

Nick R. Monday, 02 Nov, 2009 Posted at 03:00PM

+1 for rails patch

Chris Rogers Monday, 09 Nov, 2009 Posted at 05:27PM

Works great, appreciated.

Do you have something to say about this post?
Retype the image to the right Spam Hint: Are You Human? Textile Formatting Tips

or

Ryan Heath | Site Management A Ruby on Rails production.

This site is a Formed Function. Formed Function LLC | @formedfunction | Get in Touch