尽管在该文件中定义了Rails,但无法从文件中自动加载常量

Pet*_* P. 18 ruby ruby-on-rails autoload

这是一个难以解释的问题.我在另一个模块名称空间中有一个模块,如下所示

# app/models/points/calculator.rb
module Points
  module Calculator
    def self.included(base)
      base.send(:include, CommonMethods)
      base.send(:include, "Points::Calculator::#{base}Methods".constantize)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

那么在其他课程中,我需要做的就是:

class User
  include Points::Calculator
end
Run Code Online (Sandbox Code Playgroud)

我已经在application.rb中指定了这个目录可以自动加载...(尽管我认为rails通过模型进行递归...)

config.autoload_paths += Dir[ Rails.root.join('app', 'models', "points") ]
Run Code Online (Sandbox Code Playgroud)

在开发环境中,一切正常.运行测试(和生产环境)时,我收到以下错误:

Unable to autoload constant Points::Calculator, expected /Users/pete/work/recognize/app/models/points/calculator.rb to define it (LoadError)
Run Code Online (Sandbox Code Playgroud)

我实际上遵循了这里的建议来解决问题:通过在application.rb中明确要求calculator.rb,阻止Rails在开发模式下卸载模块.

但是,为什么会发生这种情况?

我在ActiveSupport的dependencies.rb文件中粘贴了一些调试输出,并注意到这个文件需要两次.第一次需要我可以看到常量确实已加载.

但是第二次它需要的常量已被卸载,就Rails而言,但是当调用实际的require时,ruby返回false,因为ruby知道它已经需要它.然后Rails抛出"无法自动加载常量"错误,因为常量仍然不存在,并且ruby没有"重新要求"该文件.

任何人都可以阐明为什么会发生这种情况?

MId*_*hna 8

Rails增强了ruby的常量查找机制.

Ruby中的常量查找:

类似于method missing,Module#constant-missing当对常量的引用无法解析时,会调用a.当我们在给定的词法范围中引用常量时,会在以下位置搜索该常量:

Each entry in Module.nesting 
Each entry in Module.nesting.first.ancestors
Each entry in Object.ancestors if Module.nesting.first is nil or a module.
Run Code Online (Sandbox Code Playgroud)

当我们引用一个常量时,Ruby首先尝试根据这个内置的查找规则找到它.

ruby无法找到... rails并使用its own lookup convention它并且知道已经加载了哪些常量(通过ruby)时,Rails会覆盖Module#const_missing加载缺少的常量,而不需要程序员明确的require调用.

它自己的查找约定?

对比Ruby的自动加载(需要事先指定每个自动加载常量的位置),遵循将常量映射到文件名的约定.

Points::Calculator # =>points/calculator.rb
Run Code Online (Sandbox Code Playgroud)

现在对于常量Points :: Calculator,rails在autoload_paths配置定义的自动加载路径中搜索此文件路径(即'points/calculator.rb').

在这种情况下,rails points/calculator在其自动加载的路径中搜索文件路径,但无法找到文件,因此显示此错误/警告.

这个答案是Urbanautomation博客的摘要.

  • 的确,我看过这篇文章并感谢您的解释.但是,该文件存在并正确加载一次,但如上所述,我可以看到它需要两次,并且在第二次运行时,常量已被卸载.我不清楚你的解释是如何解决这个问题的. (7认同)

inf*_*sed 0

Calculator应该是一个可以正确自动加载的

module Points
  class Calculator
  ...
  end
end
Run Code Online (Sandbox Code Playgroud)