为什么模块中的 ruby 类不能与常规类具有相同的范围?



我有一个模块,里面有一个类,但我发现如果不指定模块路径,里面的类就无法访问封闭模块中的任何方法。

从另一个角度来看,module_function似乎没有携带到类中。

示例:

module MyMod
def meaning
42
end
class Foo
def initialize
puts "New Foo"
puts "I can call #{MyMod.meaning} from here"
puts "But I can't get to #{meaning} from here"
end
end
def go
puts "I can get to #{meaning} from here"
bar = Foo.new
end
module_function :go, :meaning
go
end

如果你运行这个,你会在";但我不能"第行:

undefined local variable or method `meaning'

但是,如果删除整个文件周围的MyMod位,则访问外部方法没有问题。

有没有一种简单的方法可以在不必给出完整路径的情况下使这些内容变得可访问?

(我挑剔不包括完整路径的原因之一是因为我正在使用http://redshift.sourceforge.net/script/为了创建我可以实例化的ruby脚本,而且它们没有像"MyMod"这样清晰简单的名称,我可以直接添加到路径中,实际上我必须传递作用域,即使它只是类的封闭作用域)

我有一个模块,里面有一个类

不,你没有。您有一个模块定义,里面有一个类的定义会使该类成为嵌套类。Ruby没有嵌套类。

Ruby不是Beta、Scala或Newspeak,Ruby中没有嵌套类。

将模块或类定义嵌套在另一个模块或类定义内不会在两个类/模块之间创建嵌套关系。它只使成为引用外部类/模块命名空间的类/模块部分的常量

换句话说,之间没有区别

module Foo
class Bar
end
end

class Quux
end
module Foo
Bar = Quux
end

只有常量被嵌套,但没有被常量引用的对象。

但我发现里面的类在不指定模块路径的情况下无法访问封闭模块中的任何方法。

这正是因为不存在";封闭模块">。有一个词法封闭的模块定义,但Foo类和MyMod模块之间创建任何形式的关系。

另一种看待它的方法是module_function似乎没有携带到类中。

老实说,我不明白你说的是什么意思,一个方法";carry into a class";,但CCD_ 3不是魔术。它做的正是文档中所说的:它获取模块的一个实例方法,将其复制为模块的singleton类的实例方法,并使原始实例方法private

您可以在Ruby/Spec中阅读它的规范,它非常简单。此外,Rubinius源代码,用于引导Rubinius内核的基本版本和完整版本都是可读的。

最后,Module#module_function确实没有比做更多的事情

class Module
def module_function(*meths)
meths.each do |meth|
define_singleton_method(meth, &instance_method(meth).bind(self))
private meth
end
self
end
end

如果运行此程序,则会在";但我不能"第行:

undefined local variable or method `meaning'

原因很简单:类Foo及其任何超类都没有该名称的任何方法,所以当然会得到一个异常。

但如果删除整个文件周围的MyMod位,则访问外部方法没有问题。

没有;外部方法">。Ruby没有类似Beta的嵌套类。这确实是你误解的根本原因。你期望Ruby表现得像Beta,但事实并非如此。Ruby的灵感来自任何语言,最著名的是Smalltalk、Lisp、Perl和Clu,但Beta不在其中。

这是因为一个完全不同的原因:

def meaning
42
end
class Foo
def initialize
meaning
end
end

在顶层定义的方法被隐式定义为Object的私有实例方法。这是因为顶层的默认定义::Object。由于Foo继承自Object,因此方法查找最终会找到Object中定义的meaning方法。

有没有一种简单的方法可以在不提供完整路径的情况下访问这些内容?

继承。例如,Module#include调用的Module#append_features使模块成为包含类的超类,因此模块的所有实例方法都成为方法查找祖先链的一部分。

旁白:如果没有嵌套,那么Module::nesting会做什么?是的,这是一个不幸命名的方法。术语";嵌套类";或";嵌套模块";在OO中有一个定义明确的含义,一直追溯到Beta。但这种方法是关于一种完全不同的嵌套:

它指的是模块定义词法嵌套,而指的是模块本身的嵌套

例如,这些模块定义都定义了完全相同的模块,但定义text具有不同的嵌套:

module Foo
module Bar
module Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo::Bar, Foo]
end
end
end
end
module Foo
module Bar
module Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar, Foo]
end
end
end
module Foo
module Bar::Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo]
end
end
end
module Foo::Bar
module Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo::Bar]
end
end
end
module Foo
module Bar::Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo]
end
end
module Foo::Bar::Baz
module Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz]
end
end
module Foo::Bar
module Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux, Foo::Bar]
end
end
module Foo::Bar::Baz::Qux
p Module.nesting
#=> [Foo::Bar::Baz::Qux]
end

同样,这纯粹是模块定义词法嵌套。模块本身没有嵌套;在所有这些情况下,模块本身都是相同的。此嵌套仅影响常量查找

在封闭模块定义中,首先从词法上向外查找常量,然后在继承链中向上查询常量。

还有另一个可以嵌套的例子:创建嵌套的词法作用域,而所有其他词法作用域(脚本、模块/类定义和方法定义)都不嵌套。换句话说,块和仅的块可以访问其封闭词法范围的局部变量(和self)。

因为Ruby名称解析和方法查找的工作方式。当你在任何地方写x时,Ruby会按照以下顺序:

  1. 寻找x局部变量

  2. self.class中寻找x方法

  3. self.superclass中寻找x方法

  4. 重复步骤3,直到superclassnil

    Ruby将在类层次结构中向上走尝试找到方法x直到它到达CCD_ 26。

  5. 调用method_missing

    它的默认行为是引发您遇到的错误。

MyMod仅包含Foo,它不是Module#module_function0类层次结构的一部分。这就是找不到该方法的原因。

最新更新