Recently we stumbled upon this inheritance issue, which seemed very weird at the first:
class A
def talk
'A'
end
end
class B < A
def self.define_talk
define_method :talk do
super() << 'B'
end
end
end
class C < B
define_talk
def talk
super << 'C'
end
end
> C.new.talk
=> "AC"
The talk addition from class B doesn’t appear, even though define_talk is triggered by Class C. C’s super call seems to ignore its direct parent B.
Overriding vs Overwriting
The reason for that is the delayed execution of define_talk, which adds the talk method to C rather than B. The previous definition of C is equivalent to this:
class C < B
# calling define_talk is equivalent to
def talk
super << 'B'
end
def talk
super << 'C'
end
end
The second definition of talk doesn’t override the first one, it overwrites it: The first definition is completely gone and therefore no longer available as super.
Overriding with Anonymous Modules
To fix this we need to ensure that B defines the method on B or another untouched inheritance layer. This layer can be an anonymous module created and included by the define_talk method:
class B < A
def self.define_talk
mod = Module.new do
define_method :talk do
super() << 'B'
end
end
include mod
end
end
Now C’s definition of talk only overrides the one of the anonymous module, which is therefore still available as super.
> C.new.talk => "ABC"