为什么 Ruby 的前缀在与模块一起使用时与 类 的行为不同?

Is there a reason why Ruby's prepend behaves differently when used with modules versus classes?

在从 Rails 引擎对模块进行猴子修补时,我们发现如果将模块 B 添加到另一个模块 A,则添加的模块 B 不会添加到 [=36] 的祖先=]es 在 prepend 之前已经包含模块 A。举例说明:

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]

classes BD 都包含 A,但只有 D 采用新行为,因为它是在我们调用 prepend.

相比之下,我们了解到 class 继承不会表现出这种行为:

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]

这一次,class和 BD 都采用了新的行为,即使 B 是在我们调用 prepend 之前定义的。

prepend 与模块一起使用时与 classes 一起使用时的行为不同是否有原因?我假设这是设计使然,但在将 prepend 与模块一起使用时确实会出现问题。

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]