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.

Published by Avdi Grimm

24 Comments

  1. 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.

    Reply
    • #1 reason: Some module which you need happens to override a method from another module or base class for its own unrelated purposes.

      Reply
      • 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

        Reply
        • 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.

          Reply
          • 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.

          Reply
  2. 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.

    Reply
    • 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.

      Reply
  3. 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.

    Reply
  4. #1 reason: Some module which you need happens to override a method from another module or base class for its own unrelated purposes.

    Reply
  5. 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

    Reply
  6. 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.

    Reply
  7. …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.

    Reply
  8. 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

    Reply
  9. Now that is a more interesting and less scary use case. 🙂

    Reply
  10. The problem here is that Interloper itself might use its own #foo method in other (desirable) Interloper methods.

    Reply
  11. If you're overriding #foo in Child anyway, then you're already preventing that.

    Reply
  12. True. This is more of an issue for the case were you're merely calling it and not overriding it (not shown).

    Reply
  13. 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.

    Reply
  14. 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.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *