Rails无法在文件中定义常量但无法从文件中自动加载常量 [英] Rails unable to autoload constant from file despite being defined in that file

查看:101
本文介绍了Rails无法在文件中定义常量但无法从文件中自动加载常量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个棘手的解释.我在另一个模块名称空间中有一个模块,如下所示:

This is a tricky one to explain. I have a module in another module namespace like so:

# 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

因此,在其他课程中,我要做的只是:

So then in other classes all I need to do is:

class User
  include Points::Calculator
end

我已在application.rb中将此目录指定为可自动加载...(即使我认为Rails会通过模型递归...)

I've specified this directory in application.rb to be autoloadable...(even though i think rails recurses through models...)

config.autoload_paths += Dir[ Rails.root.join('app', 'models', "points") ]

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

In development env, everything works fine. When running tests(and production env), I get the following error:

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

我实际上按照此处的建议来解决此问题:通过在application.rb中明确要求Calculator.rb,阻止Rails在开发模式下卸载模块.

I actually followed the advice here to fix the problem: Stop Rails from unloading a module in development mode by explicitly requiring calculator.rb in application.rb.

但是,为什么会这样?

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

I stuck some debug output in ActiveSupport's dependencies.rb file and noticed that this file is being required twice. The first time its required I can see that the constant is indeed loaded.

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

But the 2nd time its required the constant has been unloaded as far as Rails can tell, but when the actual require is called, ruby returns false because ruby knows its already required it. Then Rails throws the "unable to autoload constant" error because the constant still isn't present and ruby didn't "re-require" the file.

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

Can anyone shed light on why this might be happening?

推荐答案

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

Rails augments the constant lookup mechanism of ruby.

在Ruby中进行恒定查找:

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

Similar to method missing, a Module#constant-missing is invoked when a reference to a constant fails to be resolved. When we refer to a constant in a given lexical scope, that constant is searched for in:

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首先尝试根据此内置查找规则找到它.

When we refer to a constant, Ruby first attempts to find it according to this built-in lookup rules.

ruby​​找不到... rails插入,并使用 its own lookup convention 及其有关已加载(由ruby加载)哪些常量的知识时,Rails覆盖Module#const_missing以加载缺少的常量,而无需程序员进行显式的require调用.

When ruby fails to find... rails kicks in, and using its own lookup convention and its knowledge about which constants have already been loaded (by ruby), Rails overrides Module#const_missing to load missing constants without the need for explicit require calls by the programmer.

它自己的查找约定?

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

Contrasting Ruby’s autoload (which requires the location of each autoloaded constant to be specified in advance) rails following a convention that maps constants to file names.

Points::Calculator # =>points/calculator.rb

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

Now for the constant Points::Calculator, rails searches this file path (ie 'points/calculator.rb') within the autoload paths, defined by the autoload_paths configuration.

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

In this case, rails searched for file path points/calculator in its autoloaded paths, but fails to find file and hence this error/warning is shown.

此答案是来自城市自动化博客.

我写了一篇关于Zeitwerk的博客,Zietwerk是Rails中的新代码重新加载器.在->

I wrote a blog about Zeitwerk, the new code reloader in Rails. Check it out at -> https://blog.bigbinary.com/2019/10/08/rails-6-introduces-new-code-loader-called-zeitwerk.html

这篇关于Rails无法在文件中定义常量但无法从文件中自动加载常量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