Search code examples
rubyinheritancesuperclass

Let `super` call the parent class over an included module


I have class Boy that inherits class Person, and includes module Bipedal. Both Person and Bipedal have versions of #two_legs.

module Bipedal
  def two_legs(name)
    puts "#{name} has exactly two limbs used for walking."
  end
end

class Person
  def two_legs(name)
    puts "#{name} has two human legs."
  end
end

class Boy < Person
  include Bipedal
  attr_accessor :name
  def initialize(name)
    @name = name
  end

  def two_legs
    super(@name)
  end
end

Since the Bipedal module is included in Boy, Bipedal#two_legs takes precedence over the Person#two_legs. When I call super on a Boy instance, the module Bipedal takes precedence over the parent class Person.

johnny = Boy.new('Johnny')
johnny.two_legs
# >> "Johnny has exactly two limbs used for walking."

I want to use one version in one place and the other in another. Bipedal has other stuff, so I can't comment out include Bipedal. Is there some standard way to let Boy#two_legs or super to use the parent class version instead of the module version as follows?

johnny.two_legs
# >> "Johnny has two human legs."

I came up with this:

Boy.superclass.instance_method(:two_legs).bind(self).call(@name)

which works in place of super(@name), but is more complicated than I was expecting.

Again, the question is, is there a standard way to force the parent class to take precedence over the module when calling super?


Solution

  • One could use the method Method#super_method twice. That being a garden-variety Ruby method, I believe it would qualify as a "standard way" to let Boy#two_legs invoke Person#two_legs rather than Bipedal#two_legs.

    class Boy < Person
      include Bipedal
      attr_accessor :name
      def initialize(name)
        @name = name
      end
      def two_legs
        method(:two_legs).super_method.super_method.call(@name)
      end
    end
    
    willie = Boy.new('Willie')
    willie.two_legs
    Willie has two human legs.
    

    Note the following.

    willie.method(:two_legs).super_method.owner
      #=> Bipedal
    willie.method(:two_legs).super_method.super_method.owner
      #=> Person