Sean O'Halpin
7/13/2006 11:56:00 PM
On 7/14/06, Tom Werner <tom@helmetstohardhats.org> wrote:
> Allow me to present a scenario:
>
> class Firetruck
> def put_out_fire(options = {})
> # code
> end
> end
>
> Pretend Firetruck is in a 3rd party application (like Rails) that is
> happy to allow plugins to modify core code. Now, let's say I want to
> write some code that always adds a certain attribute to the options
> hash. I could do this:
>
> class Firetruck
> alias_method :__old_put_out_fire, :put_out_fire
> def put_out_fire(options = {})
> __old_put_out_fire(options.merge({:nozzle => :big}))
> end
> end
>
> Which works just fine until someone else comes up with a plugin that
> wants to modify the same method (doing something similar to me) and just
> so happens to also use :__old_put_out_fire as THEIR alias. Now we've got
> my plugin's method as the alias calling itself, which leads to, you
> know, badness.
>
> So I'm wondering if there's a better way. Perhaps some way to turn
> Firetruck into an ancestor of itself, so to speak, so that my plugin
> would create a new Firetruck class, pushing the old Firetruck backward
> in the chain and allowing me to call super instead and preventing
> alias_method explosions. Or would that just end up causing more havoc?
>
> Tom
>
> --
> Tom Werner
> Helmets to Hardhats
> Software Developer
> tom@helmetstohardhats.org
> www.helmetstohardhats.org
>
Here's one way to do it:
class Firetruck
def put_out_fire(options = {})
p [1, self.class, options]
# code
end
end
class Firetruck
# get ref to (unbound) old instance method
old_put_out_fire = instance_method(:put_out_fire)
define_method :put_out_fire do |options|
p [2, self.class, options]
options ||= { }
old_put_out_fire.bind(self).call(options.merge({:nozzle => :big}))
end
end
f = Firetruck.new
f.put_out_fire :colour => :red
__END__
[2, Firetruck, {:colour=>:red}]
[1, Firetruck, {:nozzle=>:big, :colour=>:red}]
Note that the method rebinding happens every time you call the method
so incurs a fair bit of overhead.
Regards,
Sean