简体   繁体   English

rails autoload_paths中的错误?

[英]Bug in rails autoload_paths?

I am encountering a strange bug in my code. 我在代码中遇到了一个奇怪的错误。 I have a rails application with the following two files in the lib: 我有一个rails应用程序,在lib中有以下两个文件:

lib/module_one/module_two/class_one.rb 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

and

lib/module_one/module_two/class_two.rb LIB / module_one / module_two / class_two.rb

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

Now my problem is, that when I go into the console and write: 现在我的问题是,当我进入控制台并写道:

ModuleOne::ModuleTwo::ClassOne.test

it throws the following: NameError: uninitialized constant ClassTwo 它抛出以下内容: NameError: uninitialized constant ClassTwo

The strange thing is, that the problem seems to be connected to the use of class << self instead of self.method . 奇怪的是,这个问题似乎与使用class << self而不是self.method If I change the class_one.rb file like this it works!: 如果我像这样更改class_one.rb文件就可以了!

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

Im loading the files in application.rb like this: 我在application.rb中加载文件,如下所示:

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

Is this a bug in rails, or is it just me getting something all wrong? 这是rails中的一个错误,还是只是我弄错了?

Im using rails 3.1.3 btw 我使用rails 3.1.3 btw

Constant lookup 常量查找

So first off, the basic process for constant resolution is that ruby first searches the lexical scope of the receiver (the class or module enclosing the reference) for ClassTwo and when it can't find it, it goes up a level ( Module.nesting returns this search path) and so on. 首先,恒定解析的基本过程是ruby首先在ClassTwo搜索接收器的词法范围(包含引用的类或模块),当它找不到它时,它会上升一个级别( Module.nesting返回此搜索路径)等等。 In your case that means looking for ModuleOne::ModuleTwo::ClassOne:ClassTwo , then ModuleOne::ModuleTwo::ClassTwo , then ModuleOne::ClassTwo and so on. 在你的情况下,这意味着寻找ModuleOne::ModuleTwo::ClassOne:ClassTwo ,然后是ModuleOne::ModuleTwo::ClassTwo ,然后是ModuleOne::ClassTwo等等。

If that fails the ruby looks in the inheritance hierarchy of the enclosing class/module (eg has the superclass of ClassOne defined something. The ancestors method on module returns this search path. Lastly, the top level constants are searched. 如果失败,ruby会查看封闭类/模块的继承层次结构(例如,ClassOne的超类定义了一些东西。模块上的ancestors方法返回此搜索路径。最后,搜索顶层常量。

Rails autoloading Rails自动加载

Back to rails's magic loading. 回到rails的神奇装载。 Here a const_missing hook is added by rails that is called when ruby can't find the class, which basically tries to replicate this search logic, seeing at each step whether a file could have been loaded which would contain the missing constant. 这里有一个const_missing挂钩由rails添加,当ruby找不到类时调用它,它基本上试图复制这个搜索逻辑,在每一步看到是否可以加载一个包含缺失常量的文件。

Ideally ruby would pass the search path (ie the nesting) to search through, but unfortunately it doesn't: when you reference ClassTwo , const_missing gets called with just 'ClassTwo'. 理想情况下,ruby会通过搜索路径(即嵌套)来搜索,但不幸的是它没有:当你引用ClassTwoconst_missing只用'ClassTwo'调用。

Rails guesses the nesting by prepending that with the name of the class on which const_missing is being called (ie the class enclosing the access to the constant). Rails通过在其上调用const_missing的类的名称(即包含对常量的访问的类)前面来猜测嵌套。 For example, in your second example it ends up with ModuleOne::ModuleTwo::ClassOne::ClassTwo . 例如,在你的第二个例子中,它以ModuleOne::ModuleTwo::ClassOne::ClassTwo You can see this easily enough by defining const_missing to log what it's called with 通过定义const_missing来记录它的调用内容,你可以很容易地看到这一点

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

Rails then strips off 'ClassOne' and tries ModuleOne::ModuleTwo::ClassTwo and so on up the chain. 然后Rails剥离'ClassOne'并尝试ModuleOne::ModuleTwo::ClassTwo ,依此类推。

So why does class << self make a difference? 那么为什么class << self有所作为呢? If you repeat your first case with the const_missing logging you'd see that the logged qualified name is now just ::ClassTwo . 如果您使用const_missing日志重复第一个案例,您会看到记录的限定名称现在只是::ClassTwo const_missing is now being called on ClassOne's metaclass, and because class << self hasn't been assigned to a constant, it has no name and thus rails' attempt to fudge the nesting doesn't work. const_missing现在在ClassOne的元类上被调用,并且因为class << self尚未被赋值给常量,所以它没有名称,因此rails'试图捏造嵌套不起作用。

This opens the door to a horrible workaround: 这为一个可怕的解决方法打开了大门:

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

Because the class that const_missing gets called on now has a name (ModuleOne::ModuleTwo::ClassOne::FOO) rails' workaround now works. 因为现在调用const_missing的类有一个名称(ModuleOne :: ModuleTwo :: ClassOne :: FOO)rails'的解决方法现在可以使用了。

Dave's workaround works I think because const_missing gets called on ModuleOne::ModuleTwo::ClassOne rather than the anonymous eigenclass/metaclass. 我认为Dave的解决方法是有效的,因为const_missing在ModuleOne::ModuleTwo::ClassOne而不是匿名的eigenclass / metaclass。

The real fix would be for ruby to pass const_missing a nesting. 真正的修复方法是让ruby传递const_missing一个嵌套。 There is a bug logged against ruby to this effect although it has been open for a long time. 虽然已经打开了很长时间,但是有一个针对ruby记录的错误 So yes, this could be considered a bug in the magic loading stuff (there are other edge cases) but the underlying reason is a weakness in the ruby api that forces the use of brittle workarounds. 所以是的,这可能被认为是魔法加载中的一个错误(还有其他边缘情况),但潜在的原因是ruby api的弱点迫使使用脆弱的变通方法。

(Only a partial answer, but need formatting.) (只是部分答案,但需要格式化。)

It's because of how class << self works. 这是因为class << self运作”。

For example, if you change it to: 例如,如果您将其更改为:

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

it works fine. 它工作正常。


Edit; 编辑; too long for reasonable comment. 太长时间没有合理的评论。

I'm poking around a bit... On an intuitive level it makes sense to me, I'm just not sure why yet. 我正在喋喋不休......在直观的层面上,这对我来说很有意义,我只是不确定为什么。 Don't know if I knew a real reason once, or if I'm just making it up. 不知道我是否知道一次真正的理由,或者我是否只是在弥补它。

I'm not sure why self seems to refer to the module, though; 我不确定为什么self似乎会参考模块; the "Programming Ruby 1.9" book doesn't go in to enough depth on the class << semantics. “Programming Ruby 1.9”一书并没有深入研究class <<语义学。 I'll tweet something and refer to this question and someone smarter will create a real answer. 我会发推特,并参考这个问题,聪明的人会创造一个真正的答案。

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

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