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.

Related posts:

  1. Hash Transforms in Ruby
  2. Sustainable Development in Ruby, Part 2: Method Injection
  3. First and Rest in Ruby
  4. Class.new and .inherited()
  5. Double-Load Guards in Ruby
This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.
  • http://twitter.com/duwanis Tommy Morgan

    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.

    • http://avdi.org avdi

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

      • http://twitter.com/duwanis Tommy Morgan

        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

        • http://avdi.org avdi

          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.

          • http://twitter.com/duwanis Tommy Morgan

            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

          • http://avdi.org avdi

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

          • http://twitter.com/duwanis Tommy Morgan

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

          • http://avdi.org avdi

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

        • http://avdi.org avdi

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

          • http://twitter.com/duwanis Tommy Morgan

            Now *that* is a more interesting and less scary use case. :)

  • dkubb

    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.

    • http://avdi.org avdi

      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.

  • http://twitter.com/duwanis Tommy Morgan

    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.

  • http://wideteams.com Avdi Grimm

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

  • http://twitter.com/duwanis Tommy Morgan

    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

  • http://wideteams.com Avdi Grimm

    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.

  • http://wideteams.com Avdi Grimm

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

  • http://twitter.com/duwanis Tommy Morgan

    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

  • http://twitter.com/duwanis Tommy Morgan

    Now *that* is a more interesting and less scary use case. :)

  • http://wideteams.com Avdi Grimm

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

  • http://twitter.com/duwanis Tommy Morgan

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

  • http://wideteams.com Avdi Grimm

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

  • dkubb

    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.

  • http://wideteams.com Avdi Grimm

    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.