If we define two modules, with methods that have the same names
module M1
def foo
puts "M1"
end
end
module M2
def foo
puts "M2"
end
end
and then include them in a class in a specific order
class C include M1 include M2 end
method from the last included module will be used.
C.new.foo # => "M2"
So it looks like the methods are “copied” into the including class, so that the last definition of “foo” gets precedence. That’s how I thought about including Ruby modules initially.
But if that was the case the following example
module M
def foo
puts "M"
end
end
class C
def foo
puts "C"
end
include M
end
should print “M”. But it doesn’t.
C.new.foo # => "C"
It calls the class “C” version of the “foo”, so the method can’t be redefined during the include.
What actually happens (and what I learned from “Include” part of Chapter 4 of Ruby Hacking Guide) is that the included module gets injected into the class hierarchy right above “C”.
class C
def foo
puts "C"
super # Calling to see what the superclass defined.
end
end
Let’s check what the hierarchy looks like before the inclusion of M.
C.ancestors # => [C, Object, Kernel]
Now let’s define “M”
module M
def foo
puts "M"
end
end
and include it in “C”.
class C include M end
Let’s check how it affected the class hierarchy.
C.ancestors # => [C, M, Object, Kernel]
Module “M” got injected as a direct superclass of “C”.
C.new.foo # => "C" then "M"
As C#foo calls super it’s now obvious how we got that output.