Unable to return errors created on model

925 views Asked by At

I'm using the gem Responders but I'm not able to show errors that I create on my models using erros.add(:base, 'Error message').

On my controller, before the respond_with @app, I debugged the @app object and it has errors @app.errors.any? returns true

On my view, when I check the flash and @app objects, none has the error

App controller

# app_controllers.rb
def destroy
  @app = current_company.apps.find(params[:id])
  @app.destroy
  respond_with @app
end

App model

# app.rb

before_destroy :destroy_on_riak

# ...

def destroy_on_riak
  # SOME CODE HERE
rescue Exception => e
  errors.add(:base, I18n.t("models.app.could_not_destroy", :message => e.message))
  return false
end

App view

# apps.html.haml
-flash.each do |name, msg|
  %div{:class => "flash #{name}"}
    =content_tag :p, msg if msg.is_a?(String)

This is the @app object before the @app.destroy

"#<ActiveModel::Errors:0x00000004fa0510 @base=#<App id: 34, ...>, @messages={}>"

This is the @app object after the @app.destroy

"#<ActiveModel::Errors:0x00000004fa0510 @base=#<App id: 34, ...>, @messages={:base=>[\"Não foi possível excluir a aplicação: undefined method `get_or_new' for #<App:0x00000004f824c0>\"]}>"

I have removed what's inside the @base= for simplicity.

4

There are 4 answers

1
codenamev On

I'd have to agree with @p.mastinopoulos on this. This should really be handled with the builtin validations. Sounds like you are in need of building a custom validator.

http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate

Try replacing your before_destroy with a validate:

validate :destroy_on_riak, on: :destroy

Haven't tried this, but if it doesn't work, you may want to consider creating a custom Validator as referenced in the docs.

4
jefflunt On

The mystery seems to be either (a) you might not be calling the right method, or (b) the .errors hash doesn't contain what you think it contains.

Wrong method?

In your controller you're calling @app.destroy, but the method that adds the errors is called destroy_on_riak

Are you sure you don't mean to type this?

# app_controllers.rb
def destroy
  @app = current_company.apps.find(params[:id])
  @app.destroy_on_riak    # <= The offending line?
  respond_with @app
end

Or is there a before_destroy callback missing from your code sample that in turn calls destroy_on_riak? From the code included I don't see anywhere that the destroy_on_riak method ever gets called, so this is just a guess.

Unexpected contents of .errors hash?

If that's not the problem, then when @app.errors.any? is returning true, then at that point in the code print the contents of @app.errors to your log so you can see what's wrong.

1
p.matsinopoulos On

I will give you some hints:

Hint 1

Your form may be calling valid? on the @app object. The valid? method clears the errors array on the instance.

It is not correct to use the errors array/construct outside of the validations context. But this is MHO.

Hint 2

According to Responders gem (which I have never used in the past), your locale just needs to have the correct configuration. Example:

flash:
  actions:
    create:
      notice: "{resource_name} was successfully created"
    update:
      notice: "{resource_name} was successfully updated"
    destroy:
      notice: "{resource_name} was successfully destroyed"
      alert: "{resource_name} could not be destroyed"

Does it?

0
Nathan Herald On

jefflunt is correct, when one calls #valid? it clears the errors array: see https://github.com/rails/rails/blob/4a19b3dea650351aa20d0cad64bf2d5608023a33/activemodel/lib/active_model/validations.rb#L333

The validators are designed to 100% determine the validity of your object, not when you add errors yourself for later use.

While ActiveRecord does override #valid?, it still calls super: https://github.com/rails/rails/blob/4a19b3dea650351aa20d0cad64bf2d5608023a33/activerecord/lib/active_record/validations.rb#L58

If you want to add errors and have them persist I recommend something like this:

def failures
  @failures ||= []
end

validate :copy_failures_to_errors

def copy_failures_to_errors
  @failures.each { |f| errors.add(*f) }
end

Then modify your rescue:

def destroy_on_riak
  # SOME CODE HERE
rescue Exception => e
  failures.push([:base, I18n.t("models.app.could_not_destroy", :message => e.message)])
  return false
end

I know this seems convoluted and I know there are even examples online where people use or recommend errors.add(:base, ...), but it is not a safe way to store and retrieve errors for later.


Also, just a recommendation, but I advise you to rescue StandardError and not Exception unless you absolutely must. Out of memory errors and stuff like that are Exceptions, but every normal error you would ever want to rescue should inherit from StandardError. Just FYI.