Has_many and validates_associated

Given the following code:

class MyClass < ActiveRecord::Base

has_many :technical_contacts
has_many :schedule_contacts

validates_associated :technical_contacts, :schedule_contacts

protected

def validate
validate_contacts(technical_contacts)
validate_contacts(schedule_contacts)
end

private

def validate_contacts(contacts)
contacts.each_with_index{|contact, i|
(i+1).upto(contacts.length-1) do |j|
if contacts[j].email == contact.email
contacts[j].errors.add(:email,
‘You have already provided this email
address’)
end
end
}
end

end

If my validate_contacts method adds errors to some contact objects, I
would expect @myclass.save to fail but it does not. I have tested that
the objects are invalid using the “valid?” method and they are so I
don’t understand what is going wrong. Can anyone please help?

Craig N. wrote:

    if contacts[j].email == contact.email

If my validate_contacts method adds errors to some contact objects, I
would expect @myclass.save to fail but it does not. I have tested that
the objects are invalid using the “valid?” method and they are so I
don’t understand what is going wrong. Can anyone please help?

Custom “validate” methods run after all other validations, so
validates_associated will run before the contact errors are added.

You can either get rid of the validates_associated call and add
the base errors in the validate_contacts method, or move the
validate_contacts calls to a :validates_each call that is
placed before the validates_associated call.


We develop, watch us RoR, in numbers too big to ignore.

Mark Reginald J. wrote:
You can either get rid of the validates_associated call and add
the base errors in the validate_contacts method, or move the
validate_contacts calls to a :validates_each call that is
placed before the validates_associated call.>

Hi Mark,

I tried your suggestion but the save method is still working when it
shouldn’t. Here is my code:

class MyClass < ActiveRecord::Base

has_many :technical_contacts
has_many :schedule_contacts

validates_each :technical_contacts do |model, attr, value|
value.each_with_index{|contact, i|
(i+1).upto(value.length-1) do |j|
if value[j].email == contact.email
value[j].errors.add(:email,
‘You have already provided this email
address’)
end
end
}
end

validates_associated :technical_contacts, :schedule_contacts

end

Any help would be most appreciated.

Thanks,

Craig

Craig N. wrote:

  (i+1).upto(value.length-1) do |j|

end

With what you have the error messages you add to a contact in the
validates_each will be cleared before valid? is called on each
of them during the validates_associated.

Is something like this possible?

class Contact < ActiveRecord::Base
validates_uniqueness_of :email, :scope => :my_class_id,
:message => ‘^You have already provided this email address’
end

class TechnicalContact < Contact end
class ScheduleContact < Contact end

class MyClass < ActiveRecord::Base
has_many :technical_contacts
has_many :schedule_contacts
validates_associated :technical_contacts, :schedule_contacts
end

Otherwise, drop the validates_associated:

validates_each :technical_contacts, :schedule_contacts do |m, a, v|
v.each_with_index{ |contact, i|
(i+1).upto(v.length-1) do |j|
if value[j].email == contact.email
value[j].errors.add(:email, ‘Email already provided’)
m.errors.add_to_base(a, ‘Bad contact email address’)
end
end
}
end


We develop, watch us RoR, in numbers too big to ignore.