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.
CodePudding user response:
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
- This behavior still appears in ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux], but
- This behavior no longer appears in ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]