Пространство имен сервисных объектов в Rails 6 с автозагрузчиком Zeitwerk

Rails 6 переключился на Zeitwerk в качестве автозагрузчика по умолчанию. Zeitwerk загрузит все файлы в папку /app, устраняя необходимость в использовании пространств имен. Это означает, что объект службы TestService в app/services/demo/test_service.rb теперь может быть вызван напрямую, напримерTestService.new().call.

Тем не менее, пространство имен было полезно для организации объектов в более сложных приложениях rails, например API::UsersController, или для служб, которые мы используем Registration::CreateAccount, Registration::AddDemoData и т. Д.

Одно из решений, предложенное в руководстве по рельсам, - удалить путь из пути автозагрузчика в application.rb, напримерconfig.autoload_paths -= Dir["#{config.root}/app/services/demo/"]. Тем не менее, это похоже на обезьяний патч, позволяющий перенести старый способ или организовать объекты по новому.

Каков правильный способ размещения объектов в пространстве имен или способ организации рельсов 6 без использования рельсов по-старому?

2 ответа

Решение

Неверно сказать, что Zeitwerk устраняет "необходимость в пространстве имен". Zeitwerk действительно автоматически загружает все подкаталогиapp (Кроме assets, javascripts, а также views). Любые каталоги подappзагружаются в "корневое" пространство имен. Но Zeitwerk также "оживляет" модули для любых каталогов, находящихся в этих корнях. Так:

/models/foo.rb => Foo
/services/bar.rb => Bar
/services/registration/add_demo_data.rb => Registration::AddDemoData

Если вы уже привыкли загружать константы из "нестандартных" каталогов (добавляя в config.autoload_paths) обычно не так много изменений. Тем не менее, есть несколько случаев, которые требуют небольшой настройки. Во-первых, вы переносите проект, который просто добавляетappсам в путь автозагрузки. В классической версии (до Rails 6) это позволяет использоватьapp/api/base.rb содержать API::Base, тогда как в Zeitwerk ожидается, что он будет содержать только Base. Это тот случай, о котором вы упомянули выше, когда рекомендуется исключить этот каталог из пути автозагрузки. Другой альтернативой было бы просто добавить каталог-оболочку, напримерapp/api/api/base.rb.

Второй момент, на который следует обратить внимание, это то, как Zeitwerk выводит константы из имен файлов. Из руководства по миграции Rails:

classicрежим выводит имена файлов из отсутствующих имен констант (подчеркивание), тогда как режим zeitwerk выводит имена констант из имен файлов (camelize). Эти помощники не всегда противоположны друг другу, особенно если используются акронимы. Например,"FOO".underscoreявляется "foo", но "foo".camelize является "Foo"не "FOO".

Так, /api/api/base.rb фактически приравнивается к Api::Base в Цайтверке, а не API::Base.

Zeitwerk включает в себя задачу rake для проверки автозагрузки в проекте:

% bin/rails Zeitwerk:check
Hold on, I am eager loading the application.
expected file app/api/base.rb to define constant Base

Я отправляю отдельный ответ, но на самом деле принятый ответ содержит всю полезную информацию. Поскольку мой комментарий был больше, чем разрешено, я решил добавить отдельный ответ для тех, кто борется с аналогичной проблемой.

Мы создали «компоненты» в приложении, в которых мы разделяем доменные пространства имен / пакеты. Они сосуществуют с некоторыми "некомпонентными" частями Rails, которые трудно переместить под компоненты. С классическим автозагрузчиком мы добавили #{config.root}/app в наших autoload_paths.

Эта настройка не подходит для Zeitwerk и удаление "#{config.root}/app"from autoload_paths не помогло. rmlockerd предложение переместить app/api/ под /app/api/api побудило меня задуматься о создании отдельного 'app/components' и переместите все компоненты в этот каталог и добавьте этот путь в autoload_paths. Zeitwerk это понравилось.

Другие вопросы по тегам