为什么 `Object.include` 和 `Fixnum.prepend` 的顺序很重要?

Why does order of `Object.include` and `Fixnum.prepend` matter?

我有这个模块:

module MyMod
  def +(other)
    puts "hello"
  end
end

这成功覆盖 +Fixnum:

Fixnum.prepend(MyMod)

123 + :test  # outputs "hello"

假设我需要为 Fixnum 和其他对象覆盖 + 运算符。这成功覆盖了 Fixnum 和其他对象的 +

Fixnum.prepend(MyMod)
Object.include(MyMod)

123 + :test  # outputs "hello"

但是,如果我更改 prependinclude 的顺序,我的覆盖无效:

Object.include(MyMod)
Fixnum.prepend(MyMod)

123 + :test  # error: -:10:in `+': :test can't be coerced into Fixnum (TypeError)

为什么includeprepend的顺序在这里有这个效果?

请参阅 Module#prepend_features 的文档:

When this module is prepended in another, Ruby calls prepend_features in this module, passing it the receiving module in mod. Ruby’s default implementation is to overlay the constants, methods, and module variables of this module to mod if this module has not already been added to mod or one of its ancestors. See also Module#prepend.

因此,prepend 仅在其参数尚未添加到接收者或其祖先之一时才会执行任何操作。由于 ObjectFixnum 的祖先,因此在 Object.include(MyMod) 之后调用 Fixnum.prepend(MyMod) 不会执行任何操作。

只是为了澄清@adrian 的回答。

没有模组的祖先链:

puts Fixnum.ancestors
# >> Fixnum
# >> Integer
# >> Numeric
# >> Comparable
# >> Object
# >> Kernel
# >> BasicObject

使用 "working" 改装

Fixnum.prepend(MyMod)
Object.include(MyMod)

puts Fixnum.ancestors
# >> MyMod # here it is, having precedence over Fixnum#+
# >> Fixnum
# >> Integer
# >> Numeric
# >> Comparable
# >> Object
# >> MyMod # note the second copy. include(MyMod) doesn't check for duplicates, but it doesn't matter (here).
# >> Kernel
# >> BasicObject

使用 "not working" 改装

Object.include(MyMod)
Fixnum.prepend(MyMod)

puts Fixnum.ancestors
# >> Fixnum
# >> Integer
# >> Numeric
# >> Comparable
# >> Object
# >> MyMod  # fixnum will override this
# >> Kernel
# >> BasicObject