0

我正在使用 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 时如何正确配置单例类?

4

2 回答 2

1

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

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

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

于 2020-04-09T15:54:23.510 回答
0

This is why referring reloadable constants has been finally forbidden in Rails 7, because it doesn't make sense and you find the hard way.

This is unrelated to Zeitwerk, it is related to logic about reloading itself.

TLDR: Since code in app/lib is reloadable (that is why you put it there), you need to consider that on reload the initialization has to happen again. That is accomplished with a to_prepare block. Please have a look at https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#autoloading-when-the-application-boots.

On the other hand, if you are good about not reloading that singleton, then you can move it to the top-level lib and issue a require for it in the initializers. (Assuming that lib is not in the autoload paths, which is not by default.)

于 2022-02-17T21:42:05.663 回答