Rails 6 Zeitwerk выгружает мой класс после инициализатора

Я реализую одноэлементный класс / модуль в приложении Rails 6 с помощью загрузчика Zeitwerk.

# 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

Класс Singleton инициализируется в

# 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?


person Max Ivak    schedule 09.04.2020    source источник


Ответы (1)


Я считаю, что проблема здесь в том, как Zeitwerk загружает ваш код, он сначала загружает Gems из вашего Gemfile, затем запускает инициализаторы, затем загружает код вашего приложения, поэтому попытка запустить Mynamespace::MyModel.client означает, что он должен остановить то, что он делает, и загрузить app/lib/mynamespace/mymodel.rb, чтобы загрузить эту константу, чтобы выполнить на ней client=.

Это также означает, что если вы измените код Mynamespace::MyModel, Rails не сможет выполнить горячую перезагрузку константы, потому что инициализаторы не запускаются повторно, что приводит к циклической блокировке зависимостей (вы когда-нибудь видели ошибку типа «модуль MyModel удален из дерева, но все еще активен! "или использовать require_dependency перед использованием некоторого кода, который должен быть загружен автоматически, но это не так?). Zeitwerk пытается исправить этот класс проблем.

Переместите этот код из config/initializers в config/application.rb, и он все равно будет запускаться при загрузке.

person Unixmonkey    schedule 09.04.2020
comment
после перемещения кода инициализации на config/application.rb выдает ошибку uninitialized constant Mynamespace::Mymodel и не загружается. - person Max Ivak; 10.04.2020
comment
он работает, если поместить мой код в блок after_initialize в config/application.rb: config.after_initialize do Mynamespace::Mymodel.client = "my_custom_value" end. Добавьте пример к своему ответу, и я приму его. - person Max Ivak; 10.04.2020
comment
Zeitwerk перезагрузит мой класс после изменений в режиме разработки, и одноэлементный класс не будет работать - значение, хранящееся в Mynamespace::Mymodel.client, сбрасывается на ноль после перезагрузки. - person Max Ivak; 10.04.2020
comment
Что касается неинициализированной константы, это означает, что вы должны require файл, содержащий определение модуля. Что вы имеете в виду под последним комментарием? Вы редактируете код в MyModel и ожидаете, что он сохранит значение, установленное во время инициализации? - person Unixmonkey; 10.04.2020
comment
Я редактирую код в классе приложения, который использует Mymodel class AnotherClass def bar client = Mynamespace.Mymodel.client end end. если я редактирую AnotherClass любым новым кодом в классе, Zeitwerk выгружает инициализированную Mymodel и Mymodel.client возвращает ноль. - person Max Ivak; 10.04.2020
comment
Интересно, может ли это помочь: stackoverflow.com/a/39498710/23915 По сути, удалите MyModel откуда-то автозагрузку Rails (возьмите его из /app и поместите в /lib каталог верхнего уровня или что-то в этом роде, или удалите его из путей автозагрузки с помощью чего-то вроде stackoverflow. com / a / 59422729/23915 показывает. - person Unixmonkey; 10.04.2020