Ruby,向Module,Class或Object添加方法

时间:2015-03-21 01:52:43

标签: ruby

当我向Kernel添加方法以使其全局可用时,我发现了一些奇怪的事情。这很有趣,我正在寻找一些文档或很好的解释。

让我们看一下代码:

档案:./ demo.rb

# example 1

module Kernel
  def foo
    puts "I'm defined inside the module!"
  end
end


# example 2

module Bar
  def bar
    puts "I'm included! (bar)"
  end
end

Kernel.send :include, Bar


# example 3

module Baz
  def baz
    puts "I'm included! (baz)"
  end
end

module Kernel
  include Baz
end

然后,在bash和IRB中

$ irb -r ./demo.rb
> foo
# I'm defined inside the module!
> bar
# NameError: undefined local variable or method `bar' for main:Object
> baz
# NameError: undefined local variable or method `baz' for main:Object
>
> self.class.ancestors
# => [Object, Kernel, BasicObject] 
>
> include Kernel
>
> self.class.ancestors
# => [Object, Kernel, Baz, Bar, BasicObject]
>
> foo
# I'm defined inside the module!
> bar
# I'm included! (bar)
> baz
# I'm included! (baz)

foo按预期工作,可用于包含Kernel的所有对象。另一方面,barbaz无法立即获得 我想这是因为IRB的评估上下文Object)已经包含Kernel,并且在模块B中包含模块A将不会“重新加载”之前的所有内容B.
好的,它非常有意义,实际上重新包括Kernel将添加另外两种方法。

然后,我的问题是:

  1. 为什么开启 Kernel有效? (例1)
  2. 如果开放模块的处理方式不同,为什么第3个例子不能正常工作?

1 个答案:

答案 0 :(得分:1)

你在Ruby中调用foo.bar会发生什么?像这样:

foo.class.ancestors.each do |klass|
  if klass.public_instance_methods.include? :bar
    return klass.instance_method(:bar).bind(foo).call
  end
end
raise NameError

即。 Ruby搜索祖先以找到匹配的实例方法。

当你在Ruby中调用A.include B时会发生什么?像这样:

B.ancestors.each do |mod|
  A.ancestors << mod unless A.ancestors.include? mod
end

B及其所有祖先都成为A的祖先。这两种行为解释了一切:

  1. 打开Kernel是有效的,因为它包含在Object中,因此是每个对象的祖先,这意味着无论何时在任何对象上调用方法,都可以搜索其方法(包括新方法)。
  2. 打开模块的方式不同。你的第二个和第三个例子实际上是相同的。它们都不起作用,因为Kernel的祖先只在被包含时被搜索,这是在你向它添加新祖先之前搜索的。