我自己扩展Kernel,在实例方法的定义中Kernel#abort,我调用了单例方法Kernel.abort:
module Kernel
  extend self
  def abort
    puts "Press ENTER to exit..."
    gets
    Kernel.abort
  end
end
abort
当我调用时Kernel#abort,似乎Kernel.abort方法定义中的调用引用原始Kernel#abort(扩展为Kernel.abort).
Ruby如何知道当我写作时Kernel.abort,我的意思是原始abort方法,而不是我刚刚创建的方法?我如何递归调用abort刚刚创建的新方法?
Kernel.abort是通过首先定义实例方法Kernel#abort然后使其成为单例方法来定义的module_function。(Rubinius 中的情况绝对如此;我在 MRI 源中找不到它,但见下文。)module_function 复制该方法。当您重新定义时,abort您会重新定义实例方法,但不会重新定义单例副本。
Objectinclude Kernel,所以当你说abort你得到了你已经重新定义的实例方法,但是当你说Kernel.abort你得到了你还没有重新定义的单例方法时。
如果您确实想在 中使用递归abort,或者只是为了证明这个解释是正确的,请module_function :abort在重新定义方法后调用。单例方法将更新为与实例方法相同,并且两种方法都将递归。
请注意,您不需要extend self重新定义abort. 由于Kernel已经包含在 中Object,因此您只需重新定义所有对象的实例方法即可看到重新定义的版本。另一方面,如果一开始Kernel就使用了extend self暴露,我们就可以重新定义它,而不会出现任何复杂的情况。#abort
下面演示了用户定义的纯 Ruby 方法缺乏递归的情况,即这module_function是负责任的,而本机方法则不然:
$ cat foo.rb
module Foo
  def bar
    puts "old version"
  end
  module_function :bar
end
module Foo
  def bar
    puts "new version"
    Foo.bar
  end
end
Object.include Foo
bar
$ ruby foo.rb
new version
old version