这是一个难以解释的问题。我在另一个模块名称空间中有一个模块,如下所示:
# 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
那么在其他课程中,我需要做的是:
class User
include Points::Calculator
end
我已经在application.rb中指定了这个目录可以自动加载...(尽管我认为rails通过模型进行递归...)
config.autoload_paths += Dir[ Rails.root.join('app', 'models', "points") ]
在开发环境中,一切正常。运行测试(和生产环境)时,我收到以下错误:
Unable to autoload constant Points::Calculator, expected /Users/pete/work/recognize/app/models/points/calculator.rb to define it (LoadError)
我实际上遵循了这里的建议来解决问题:Stop Rails from unloading a module in development mode通过在application.rb中明确要求calculator.rb。
但是,为什么会发生这种情况?
我在ActiveSupport的dependencies.rb文件中粘贴了一些调试输出,发现这个文件需要两次。第一次需要我可以看到常量确实已加载。
但是第二次需要将常量卸载到Rails可以告诉,但是当调用实际require时,ruby返回false,因为ruby知道它已经需要它。然后Rails抛出“无法自动加载常量”错误,因为常量仍然不存在,并且ruby没有“重新要求”该文件。
有人能说清楚为什么会这样吗?
答案 0 :(得分:7)
Rails增强了ruby的常量查找机制。
Ruby中的常量查找:
与method missing
类似,当对常量的引用无法解析时,将调用Module#constant-missing
。当我们在给定的词法范围中引用常量时,在:
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.
当我们引用常量时,Ruby首先尝试根据此内置查找规则找到它。
ruby无法找到... rails ,并使用 its own lookup convention
以及有关已加载哪些常量的知识(通过ruby) ,Rails重写Module#const_missing
以加载缺少的常量,而无需程序员明确的require调用。
自己的查找惯例?
对比Ruby的自动加载(需要事先指定每个自动加载常量的位置),遵循将常量映射到文件名的约定。
Points::Calculator # =>points/calculator.rb
现在对于常量Points :: Calculator,rails在autoload_paths
配置定义的自动加载路径中搜索此文件路径(即'points / calculator.rb')。
在这种情况下,rails在其自动加载的路径中搜索文件路径points/calculator
,但无法找到文件,因此显示此错误/警告。
这个答案是这个Urbanautomation blog的摘要。
答案 1 :(得分:0)
Calculator
应该是类才能正确自动加载
module Points
class Calculator
...
end
end
答案 2 :(得分:-1)
如果有人在具有zeitwerk
自动加载器的rails 6中遇到此问题,
在您的application.rb
中将ruby常量查询更改回经典
# config/application.rb
#...
config.autoloader = :classic
#...
了解更多详细信息