Многие плагины содержат множество определений классов внутри plugin.rb или используют require_relative для загрузки файлов Ruby. Это работает, но имеет ряд недостатков:
- Отсутствие автоматической перезагрузки изменений в режиме разработки. Любые изменения требуют полной перезагрузки сервера.
- Правильная расстановка вызовов
requireможет быть болезненным процессом. - Если они вызываются через
requireвне блокаafter_initialize, то другие автоматически загружаемые классы/модули могут оказаться недоступными.
Есть решение! Плагины могут опираться на стандартную систему автозагрузки Rails. Для новых плагинов всё необходимое уже определено в шаблоне плагина. В этой теме описано, как адаптировать существующий плагин и расширить его конфигурацию.
1. Определите модуль и Rails::Engine для вашего плагина
В файле plugin.rb определите модуль для вашего плагина с уникальным именем PLUGIN_NAME и добавьте строку require_relative для загрузки файла движка, который мы создадим.
# name: my-plugin-name
# ...
module ::MyPluginModule
PLUGIN_NAME = "my-plugin-name"
end
require_relative "lib/my_plugin_module/engine"
Теперь создайте файл {plugin}/lib/my_plugin_module/engine.rb:
module ::MyPluginModule
class Engine < ::Rails::Engine
engine_name PLUGIN_NAME
isolate_namespace MyPluginModule
end
end
Важные замечания:
-
В файле
plugin.rbвы должны указать::перед именем модуля, чтобы определить его в корневом пространстве имён (иначе он будет определён внутриPlugin::Instance). -
Строка
require_relative "lib/.../engine"должна находиться в корне файлаplugin.rb, а не внутри блокаafter_initialize. -
Размещение движка в отдельном файле внутри папки
lib/крайне важно. Определение его непосредственно в файлеplugin.rbне сработает. (Rails использует наличие директорииlib/для определения корня движка). -
Путь к файлу должен включать имя модуля в соответствии с правилами Zeitwerk.
-
engine_nameиспользуется как префикс для задач rake и любых маршрутов, определяемых движком ([
документация Rails]). -
isolate_namespaceпомогает предотвратить утечку данных между ядром и плагином ([
документация Rails]).
2. Определите файлы Ruby в правильной структуре директорий
Теперь движок будет автоматически загружать все файлы в {plugin}/app/{type}/*. Например, мы можем определить контроллер:
{plugin}/app/controllers/my_plugin_module/examples_controller.rb
module ::MyPluginModule
class ExamplesController < ::ApplicationController
requires_plugin PLUGIN_NAME
def index
render json: { hello: "world" }
end
end
end
Теперь этот класс будет автоматически загружаться всякий раз, когда Rails пытается получить доступ к ::MyPluginModule::MyController. Чтобы проверить работу, попробуйте обратиться к этому классу из консоли Rails.
Для корректной работы автозагрузки пути к файлам должны соответствовать полной иерархии модулей/классов согласно правилам, определённым Zeitwerk.
3. Определение маршрутов для движка плагина
Создайте файл {plugin}/config/routes.rb:
MyPluginModule::Engine.routes.draw do
get "/examples" => "examples#index"
# определите маршруты здесь
end
Discourse::Application.routes.draw do
mount ::MyPluginModule::Engine, at: "my-plugin"
end
Этот файл будет автоматически загружен движком, и изменения вступят в силу без перезагрузки сервера. В данном случае действие контроллера будет доступно по адресу /my-plugin/examples.json.
4. Добавление дополнительных путей для автозагрузки
Иногда может потребоваться добавить дополнительные директории с автоматически загружаемыми файлами Ruby. Наиболее частый пример — директория lib/ в плагине.
Измените определение движка, добавив lib/ в пути автозагрузки движка:
class Engine < ::Rails::Engine
engine_name PLUGIN_NAME
isolate_namespace MyPluginModule
config.autoload_paths << File.join(config.root, "lib")
end
Теперь вы можете определить модуль lib, например:
{plugin}/lib/my_plugin_module/some_lib_module.rb
module ::MyPluginModule::SomeLibModule
end
Теперь любые ссылки на ::MyPluginModule::SomeLibModule будут автоматически загружать модуль из этого файла.
5. Прибыль!
Теперь все эти файлы будут автоматически загружаться без явных вызовов require. Изменения будут автоматически обнаруживаться Rails и перезагружаться на месте без перезагрузки сервера.
Этот документ находится под версионным контролем — предлагайте изменения на GitHub.