多くのプラグインでは、plugin.rb内に多数のクラス定義を含めたり、require_relativeを使用してRubyファイルをロードしたりします。これは機能しますが、いくつかの欠点があります。
- 開発中の変更が自動リロードされない。変更のたびにサーバーの完全な再起動が必要になる
require呼び出しを正しい順序に設定するのが面倒になることがあるafter_initializeブロックの外でrequireされると、他のautoloadedクラス/モジュールが利用できない可能性がある
解決策があります!プラグインは標準のRailsの自動ロードシステムを利用できます。新しいプラグインの場合、必要なものはすべてplugin-skeletonで定義されています。このトピックでは、既存のプラグインを適応させ、設定を拡張する方法について説明します。
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"は、after_initializeブロック内ではなく、plugin.rbファイルのルートに配置する必要があります。 -
lib/の下の独自のファイルにエンジンを配置することが重要です。plugin.rbファイルで直接定義すると機能しません。(Railsはlib/ディレクトリの存在を使用してエンジンのルートを決定します) -
ファイルパスは、Zeitwerkのルールに従ってモジュール名を含める必要があります。
-
engine_nameは、エンジンによって定義されたrakeタスクおよび任意のルートのプレフィックスとして使用されます(
rails docs)。 -
isolate_namespaceは、コアとプラグイン間での漏洩を防ぐのに役立ちます(
Rails docs)。
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"
# define routes here
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で行ってください。