I have a class Base
, and two classes Derived
and Derived2
that inherit from Base
. They each have a function foo
defined in them.
I also have a module Gen
which is prepend
-ed to Base
. It is also prepend
-ed to Derived2
but not to Derived
.
When I call foo
on an instance of Derived2
, the result is as if the Gen
module was only prepend
-ed to Base
and not to Derived2
also. Is this the expected behavior?
Here is the code for the above scenario:
module Gen
def foo
val = super
'[' + val + ']'
end
end
class Base
prepend Gen
def foo
"from Base"
end
end
class Derived < Base
def foo
val = super
val + "from Derived"
end
end
class Derived2 < Base
prepend Gen
def foo
val = super
val + "from Derived"
end
end
Base.new.foo # => "[from Base]"
Derived.new.foo # => "[from Base]from Derived"
Derived2.new.foo # => "[from Base]from Derived"
I expected the last of the above statement to output:
[[from Base]from Derived]
To help you understand, there is a method Class#ancestors
, which tells you the order in which a method will be searched for. In this case:
Base.ancestors # => [Gen, Base, Object, Kernel, BasicObject]
Derived.ancestors # => [Derived, Gen, Base, Object, Kernel, BasicObject]
Derived2.ancestors # => [Gen, Derived2, Gen, Base, Object, Kernel, BasicObject]
So when you call a method on an object that is an instance of said class, that method will be searched in the corresponding list in that order.
super
just says "go traverse the chain further and find me the same method".For Base
, we have two implementations of foo
- that in Base
and that in Gen
. The Gen
one will be found first as the module was prepended. Therefore calling it on an instance of Base
will call Gen#foo
=[S]
, which will search up the chain as well (via super
) =[from Base]
.
For Derived
, the module wasn't prepended, and we have inheritance. Therefore, the first found implementation is that in Derived
=Sfrom Derived
and super
will search the rest of the chain that comes from Base
(aka the above paragraph is played out) =[from Base]from Derived
.
For Derived2
, the module is prepended, so the method there will be found first =[S]
. Then the super
there will find the next foo
in Derived2
=[Sfrom Derived]
, and the super
there will play out the situation for Base
again =[[from Base]from Derived]
.
EDIT: It seems that up until very recently, prepend
would first search in the ancestors chain and add the module only if it is not already present (similarly to include
). To make it even more confusing, if you first create the parent, inherit from it, prepend in the child and then prepend in the parent, you will get the result of newer versions.