繁体   English   中英

rails autoload_paths中的错误?

[英]Bug in 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

常量查找

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

如果失败,ruby会查看封闭类/模块的继承层次结构(例如,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日志重复第一个案例,您会看到记录的限定名称现在只是::ClassTwo const_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的弱点迫使使用脆弱的变通方法。

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

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

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

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

它工作正常。


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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM