views:

166

answers:

2

I have a sign-up form that has nested associations/attributes whatever you want to call them.

My Hierarchy is this:

class User < ActiveRecord::Base
  acts_as_authentic
  belongs_to :user_role, :polymorphic => true
end

class Customer < ActiveRecord::Base
  has_one :user, :as => :user_role, :dependent => :destroy
  accepts_nested_attributes_for :user, :allow_destroy => true
  validates_associated :user
end

class Employee < ActiveRecord::Base
  has_one :user, :as => :user_role, :dependent => :destroy
  accepts_nested_attributes_for :user, :allow_destroy => true
  validates_associated :user
end

I have some validation stuff in these classes as well. My problem is that if I try to create and Customer (or Employee etc) with a blank form I get all of the validation errors I should get plus some Generic ones like "User is invalid" and "Customer is invalid" If I iterate through the errors I get something like:

user.login can't be blank
User is invalid
customer.whatever is blah blah blah...etc
customer.some_other_error etc etc

Since there is at least one invalid field in the nested User model, an extra "X is invalid" message is added to the list of errors. This gets confusing to my client and so I'm wondering if there is a quick way to do this instead of having to filer through the errors myself.

+1  A: 

Use after_validation method

  def after_validation
    # Skip errors that won't be useful to the end user
    filtered_errors = self.errors.reject{ |err| %w{ user User  }.include?(err.first) }
    self.errors.clear
    filtered_errors.each { |err| self.errors.add(*err) }
  end

EDITED

Note:-

Add the in the following list in your case it's User or user. you can add multiple if you have multiple assosciation separated by space.

%w{ User user }.include?(err.first) #### This piece of code from the above method has logic which reject the errors which won't be useful to the end user
Salil
@Salil: Ok this looks promising! :) I think you have a grammar error under "Note:-" thats confusing me.. "Add the ... ???" Also, where should I put this function? in each model that does this? Is this a function override from ARB?
DJTripleThreat
sorry for grammar :). put this method in every model where you write **validates_associated**
Salil
@Salil Ok I will upvote you now because this mostly works. One problem though: instead of this:**Login** is too short (minimum is 3 characters)**Password** is too short (minimum is 4 characters)**Password confirmation** is too short (minimum is 4 characters)I am getting this:**User.login** is too short (minimum is 3 characters)**User.password** is too short (minimum is 4 characters)**User.password confirmation** is too short (minimum is 4 characters)If you can figure out how to fix this I will accept your answer.
DJTripleThreat
+1  A: 

Salil's answer was almost right but he never made it 100%. Here is the correct way to do it:

def after_validation
    # Skip errors that won't be useful to the end user
    filtered_errors = self.errors.reject{ |err| %{ person }.include?(err.first) }

    # recollect the field names and retitlize them
    # this was I won't be getting 'user.person.first_name' and instead I'll get
    # 'First name'
    filtered_errors.collect{ |err|
      if err[0] =~ /(.+\.)?(.+)$/
        err[0] = $2.titleize
      end
      err
    }

    # reset the errors collection and repopulate it with the filtered errors.
    self.errors.clear
    filtered_errors.each { |err| self.errors.add(*err) }
  end
DJTripleThreat
Also, I have propose a feature request as a better solution then this work-around: https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/5632-validates_associated-should-be-allowed-to-not-create-an-error#ticket-5632-2
DJTripleThreat