【问题标题】:Rails 6 Zeitwerk unloads my class after initializerRails 6 Zeitwerk 在初始化程序后卸载我的类
【发布时间】:2020-04-09 07:19:50
【问题描述】:

我正在使用 Zeitwerk 加载器在 Rails 6 应用程序中实现一个单例类/模块。

# app/lib/mynamespace/mymodel.rb

module Mynamespace
  module Mymodel
    class << self
      attr_accessor :client
    end

    def self.client
      @client ||= "default_value"
    end

    def self.client=(client)
      @client = client
    end
end

单例类在

中初始化
# config/initializers/mymodel.rb

Mynamespace::Mymodel.client = "my_custom_value"
# Mynamespace::Mymodel.client - this returns correct value

然后当我在控制器中使用单例类时

# app/controllers/mycontroller.rb

client = Mynamespace::Mymodel.client

它返回一个空对象,因为它没有被初始化:client == "default_value" 但应该是 "my_custom_value"。

日志显示错误

DEPRECATION WARNING: Initialization autoloaded the constants Mynamespace::Mymodel

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

如何在使用 Zeitwerk 时正确配置单例类?

【问题讨论】:

    标签: ruby-on-rails zeitwerk


    【解决方案1】:

    我认为这里的问题是 Zeitwerk 加载代码的方式,它首先从 Gemfile 加载 Gem,然后运行初始化程序,然后加载应用程序代码,因此尝试运行 Mynamespace::MyModel.client,意味着它必须停止它的运行执行并加载app/lib/mynamespace/mymodel.rb 以加载该常量,在其上执行client=

    这也意味着如果你更改Mynamespace::MyModel 代码,Rails 将无法热重载常量,因为初始化程序不会重新运行,引入循环依赖锁(你见过错误像“模块 MyModel 从树中删除但仍处于活动状态!”或必须使用 require_dependency 在使用一些应该自动加载但不是的代码之前?)。 Zeitwerk 试图解决这类问题。

    将该代码移出config/initializers,移入config/application.rb,它仍将在启动时运行。

    【讨论】:

    • 将初始化代码移动到config/application.rb 后,它会给出错误uninitialized constant Mynamespace::Mymodel 并且无法启动。
    • 如果将我的代码放在after_initialize 块中的config/application.rb 中,它会起作用:config.after_initialize do Mynamespace::Mymodel.client = "my_custom_value" end。在您的答案中添加一个示例,我会接受。
    • Zeitwerk 将在开发模式更改后重新加载我的类,并且单例类将不起作用 - 存储在 Mynamespace::Mymodel.client 中的值在重新加载后重置为 nil。
    • 关于未初始化的常量,这意味着您应该 require 保存模块定义的文件。你最后的评论是什么意思?您是否正在编辑 MyModel 中的代码,并希望它保持初始化期间设置的值?
    • 让我说 Zeitwerk 不会在 Gemfile 中加载 gem(Bundler 会),也不会运行初始化程序(Rails 会手动运行)。 Zeitwerk 只负责您应用程序中的 autoloadable 代码(想想app 下的内容)及其引擎依赖项(apps 下的内容)。
    【解决方案2】:

    这就是为什么在 Rails 7 中最终禁止引用可重载常量的原因,因为它没有意义,而且你会发现很难。

    这与 Zeitwerk 无关,它与重新加载自身的逻辑有关。

    TLDR:由于app/lib 中的代码是可重新加载的(这就是您将其放在那里的原因),因此您需要考虑重新加载时必须再次进行初始化。这是通过to_prepare 块完成的。请看https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#autoloading-when-the-application-boots

    另一方面,如果您不想重新加载该单例,那么您可以将其移动到顶级 lib 并在初始化程序中为其发出 require。 (假设 lib 不在自动加载路径中,默认情况下不在。)

    【讨论】:

      猜你喜欢
      • 2019-10-17
      • 2021-05-31
      • 1970-01-01
      • 2012-02-01
      • 1970-01-01
      • 2012-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多