DiscourseプラグインとRails 6のconfig/initializersに関する質問

Discourse プラグインのエキスパートの皆様、

Discourse プラグインと Rails のイニシャライザについて、簡単な質問があります。

Discourse プラグインに「config」というディレクトリがあり、その下に「initializers」というサブディレクトリがある場合、Discourse の Rails アプリは、Rails 6 のように「initializers」ディレクトリ下のすべてのプラグイン用イニシャライザファイルを読み取るのでしょうか?

私がこの質問をした理由は、現在、クライアント向けに「バックオフィス」用の Rails 6 アプリケーション(Rails のみで、EmberJS や他の JS フレームワークは上層に使用しない)をゼロから構築しているからです。その中で、以下のようなイニシャライザのサブディレクトリを作成しています。

./config/initializers/client/

……そして、そのクライアント固有のすべてのイニシャライザを「client」というサブディレクトリに配置しています。

Rails 6 は標準的なイニシャライザディレクトリ(サブディレクトリを含む)下のすべてのファイルを読み取るため、Discourse プラグインでも同様のディレクトリ構造でイニシャライザを配置した場合、以下のように動作し、プラグイン内のすべてのイニシャライザを Rails 6 と同様に読み取るのか気になっています。

./plugins/my_plugin/config/initializers/myclient/
      client_initializer1.rb
      client_initializer2.rb
      client_initializer2.rb

……これを plugin.rb ファイルに登録することなく動作させることは可能でしょうか?

ありがとうございます!

追伸:GitHub で約 10 件の Discourse プラグインを確認しましたが、config ディレクトリ下にイニシャライザを配置しているものは一つもありませんでした。そのため、この質問を投稿することにしました(また、現在の私の Rails 開発環境は Discourse 用に設定されておらず、すべてクライアント用に設定されています)。

「いいね!」 2

Discourseプラグインと一般的なRailsエンジンとの違いについても知りたいです。ソースコードを見てみましたが、よくわかりませんでした。

Ruby on Rails Guides: Configuring Rails Applications をざっと見たところ、plugin.rb もそれとほぼ同じ役割を果たしていると思います。ある程度規模のあるプラグインの多くは、ロジックそのものではなく、ロジックへのエントリーポイントとしてこれを利用しています。

ええ、最近その Rails ガイドを何度も読みました。基本的に、Rails 6 の設定の仕組みには「まあまあ」慣れてきました(まだ学習中ですが、毎日少しずつ慣れてきています)。

Discourse プラグインでも同じ構造(イニシャライザ用)を使えるか、あるいは Rails が Rails 6 のようにサブディレクトリ内のイニシャライザを読み取るのか、それとも plugin.rb でプラグインのイニシャライザディレクトリを設定の「アセット」(より適切な言葉が見つからなかったので)として登録する必要があるのか、疑問に思っています。

昨日は Rails プラグインについて調べて、少し比較もしていました。

私の知る限り、Discourse が読み取る Ruby ファイルは plugin.rb のみです。JS ファイルと設定ファイルは読み取られます。すべての Ruby ファイルは plugin.rb で require する必要があります。

「いいね!」 3

私もそう思います。

必要な追加の Ruby ファイルは、plugin.rb から読み込みます。

以下は Follow プラグインからの例です。

「いいね!」 1

GitHub 上のいくつかの Discourse プラグインを読んでみると、私もそう感じています。

どうやら Discourse は plugin.rb だけを読み込み、初期化子など読み込みたい他の Ruby ファイルはすべて plugin.rb 内でロードする必要があるようです。

しかし、私は間違っていた可能性があり、Discourse プラグインと Rails の間に「より深い」統合があるかもしれないと期待していました。Rails 6 の素晴らしさに慣れっこになっているためです。

確認してくださりありがとうございます。

「いいね!」 1

plugin.rb にも初期化コードを require する方法があるべきだと思います。
plugin.rb で以下を試しましたか?

Rails.application.config.before_initialize do
  # 初期化コードをここに記述
end

はい、plugin.rb 内で初期化子を呼び出して読み込むことは問題ありません。

私がこの質問をした理由は、現在、ある企業のレガシーなバックオフィススクリプト(数十年にわたるもの)を Rails アプリに変換する、かなり大規模な Rails 6 アプリを開発しているためです。初期化子を追加する際、Rails では単に config/initializers の下にサブディレクトリを作成するだけでよく、これらのファイルを Rails に含めるためのコードを一切記述する必要なく、すべての初期化子ファイルがきれいに読み込まれます。

皆様のご回答、誠にありがとうございます。大変感謝しております!

「いいね!」 1

はい、Rails の機能 perks が Discourse プラグインに移植されるのをぜひ実現したいですね。個人的には、サーバーを再起動せずに Ruby ファイルをライブリロードできる機能が特に気に入っています。

「いいね!」 1

これは reloadable_patch のための機能です。Ruby クラスの変更を reloadable_patch で囲めば、ライブリロードが機能するはずです!

「いいね!」 1

開発中は、plugin.rb ファイル全体をこれで囲むことはできますか?より本格的な話として、これの適用範囲はどこまで拡張可能でしょうか?

また、新しいファイルを追加したり、yml ファイルを変更したりする場合は、サーバーの再起動が必要ですよね?

「いいね!」 2

すべての場合に reloadable_patch を行う必要はありません。プラグイン開発を容易にするために instance.rb で定義されている多くのメソッドは、内部で add_to_serializer のように reloadable_patch を使用しています。

