I’m developing an online store, and the customer needs the ability to delete an order and have its products automatically restocked (e.g., for test orders). Here’s my first try at implementing this:
class Order < ActiveRecord::Base
def destroy_and_restock
restock_products
destroy
end
protected
def restock_products
line_items.each do |li|
li.product.quantity_on_hand += li.quantity
li.product.save
end
end
end
But what if I need to create another destroy_and_x method later? Why not allow that X to be passed as a parameter to the destroy() method? So now I’m thinking of going with this:
alias :old_destroy :destroy
def destroy(options = {})
if options['restock'] == true
restock_products
end
old_destroy
end
protected
def restock_products
line_items.each do |li|
li.product.quantity_on_hand += li.quantity
li.product.save
end
This is more extensible, but makes me feel somewhat dirty. Am I wrong to feel dirty? Is there a better way of doing this?
I’d say “yes, this is dirty.” Your intention isn’t to modify the behavior of the ‘destroy’ method, but rather to do some domain-specific work, then run
destroy. Your first approach is great — define a method that does what you want, and invokedestroyas needed. I think that ‘wrapping’ or ‘monkey-patching’ a method, as you’re considering, is a technique that’s best applied when standard OO approaches can’t be used — eg, when you need to modify/augment behavior in a class that is defined and used outside of your realm of control.Even if you are considering modifying the behavior of the
destroymethod itself, I’d suggest overriding the method here, rather than wrapping it: