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

时间:2015-08-14 16:15:27

标签: ruby module

我有这个模块:

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

这已成功覆盖+的{​​{1}}:

Fixnum

我们说我需要为Fixnum.prepend(MyMod) 123 + :test # outputs "hello" 和其他对象覆盖+运算符。这会成功覆盖Fixnum和其他对象的+

Fixnum

但是,如果我更改Fixnum.prepend(MyMod) Object.include(MyMod) 123 + :test # outputs "hello" prepend的顺序,则我的覆盖无效:

include

为什么Object.include(MyMod) Fixnum.prepend(MyMod) 123 + :test # error: -:10:in `+': :test can't be coerced into Fixnum (TypeError) include的顺序会产生此效果?

2 个答案:

答案 0 :(得分:4)

请参阅Module#prepend_features的文档:

  

当这个模块被放在另一个模块中时,Ruby在这个模块中调用prepend_features,并在mod中传递接收模块。 Ruby的默认实现是将此模块的常量,方法和模块变量覆盖到mod ,如果此模块尚未添加到mod或其祖先之一。另见Module#prepend。

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

答案 1 :(得分:4)

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

没有改装的祖先链:

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

使用“工作”模式

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

“不工作”modding

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

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