理想的には、当社のプラグイン API は十分に優れており、異常なほどの reloadable_patch を行う必要はないはずです。

はい、その通りです。yml ファイルの変更について何か対策が取れないものか気になります。私自身、それが個人的に気になります。

「いいね!」 3

その通りですね。reloadable_patch がライブリロードのサポートを目的としているとは知りませんでした。Discourse が既に使用している場面以外にも、多くのユースケースが考えられます。例えば:

reloadable_patch do
 require 'x/y/z'
end

あるいはモナキーパッチなどです。

「いいね!」 1

そのような関数を削除または追加しても、その関数内部で reloadable_patch が呼び出されているため、依然として問題になると思います。

いずれにせよ、非常に役立ちます。

「いいね!」 1

ライブリロードによる YAML の変更を検証してみたところ、実際に機能することがわかりました。プラグインが reloadable_patch を適切に使用していない場合、このライブリロードが破綻する可能性があります。しかし、すべてのプラグインが安全にリロードできるようになれば、YAML の変更も同様にリロード可能になります。

「いいね!」 4

ただし、リロード可能なパッチを使用する正確な手順についてご説明いただければ、非常に助かります。

私は以下のように試してみましたが、あまりうまくいきませんでした(何か見落としているはずです)。

after_initialize do
    add_to_serializer(:topic_view, :check, false) do
        puts 'nocheck'
    end
end

サーバーを起動した後、nocheckcheck に変更しても、トピックのルートを読み込むとまだ nocheck が出力されます。

after_initialize do
    reloadable_patch do |plugin|
        puts 'nocheck'
    end
end

puts 内の文字列を変更してページをリロードしても、依然としてうまくいきません。

上記の投稿で誤解を招いてしまったかもしれません。reloadable_patch は Discourse の開発には役立ちますが、@david がその使い方を非常に良く説明してくれています:


plugin.rbafter_initialize ブロック内のコードは、アプリケーションの起動時 にのみ読み込まれ、その後のリロード時には読み込まれません。

つまり、ユーザーシリアライザーに何かを追加したいと仮定します。通常の動作は以下のようになります:

起動時:

  • Discourse が user_serializer.rb を読み込む
  • Discourse が plugin.rb を読み込む(これには user_serializer のオーバーライドが含まれている)

リロード時:

  • Discourse が user_serializer.rb を再読み込みする
  • plugin.rb のパッチは再読み込みされず、プラグインによるオーバーライドは失われる)

reloadable_patch システムを使用すると:

起動時:

  • Discourse が user_serializer.rb を読み込む
  • Discourse が plugin.rb を読み込み、user_serializer 用の reloadable_patch を登録する
  • リロード可能なパッチが実行される

リロード時:

  • Discourse が user_serializer.rb を再読み込みする
  • リロード可能なパッチが実行される
  • (やった、プラグインによるオーバーライドが引き続き機能している)
「いいね!」 6

これは Rails のコア機能に関する記述であり、Discourse 特有のものではありません。

Rails では、すべてのイニシャライザーは Rails が起動したときのみ読み込まれます。したがって、「after_initializer」のようにイニシャライザーに基づいてコードを実行するプラグインは、Rails の起動時または再起動時のみ実行されます(もし誤りであればご指摘ください)。

私がこれに詳しい理由は、現在クライアント向けに Rails アプリケーションを構築しており、さまざまなタスク(ブーリアン値、静的配列など)のために多くのイニシャライザーを実装しているからです。いずれの場合も、Rails のイニシャライザー内のコードを変更した場合、新しい値を Rails アプリに反映させるには、Rails を再起動する必要があります(開発環境では「Control c」を押して「rails s」を実行)。

もし誤りであればご指摘ください!ありがとうございます。

時々、特に Rails アプリケーション全般ではなく主に Discourse プラグインの開発に携わっている方々にとって、「Discourse はこれをやる」「Discourse はあれをやる」といった表現が少し混乱を招くことがあるかもしれません。実際には、Discourse 特有のものではなく、必ずしも Discourse に固有ではない Rails のコア機能や特性について説明している場合があるからです。

Rails 6 は、config/initializers ディレクトリ(およびそのすべてのサブディレクトリ)内のすべてのファイルを、Rails が起動したときのみ読み込みます。同様に(プラグインについては 100% 確信はありませんが)、after_initialize に依存するコードを持つ Discourse プラグインも、Rails の起動時または再起動時のみ Rails アプリによって読み込まれます(どなたか確認していただけませんか?)。これは Rails の起動プロセスの根本的な性質によるものです。

ここで Rails を扱っている方々は、すでにこのことをご存知だと思われます。私自身もこれらの詳細を知り始めたばかりで(専門家になるにはまだ長い道のりがありますが)、現在は毎日 Rails アプリケーションに取り組んでいます。余談ですが、LAMP 開発のバックグラウンドから来ており、Rails が非常に優れている(使いやすく、楽しく作業できる)と感じているため、10 年前から Rails を使い始めていればよかったと今では思います。

今年は Rails の大ファンになり、その道に進ませてくれた Discourse に心から感謝しています。

繰り返しになりますが、もし誤りであればご指摘ください!ありがとうございます。私は Rails の専門家を目指して頑張っています。

「いいね!」 6

はい!その通りです。Rails のイニシャライザに関するあなたの理解も正確です。

私も Railsの大ファンです :slight_smile:

「いいね!」 7