While monkey-patching a module from a Rails engine, we found that if you prepend a module B to another module A, the prepended module B won't be added to the ancestors of classes that have already included module A prior to the prepend
. To illustrate:
module A
def a
puts 'a'
end
end
class B
include A
end
module C
def a
puts 'c'
end
A.prepend self
end
class D
include A
end
B.new.a # prints: a
D.new.a # prints: c
B.ancestors #=> [B, A, Object, Kernel, BasicObject]
D.ancestors #=> [D, C, A, Object, Kernel, BasicObject]
Both classes B
& D
include A
, but only D
takes the new behavior as it's the one defined after we call prepend
.
In contrast, we learned that class inheritance does not exhibit this behavior:
class A
def a
puts 'a'
end
end
class B < A
end
module C
def a
puts 'c'
end
A.prepend self
end
class D < A
end
B.new.a # prints: c
D.new.a # prints: c
B.ancestors #=> [B, C, A, Object, Kernel, BasicObject]
D.ancestors #=> [D, C, A, Object, Kernel, BasicObject]
This time, both classes B
& D
have taken the new behavior, even if B
was defined before we called prepend
.
Is there a reason why prepend
behaves differently when used with modules versus classes? I'm assuming this is by design, but it does present a gotcha when using prepend
with modules.
https://bugs.ruby-lang.org/issues/9573 shows a similar behavior concerning classes and modules. The bug report was posted on 2014 and was only closed 2020. Based on that report, I've manually confirmed that