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