Ruby 的前置在与模块和类一起使用时表现不同,是否有原因?

gsm*_*oza 12 ruby prepend

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]
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)

This time, both classes B & D have taken the new behavior, even if B was defined before we called prepend.

prepend与模块和类一起使用时,是否有不同的行为?我假设这是设计使然,但在与模块一起使用时确实存在问题prepend

gsm*_*oza 9

https://bugs.ruby-lang.org/issues/9573显示了有关类和模块的类似行为。该错误报告于 2014 年发布,直到 2020 年才关闭。根据该报告,我手动确认了

  • 此行为仍然出现在 ruby​​ 2.7.2p137(2020-10-01 修订版 5445e04352)[x86_64-linux] 中,但是
  • 此行为不再出现在 ruby​​ 3.0.0p0(2020-12-25 修订版 95aff21468)[x86_64-linux]中