29
votes

I don't understand why this works.

module Base
  attr_reader :first
  def setup
    @first = 1
  end
end

module Addon
  attr_reader :second
  def setup
    #super
    @second = 2
  end
end

class Test
  include Base
  include Addon

  def initialize(num)
    @num = num
    setup
  end
end

a = Test.new(1)
p a.first
p a.second

Basically I have a "base" module, which sets up some stuff. I also have an addon module, which sets up some more stuff if some class wants to include it.

Now when I test it, if I don't have that super call, I get

nil
2

When I do have the super call, I get

1
2

What does super actually do here? It's calling the setup method from the Base module, even when Base and Addon are not related.

This is the first time I've used super in the context of modules. Previously I've always used super calls with classes and thought it was just going up the inheritance tree to find a parent with the same method.

Is including multiple modules also setting up some sort of inheritance tree?

EDIT: for context, the Addon module will never be included without the Base module, and the Base module will always be included before any additional Addon modules.

1
I think you're right. The method signatures are probably stored in a stack and super just dispatches the message to the object one deeper in the stack. Interesting find I'd say!Candide

1 Answers

40
votes

Yes, when you include modules, they are injected into inheritance chain

Test.ancestors # => [Test, Addon, Base, Object, Kernel, BasicObject]

Addon module, when included, redefines existing setup method from Base. If you want Base's version be called, use super.