1

What I am trying to do is return an error, instead of an exception, when a particular model cannot be destroyed. Currently, it raises ActiveRecord::DeleteRestrictionError, but that is not returned to a flash message, or added to the errors collection of the model.

What I have done is setup this in my resourceful controller:

  def destroy
    begin
      resource.destroy
    rescue ActiveRecord::DeleteRestrictionError => e
      resource.errors.add(:base, e)
    end
  end

I'd rather not manage this within every single controller which requires this particular behavior. How can I abstract it? I can't see it being a good idea to overwrite the destroy method for ActiveRecord::Base, but maybe there won't be any gotchas?

I'm using inherited_resources gem, so perhaps there is a way to answer this by extending that?

One other idea I had was to extend ActiveRecord::Base using ActiveSupport::Concern (from here: Rails extending ActiveRecord::Base) and then delegate the destroy method to a custom destroy on a model-to-model basis. Thoughts?

Community
  • 1
  • 1
Damien Roche
  • 13,189
  • 18
  • 68
  • 96

2 Answers2

2

First I will say I agree with the attitude of not overriding ActiveRecord::Base methods like destroy. In order to DRY this behavior you have several options, I'll list two of them:

The first - Instead of writing the rescue clause in the specific controller you can embed it into your ApplicationController so the behavior will be application-wide:

# ApplicationController.rb

rescue_from ActiveRecord::DeleteRestrictionError do |exception|
  resource.errors.add(:base, exception) if resource
end

Another option would be to make a module and include it in the different controllers you want to have this behavoir:

module SafeDestroyer
  def safe_destroy(resource)
   begin
      resource.destroy
    rescue ActiveRecord::DeleteRestrictionError => e
      resource.errors.add(:base, e)
    end
  end
end

class MyController < ApplicationController
  include SafeDestroyer

  def destroy
    safe_destroy(resource)
  end

end
Erez Rabih
  • 15,562
  • 3
  • 47
  • 64
  • I much prefer the first option. Second seems to require the same unnecessary definitions with the actual innards moved elsewhere. It appears I don't have access to 'resource' from within the application controller. I've included inherited_resources. Anything stand out as to why? The exception is being caught. – Damien Roche Nov 04 '12 at 12:47
  • Found another option: http://roberto.peakhut.com/2010/09/27/admin-controllers-with-inherited-resources/ which is to inherit controllers from a custom 'resources controller' and define the custom behavior there. – Damien Roche Nov 04 '12 at 13:01
  • About your first comment: Don't know why resource is not available for you on the rescue block... Maybe it is some implementation issue with InheritedResources which I don't know that well. The option of Inheriting from controllers is pretty similar to the option of including modules but of course it will also do the job. As I stated, there are many ways to keep you code DRY, I listed two of them. You should definitely use the one that best suits your needs. – Erez Rabih Nov 04 '12 at 13:06
  • Thanks Erez, I've gone with inheriting from a base resources controller as I can build on this and include custom methods for crud. Thanks for the other possibilities, you pointed me in the right direction. – Damien Roche Nov 04 '12 at 13:10
0

What I ended up doing was inheriting my controllers from a master resources controller, which I've found to be much cleaner and powerful than the other options discussed (found here: http://roberto.peakhut.com/2010/09/27/admin-controllers-with-inherited-resources/).

// controllers/resources_controller.rb
class ResourcesController < ApplicationController
  load_and_authorize_resource
  inherit_resources

  def destroy
    begin
      resource.destroy
    rescue ActiveRecord::DeleteRestrictionError => e
      resource.errors.add(:base, e)
    end
  end
end

Then, simply inherit your resourceful controllers from this controller instead of ApplicationController:

// controllers/models_controller.rb
class ModelsController < ResourcesController

end

Hope that helps somebody in a similar situation.

Damien Roche
  • 13,189
  • 18
  • 68
  • 96