如何使用 Rails 6/Zeitwerk 在 rails 初始化程序中预加载关注点?

How can I preload concerns in a rails initializer using Rails 6/Zeitwerk?

我正在使用一个初始化程序,它通过将一些应用程序问题包含到第三方库中来对应用程序启动进行一些猴子修补。基本上:

# config/initializers/my_initializer.rb

class SomeExternalLib
  include MyConcern1
  include MyConcern2
end

这在 Rails 5.2.3 中工作正常,但在升级到 Rails 6 时我收到以下弃用消息:

DEPRECATION WARNING: Initialization autoloaded the constants MyConcern1, and MyConcern2.

Being able to do this is deprecated. Autoloading during initialization is going to be an error condition in future versions of Rails.

Reloading does not reboot the application, and therefore code executed during initialization does not run again. So, if you reload ApplicationHelper, for example, the expected changes won't be reflected in that stale Module object.

These autoloaded constants have been unloaded.

Please, check the "Autoloading and Reloading Constants" guide for solutions. (called from at /Users/myuser/code/myapp/config/environment.rb:7)

我的顾虑在 app/controllers/concerns/。经过一些调查,我发现该路径没有被自动加载,但我不知道如何让 Zeitwerk(Rails 6 的新自动加载器)动态加载它。我尝试遵循 here 描述的 STI 自动加载模式,但没有成功。知道如何解决这个弃用警告吗?

如果我更仔细地阅读错误消息会有帮助:

Autoloading during initialization is going to be an error condition in future versions of Rails.

更改的讨论是 here and guide is here

简而言之,自动加载不应该在初始化器中完成,这将被逐步淘汰。解决方案是 1) 不要在初始化器中使用需要自动加载的东西(显然是首选),或者 2) 在初始化器中明确要求依赖项。

所以我会这样做:

# config/initializers/my_initializer.rb

require 'my_concern1'
require 'my_concern2'

class SomeExternalLib
  include MyConcern1
  include MyConcern2
end

如@Glyoko 的回答所述,对依赖项使用 require 可防止在初始化程序中自动加载。但是,正如@Puhlze 在他的评论中提到的那样,这样做会导致重新加载时出现问题。

我偶然发现了一种在 this post 中使用 Rails.configuration.to_prepare 的替代方法。

例如:

# config/initializers/my_initializer.rb

Rails.configuration.to_prepare do
  class SomeExternalLib
    include MyConcern1
    include MyConcern2
  end
end

请注意,这会在开发中的每个请求之前运行,但只会在生产中的预加载之前运行一次。

编辑:它似乎也适用于重新加载。