rails autoload_paths中的错误?

我在代码中遇到了一个奇怪的错误。 我有一个rails应用程序,在lib中有以下两个文件:

LIB / module_one / module_two / class_one.rb

module ModuleOne module Moduletwo class ClassOne class << self def test puts 'Class one' ClassTwo.test end end end end end 

LIB / module_one / module_two / class_two.rb

 module ModuleOne module ModuleTwo class ClassTwo def self.test puts 'Class two' end end end end 

现在我的问题是,当我进入控制台并写道:

 ModuleOne::ModuleTwo::ClassOne.test 

它抛出以下内容: NameError: uninitialized constant ClassTwo

奇怪的是,这个问题似乎与使用class << self而不是self.method 。 如果我像这样更改class_one.rb文件就可以了!

 module ModuleOne module ModuleTwo class ClassOne def self.test puts 'Class one' ClassTwo.test end end end end 

我在application.rb中加载文件,如下所示:

 config.autoload_paths += %W(#{config.root}/lib) 

这是rails中的一个错误,还是只是我弄错了?

我使用rails 3.1.3 btw

(只是部分答案,但需要格式化。)

这是因为class << self运作”。

例如,如果您将其更改为:

 class << self def test self::ClassTwo.test end end 

它工作正常。


编辑; 太长时间没有合理的评论。

我正在喋喋不休......在一个直观的层面上它对我有意义,我只是不确定为什么。 不知道我是否知道一次真正的理由,或者我是否只是在弥补它。

我不确定为什么self似乎会参考模块; “编程Ruby 1.9”一书并没有深入研究class <<语义学”。 我会发推特,并参考这个问题,聪明的人会创造一个真正的答案。

常量查找

首先,恒定解析的基本过程是ruby首先在ClassTwo搜索接收器的词法范围(包含引用的类或模块),当它找不到它时,它会上升一个级别( Module.nesting返回此搜索路径)等等。 在你的情况下,这意味着寻找ModuleOne::ModuleTwo::ClassOne:ClassTwo ,然后是ModuleOne::ModuleTwo::ClassTwo ,然后是ModuleOne::ClassTwo等等。

如果失败,ruby会在封闭类/模块的inheritance层次结构中查找(例如,ClassOne的超类定义了一些东西。模块上的ancestors方法返回此搜索路径。最后,搜索顶层常量。

Rails自动加载

回到rails的神奇装载。 这里有一个const_missing钩子被rails添加,当ruby找不到类时调用它,它基本上试图复制这个搜索逻辑,在每一步看到是否可以加载一个包含缺失常量的文件。

理想情况下,ruby会通过搜索路径(即嵌套)来搜索,但不幸的是它没有:当你引用ClassTwoconst_missing只用’ClassTwo’调用。

Rails通过在其上调用const_missing的类的名称(即包含对常量的访问的类)前面来猜测嵌套。 例如,在你的第二个例子中,它以ModuleOne::ModuleTwo::ClassOne::ClassTwo 。 通过定义const_missing来记录它所调用的内容,你可以很容易地看到这一点

 class Object def self.const_missing missing_name puts "qualified name is #{self.name}::#{missing_name}" super end end 

Rails然后剥离’ClassOne’并尝试ModuleOne::ModuleTwo::ClassTwo等等。

那么为什么class << self有所作为呢? 如果您使用const_missing日志重复第一个案例,您会看到记录的限定名称现在只是::ClassTwoconst_missing现在正在ClassOne的元类上调用,并且因为class << self尚未被赋值给常量,所以它没有名称,因此rails'试图捏造嵌套不起作用。

这为一个可怕的解决方法打开了大门:

 module ModuleOne module ModuleTwo class ClassOne class << self def test puts 'Class one' ClassTwo.test end end FOO = class << self; self; end end end end 

因为现在调用const_missing的类有一个名称(ModuleOne :: ModuleTwo :: ClassOne :: FOO)rails'的解决方法现在可以使用了。

我认为Dave的解决方法是有效的,因为const_missing在ModuleOne::ModuleTwo::ClassOne而不是匿名的eigenclass / metaclass上调用。

真正的解决方法是让ruby传递const_missing一个嵌套。 虽然已经打开了很长时间,但是有一个针对ruby记录的错误 。 所以是的,这可能被认为是魔法加载中的一个错误(还有其他边缘情况),但潜在的原因是ruby api的弱点迫使使用脆弱的变通方法。