Rails 6 с Zeitwerk не загружает класс из модуля
У меня есть файл, который выглядит так
#app/services/account/authenticate/base.rb
module Account
module Authenticate
AuthenticateError = Class.new(StandardError)
class Base < ::Account::Base
def self.call(*attrs)
raise NotImplementedError
end
end
end
end
Теперь, когда я буду запускать код из rails c
У меня ошибка
> ::Account::Authenticate::AuthenticateError
=> NameError (uninitialized constant Account::Authenticate::AuthenticateError)
> ::Account::Authenticate.constants
=> [:Base, :ViaToken]
Поэтому rails не видит класс AuthenticateError. Но когда я создам вложенный класс из этой папки, как
=> Account::Authenticate::ViaToken
> ::Account::Authenticate.constants
=> [:Base, :AuthenticateError, :ViaToken]
Класс AuthenticateError теперь виден
> ::Account::Authenticate::AuthenticateError
=> Account::Authenticate::AuthenticateError
Решением этой проблемы является создание отдельного файла authenticate_error.rb, который будет работать с самого начала, но это решение не идеально для меня. Есть ли решение для предварительной загрузки всех классов или что-то?
(Ruby 2.6 с Rails 6.0.0.rc2)
4 ответа
У меня возникла та же проблема при развертывании приложения Rails 6.0.2 на сервере Ubuntu 18.04.
Невозможно загрузить приложение: Zeitwerk::NameError: ожидался файл /home/deploy/myapp/app/models/concerns/designation.rb для определения обозначения константы, но не
Я выяснил, что проблема была в zeitwerk. Zeitwerk - это новый движок загрузчика кода, используемый в Rails 6. Он должен быть новым по умолчанию для всех проектов Rails 6+, заменяющих старый классический движок. Zeitwerk предоставляет функции автозагрузки, активной загрузки и перезагрузки кода.
Вот как я это решил:
Перейдите к config/application.rb
файл в вашем проекте.
Добавьте эту строку в свой модуль приложения, чтобы переключиться на classic
режим автозагрузки:
config.autoloader = :classic
Вот пример:
module MyApp
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
config.autoloader = :classic
end
end
Вы можете узнать больше о zeitwerk в этой статье: Понимание Zeitwerk в Rails 6
Вот и все.
надеюсь, это поможет
обновить приложение Rails с 5.2 до 6.0, а также попасть в Zeitwerk!
Если вы хотите продолжать использовать режим автозагрузки, который вы используете в настоящее время, избегая Zeitwerk, добавьте эту строку в свой файл application.rb (ответ @PromisePreston и документ Rails )
config.autoloader = :classic
Если вы хотите перейти на Zeitwerk, используйте следующую команду:
bin/rails zeitwerk:check
(Из этой справочной статьи ).
Сценарий, который мы ближе всего подошли к этому конкретному вопросу, заключался в том, что у нас был файл в такой подпапке:
#presenters/submission_files/base.rb
module Presenters
module SubmissionFiles
class Base < Showtime::Presenter
def method_call
#code_here
end
end
end
end
Удаление дополнительных модулей, чтобы иметь:
#presenters/submission_files/base.rb
module Presenters
class SubmissionFiles::Base < Showtime::Presenter
def method_call
#code_here
end
end
end
Затем при вызове метода в других рубиновых файлах приложения используйте:
Presenters::SubmissionFiles::Base.method_call
следить
conventional file structure
который может загружать классы и модули вашего проекта по запросу (автозагрузка), если вы следуете его правилу.
# service/account/authenticate/base.rb
module Account
module Authenticate
puts "load service ....."
AuthenticateError = Class.new(StandardError)
class Base
end
end
end
::Account::Authenticate::AuthenticateError # uninitialized constant
::Account::Authenticate::Base # load service ....
::Account::Authenticate::AuthenticateError # OK
как видите, при первой попытке достичь константы журнал
load service ...
не показывает, что из-за того, что вы не играете в правила:
всякий раз, когда он получает запрос на загрузку константы, сначала он проверяет и возвращает, если эта константа уже загружена, в противном случае он будет искать файл, соответствующий константе, чтобы найти это определение константы, но не может его найти.
на шаге 2, когда вы звоните
::Account::Authenticate::Base
, он может найти файл/account/authenticate/base.rb
и загрузите его, в это время он также загрузит константу, которая определена в этом файле, теперь у нас есть константа::Account::Authenticate::AuthenticateError
, и, конечно же, на шаге 3 все в порядке.
теперь давайте попробуем поиграться с правилом, я создаю файл
/account/authenticate/authenticate_error.rb
как показано ниже
# service/account/authenticate/authenticate_error.rb
module Account
module Authenticate
puts "load error ....."
AuthenticateError = Class.new(StandardError)
end
end
и попробуйте попробовать эту константу на шаге 1
$ spring stop
$ rails c
> ::Account::Authenticate::AuthenticateError
load error .....
=> Account::Authenticate::AuthenticateError
это работало с
zeitwerk
нашел файл
account/authenticate/authenticate_error.rb
. (обратите внимание, что имя файла
/____authenticate_error.rb
все еще работают)
моя мысль: я думаю, вы могли бы безопасно работать с константой внутри модуля
::Account::Authenticate
, в случае, если вы хотите выставить эти константы ошибок извне, вы можете создать файл
/account/authenticate/error.rb
# service/account/authenticate/error.rb
module Account
module Authenticate
module Error
AuthenticateError = Class.new(StandardError)
end
end
end
тогда вы могли бы получить доступ
::Account::Authenticate::Error::AuthenticateError
, на мой взгляд, это даже понятнее, чем поставить
AuthenticateError
внутри
base.rb
.
Я смог исправить это, не пытаясь бороться с Rails 6.
Zeitwerk
автоматически загружает определенные ожидаемые папки, включая приложения / модели, приложения / контроллеры, приложения / помощники и другие.
Я создал папку
app/helpers
и переместил мой
services
папку в него.
Вот и все!