randombits
randombits

Reputation: 48490

Ruby on Rails bizarre behavior with ActiveRecord error handling

Can anyone explain why this happens?

mybox:$ ruby script/console
Loading development environment (Rails 2.3.5)
>> foo = Foo.new
=> #<Foo id: nil, customer_id: nil, created_at: nil, updated_at: nil>
>> bar = Bar.new
=> #<Bar id: nil, bundle_id: nil, alias: nil, real: nil, active: true, list_type: 0, body_record_active: false, created_at: nil, updated_at: nil>
>> bar.save
=> false
>> bar.errors.each_full { |msg| puts msg }
Real can't be blank
Real You must supply a valid email
=> ["Real can't be blank", "Real You must supply a valid email"]

So far that is perfect, that is what i want the error message to read. Now for more:

>> foo.bars << bar
=> [#<Bar id: nil, bundle_id: nil, alias: nil, real: nil, active: true, list_type: 0, body_record_active: false, created_at: nil, updated_at: nil>]
>> foo.save
=> false
>> foo.errors.to_xml
=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<errors>\n  <error>Bars is invalid</error>\n</errors>\n"

That is what I can't figure out. Why am I getting Bars is invalid versus the error messages displayed above, ["Real can't be blank", "Real you must supply a valid email"] etc.

My controller simply has a respond_to method with the following in it:

 format.xml  { render :xml => @foo.errors, :status => :unprocessable_entity }

How do I have this output the real error messages so the user has some insight into what they did wrong? How do I write my render method in my controller to show all of the appropriate error messages?

Upvotes: 0

Views: 767

Answers (3)

Draco Ater
Draco Ater

Reputation: 21226

We used to overwrite an errors method in particular model, if we needed errors of child objects too, smth like that

class Foo < ActiveRecord::Base

  alias :errors_without_children :errors

  def errors
    self.bars.each do |i|
      i.errors.each_full do |msg|
        errors_without_children.add_to_base msg
      end
    end
    errors_without_children
  end

end

You can still optimise it more. But this one already adds all bars objects' error messages to foo.

Upvotes: 0

Salil
Salil

Reputation: 47522

I think you are using

validates_associated :bar in your foo.rb MODEL

so it only giving "Bars is invalid"

to check the error messages for bars either you have to do following in your

VIEW

<%= error_messages_for :foo, :bar %>

Controller

foo.bar.errors.to_xml

& to skip "bar is invalid" message put following method in foo.rb

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

Upvotes: 1

Tony Fontenot
Tony Fontenot

Reputation: 5101

It's because the errors for bar are stored in the bar object. To get these errors you have to do something like this:

foo.bar.each do |bar|
  bar.errors.each_full { |msg| puts msg }
end

It's all convoluted to me, but I haven't figured out the best way to get all the errors in one list (besides handling it my self). I understand the reasoning behind it (as each object should only know about it's own errors). What I usually do is extent ActiveRecord::Errors and create a new each_full_with_associations function that returns them all.

It all makes sense when you see it on a form with nested fields. In that case the errors are shown properly and all is good.

Upvotes: 0

Related Questions