Ruby 包括/扩展内核不';t在main:Object上添加这些方法

Ruby 包括/扩展内核不';t在main:Object上添加这些方法,ruby,Ruby,我试图在内核模块中添加一个方法,但不是重新打开内核并直接定义一个实例方法,而是编写一个模块,我希望内核扩展/包含该模块 module Talk def hello puts "hello there" end end module Kernel extend Talk end 当我在IRB中运行此命令时: $ hello NameError: undefined local variable or method `hello' for main:Object from (

我试图在
内核
模块中添加一个方法,但不是重新打开
内核
并直接定义一个实例方法,而是编写一个模块,我希望
内核
扩展/包含该模块

module Talk
  def hello
    puts "hello there"
  end
end

module Kernel
  extend Talk
end
当我在IRB中运行此命令时:

$ hello
NameError: undefined local variable or method `hello' for main:Object
from (irb):12
from /Users/JackC/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>
但是,如果我直接定义它:

module Kernel
  def hello
    puts "hello there"
  end
end
然后它会包含在
主对象中

$ hello
hello there
 => nil 
对象
中包含
通话
模块也可以:

class Object
  include Talk
end

也许我做错了,或者我遗漏了一些简单的东西,但这种行为让我困惑。

你想要的是
包括
,而不是
扩展

include
将模块的内容添加为实例方法(如正常打开类或模块时)
extend
将它们添加为类方法(因此在您的示例中,您可以调用
Kernel.hello
,尽管这不是您想要的)

您现在可能倾向于尝试以下方法:

module Talk
  def hello
    puts "hello there"
  end
end

module Kernel
  include Talk
end
但这也行不通,因为
main
已经被实例化,而
include
只是改变了
内核的祖先

您可以强制
main
在运行时包含模块,如下所示:

# (in global scope)
class << self
  include Talk
end
#(在全局范围内)

class这与其说是一个解决方案,不如说是一个解决方案,如果您不想在main上定义函数,那么它是合适的:

module Talk

  def self.extended(mod)
    mod.module_eval do
      def hello
        puts "hello there"
      end
    end
  end

end

module Kernel
  extend Talk
end

顺便说一句,我想知道为什么在这种情况下行为会有所不同。
module\u eval
不应该具有与
模块对话相同的效果;结束

我将尝试更深入地解释一下:

当您将
模块包含到某个类中时,Ruby会创建特殊的内部包含类并将其添加到层次结构中(请注意,基本上不允许您从Ruby programm中看到包含类,它是隐藏类):

如果包含的模块具有其他包含的模块,则也将为这些模块创建includeModules:

Given A inherits B
And we have a module C
And C includes module D
When A includes C
Then A inherits includeC inherits includeD inherits B
包含C类方法表是指向原始C类方法表的链接

当您用一个模块扩展某个对象时,该模块被包含在该对象的单例类中,因此:

class << self; include C; end
# is the same as
extend C
在这里,您将
Talk
模块包含到
Kernel
的单例类中(
Kernel
是类
module
的对象)。这就是为什么只能对内核对象调用
hello
方法:
Kernel.hello

如果我们这样写:

module Kernel
  include Talk 
end
然后,
Kernel
将在内部继承include类includeTalk(带有
Talk
方法链接的类)

但是内核模块已经包含在
对象中
-
对象
继承了自己的includeKernel类,includeKernel类链接到
内核
的方法表,并且没有看到
内核
的新include类的方法

但现在,如果将内核重新包含到对象中,所有对象都将看到对话方法:

> module Talk
>   def hi
>     puts 'hi'
>   end
> end
 => nil 
> module Kernel
>   include Talk
> end
 => Kernel 
> hi
NameError: undefined local variable or method `hi` for main:Object
        from (irb):9
        from /usr/share/ruby-rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>`
> class Object
>   include Kernel
> end
 => Object 
> hi
hi
 => nil  
希望这是你观察到的一点行为:)

更新

将尝试澄清您的问题:

我仍然有点困惑,为什么我必须在对象中重新包含内核。在里面 在不涉及主对象的情况下,我可以实例化一个对象 基于类,然后重新打开该类并包含 模块,该对象将在我的模块中看到方法。有 主对象包含内核的方式有什么不同吗?我是 也不确定“对象继承其自己的includeKernel”是什么意思 类和includeKernel类…”为什么它看不到新包含的 内核中的模块

您可以讲述将模块直接包含到对象类中的情况:

module M
  def hi
    puts 'hi'
  end
end

class C
end

c = C.new
c.hi # => UndefinedMethod

class C
  include M
end

c.hi # => hi
在这种情况下,您将拥有类
c
的对象
c
。类
C
继承
Object
(因为它是
Class
类的一个实例。C在他的单例类->然后在他的类C->然后在类C的父类中查找它的实例方法(在本例中为
Object
实例方法)。当我们将模块
M
包含到类
C
中时,那么
includeM
将是
C
的超类,如果
C
在其单例类和
C
类中找不到他的实例方法,它将在
includeM
中搜索实例方法,并有一个到方法表的链接属于
M
类(模块的实例)。因此当
c
搜索实例方法
hi
时,它会在
M
模块中找到它

但这与将模块
M
包含到
内核中时的情况不同。
在程序开始时
对象
类包含模块
内核
类对象;包含内核;结束
。这就是为什么我说
对象
继承自
包含内核
包含内核
有链接到
内核
的方法表,当您更改内核的方法表时,
包含内核
还将看到以下更改:

module Kernel
  def hi # add hi method to method table of Kernel
    puts 'hi'
  end
end

hi # => hi # any Object now see method hi
但是,当您将模块M包含到内核中时,内核的方法表不会改变。相反,内核现在将继承
includeM
include类。
includeKernel
没有看到
includeM
的方法,因为它不知道
Kernel
includeM
的继承链,它只知道met
内核的hod表


但当您将
内核
重新包含到
对象
中时,包含机制将看到
内核
包含
M
,并将为
对象
创建includeM。现在
对象
将继承
includeKernel
将继承
includeM
将继承
基本对象
/p>我也尝试了include,但我得到了相同的错误。这有点让人困惑,因为如果你看看Kernel.singleton_方法,你会发现
$ hello
hello there
 => nil 
extend Talk
module M
  def hi
    puts 'hi'
  end
end

class C
end

c = C.new
c.hi # => UndefinedMethod

class C
  include M
end

c.hi # => hi
module Kernel
  def hi # add hi method to method table of Kernel
    puts 'hi'
  end
end

hi # => hi # any Object now see method hi