Question

I'm trying to upgrade a Ruby 1.9.3 app to 2.0, and everything seems to have gone smoothly except for one hiccup. I wrote a module which I include in my models to override activerecord destroy. It aliases the existing destroy method to destroy! and then overrides destroy to change a deleted_at timestamp on the record. Only when I upgrade to ruby 2.0 destroy! no longer destroys the record, but behaves just like my new override method. Any idea why this would be? The more relevant section of code is below. Full gist here.

  def self.included(base)                                                          
    base.class_eval do                                                             
      alias_method :destroy!, :destroy                                             
      alias_method :delete!, :delete                                               
      default_scope -> { where(:deleted_at => nil) }                               
    end                                                                            

    base.send :extend, ClassMethods                                                
    base.send :include, InstanceMethods                                            
  end
Was it helpful?

Solution 2

If you alias a method that is not directly defined in the current class, then alias looks for the method in the nearest ancestor of the class in which it was executed.

When you include Trashable::InstanceMethods into one of your models, it gets inserted at the front of that model's ancestor chain. Hence, calling destroy! in that model triggers the destroy method on Trashable::InstanceMethods.

If you move def destroy from InstanceMethods to base.class_eval, then it would be defined in the including model directly, and the nearest ancestor of that model that contains 'destroy' would be the relevant module in ActiveRecord. Therefore calling destroy! would trigger an SQL DELETE as expected.

See class.ancestors to further explore this behavior.

OTHER TIPS

Check out the paranoia gem. It's a Rails 3/4 compatible implementation of soft deletes that does just what you're after. If all you want to do is provide a soft-delete then I'd use the gem and be done with it. If you want to implement soft deletes yourself, then the implementation can give you some insights into how it's been done before.

In Ruby 2.0 they introduced the concept of prepending modules, so you can insert behaviour between your model and ActiveRecord::Base. I would suggest moving your code into a module and instead of including that model, you can prepend it. Saves from aliasing methods around.

Here are some articles related to the new prepend functionality:

https://gist.github.com/mattetti/5104790

http://blog.crowdint.com/2012/11/05/3-killer-features-that-are-coming-on-ruby-2-0.html

http://dev.af83.com/2012/10/19/ruby-2-0-module-prepend.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top