Calling Grandparents in Ruby

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.

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.

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

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

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

  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.

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

  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.

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

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

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

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

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

Leave a Reply to Tommy Morgan Cancel reply

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