I’m not the first to post about this, but it’s worth re-posting since not everyone knows how to do it.
Ruby makes it easy to call an objects’ superclass methods:
class Parent def foo # ... end end class Child < Parent def foo super end end
But what if we want to call the super-superclass method, skipping over the superclass definition? Or what if an included module has overridden the superclass method?
class Parent def foo # ... end end module Interloper def foo puts "Bwahaha all your foos are belong to us!" end end class Child < Parent include Interloper def foo # ??? end end
You can do it, of course (this is Ruby); but the means may not be immediately obvious:
class Child < Parent include Interloper def foo Parent.instance_method(:foo).bind(self).call end end
If it’s not clear what’s going on here: we’re creating an UnboundMethod
object representing the Parent#foo
method, binding that method to the child class instance, and immediately invoking it. It’s a bit unwieldy, but when you want to be absolutely sure of which method implementation you are calling, this is the best way to go.
I'm admittedly not thinking about this too hard at the moment, but why would you ever want to do this? It seems like a band-aid for some bad design decisions at first blush.
#1 reason: Some module which you need happens to override a method from another module or base class for its own unrelated purposes.
Wouldn't it be better to alias the parent method before including the module in that case? Or is there a circumstance I'm missing here?
http://gist.github.com/314681
I personally find that solution less elegant, and I don't like the fact that simply moving things around in the class definition could break it. It's fragile in ways that could be very nonobvious to a future maintainer. Using method binding is unambiguous as to which version of the method you want.
YMMV, of course.
It seems that allowing an unrelated module to override methods that just happen to be named the same is prone to breaking things as well, so I think by the time you're doing that you have to accept that it's going to be a bit tricky for any future maintainers to figure out what's going on. But you're right, on reflection, the alias method is unnecessarily fragile.
Perhaps it would be best then, if we're not using Interloper#foo at all? That's what bothers me about this anyway, so perhaps this would be a better solution:
http://gist.github.com/314707
The problem here is that Interloper itself might use its own #foo method in other (desirable) Interloper methods.
If you're overriding #foo in Child anyway, then you're already preventing that.
True. This is more of an issue for the case were you're merely calling it and not overriding it (not shown).
…not to mention that it's not usable if you're trying to define a method in a module. For instance, the case where you're writing a module which uses built-in Object methods like #send or #hash and needs to make sure the calls don't get diverted to some other intervening definition of #send or #hash, no matter where it is included.
Now that is a more interesting and less scary use case. 🙂
I did not know this. I can think of a few (rare) cases where I did want access to the grant parent method so I aliased it in the parent class prior to defining the same named method in the parent. This always felt like a bit of a hack to me, and while your idea is still a hack, the intention is clearer.
This may also be useful in cases where the parent class is in a library you don't control, and you'd prefer not locally/monkey patch it.
I don't think it's any more a hack than e.g. any code that uses #send to call a method. Verbose and non-obvious, maybe, but I think it's using language features for their intended purpose.
I'm admittedly not thinking about this too hard at the moment, but why would you ever want to do this? It seems like a band-aid for some bad design decisions at first blush.
#1 reason: Some module which you need happens to override a method from another module or base class for its own unrelated purposes.
Wouldn't it be better to alias the parent method before including the module in that case? Or is there a circumstance I'm missing here?
http://gist.github.com/314681
I personally find that solution less elegant, and I don't like the fact that simply moving things around in the class definition could break it. It's fragile in ways that could be very nonobvious to a future maintainer. Using method binding is unambiguous as to which version of the method you want.
YMMV, of course.
…not to mention that it's not usable if you're trying to define a method in a module. For instance, the case where you're writing a module which uses built-in Object methods like #send or #hash and needs to make sure the calls don't get diverted to some other intervening definition of #send or #hash, no matter where it is included.
It seems that allowing an unrelated module to override methods that just happen to be named the same is prone to breaking things as well, so I think by the time you're doing that you have to accept that it's going to be a bit tricky for any future maintainers to figure out what's going on. But you're right, on reflection, the alias method is unnecessarily fragile.
Perhaps it would be best then, if we're not using Interloper#foo at all? That's what bothers me about this anyway, so perhaps this would be a better solution:
http://gist.github.com/314707
Now that is a more interesting and less scary use case. 🙂
The problem here is that Interloper itself might use its own #foo method in other (desirable) Interloper methods.
If you're overriding #foo in Child anyway, then you're already preventing that.
True. This is more of an issue for the case were you're merely calling it and not overriding it (not shown).
I did not know this. I can think of a few (rare) cases where I did want access to the grant parent method so I aliased it in the parent class prior to defining the same named method in the parent. This always felt like a bit of a hack to me, and while your idea is still a hack, the intention is clearer.
This may also be useful in cases where the parent class is in a library you don't control, and you'd prefer not locally/monkey patch it.
I don't think it's any more a hack than e.g. any code that uses #send to call a method. Verbose and non-obvious, maybe, but I think it's using language features for their intended purpose.