Redmine 5.0で古いプラグインを動作させる方法(Zeitwerk対応)


My Redmine

原田です。最近スマートフォンの画面が浮いてきました。インターネットで調べるとバッテリーが膨張していることが原因のようです。バッテリーを交換するかスマートフォンを買い換えるか悩んでおります。

今回はZeitwerkが採用されたRedmine 5.0で古いプラグインを動作させる方法について書きます。

Redmine 5.0ではZeitwerkが採用されます

2022年3月27日リリース予定のRedmine 5.0ではZeitwerkが採用されます。


Redmine 5.0のロードマップ

Feature #32938: Rails 6: Zeitwerk support

Zeitwerkとは

Zeitwerkに関する情報はインターネットから見つけることができますので細かい説明は省略いたします。個人的には「ファイル名(クラス名)とファイルパス(名前空間)をRubyの規則に沿って定義すればZeitwerkにも対応する」と思っております。

プラグインがZeitwerkに対応していないとRedmine 5.0は起動しません

Redmine 5.0でZeitwerkが採用されますので、プラグインもZeitwerkで動作するよう見直す必要があります。もしプラグインがZeitwerkに対応していない場合はRedmineは起動しません。 (今回は動作検証のため redmine_extended_reminder プラグインを使用しました。)

% bundle exec rails server -e development -b 127.0.0.1 -p 3000
=> Booting Puma
=> Rails 6.1.4.7 application starting in development
=> Run `bin/rails server --help` for more startup options
Exiting
vendor/bundle/ruby/3.1.0/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:35:in `require': cannot load such file -- redmine_extended_reminder/hooks (LoadError)
    from vendor/bundle/ruby/3.1.0/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:35:in `require'
    from vendor/bundle/ruby/3.1.0/gems/activesupport-6.1.4.7/lib/active_support/dependencies/zeitwerk_integration.rb:51:in `require_dependency'
...
...
...


プラグインがZeitwerkに対応しているかを確認するために rake zeitwerk:check コマンドを使用します。Zeitwerkに対応していない時はエラーを表示しますが、最初に見つかったエラーのみを表示します。見直しが終わりましたら都度rakeコマンドを実行し確認してください。

プラグインがZeitwerkに対応している時

% bundle exec rake zeitwerk:check
Hold on, I am eager loading the application.
All is good!

プラグインがZeitwerkに対応していない時

% bundle exec rake zeitwerk:check
rake aborted!
LoadError: cannot load such file -- redmine_extended_reminder/hooks
vendor/bundle/ruby/3.1.0/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:35:in `require'
vendor/bundle/ruby/3.1.0/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:35:in `require'
vendor/bundle/ruby/3.1.0/gems/activesupport-6.1.4.7/lib/active_support/dependencies/zeitwerk_integration.rb:51:in `require_dependency'
plugins/redmine_extended_reminder/init.rb:2:in `<top (required)>'
lib/redmine/plugin_loader.rb:31:in `load'
lib/redmine/plugin_loader.rb:31:in `run_initializer'
lib/redmine/plugin_loader.rb:108:in `each'
lib/redmine/plugin_loader.rb:108:in `block in load'
...
...
...
Tasks: TOP => zeitwerk:check => environment
(See full trace by running task with --trace)

ファイル名(クラス名)とファイルパス(名前空間)をRubyの規則に合わせる

私は以下のコーディングスタイルの規則に沿ってファイル名やファイルパスを決めています。

以下は今回エラーが発生したコード(redmine_extended_reminder/init.rb#L2)です。プラグインのlibディレクトリ下に存在するrbファイルに対してrequireを実施するとエラーが発生するようです。Zeitwerkを有効にしたことでlibディレクトリが自動読み込み機能(オートロード)のパスに含まれていないのではないかと思います。

require_dependency 'redmine_extended_reminder/hooks'

これを以下のように修正しました(相対パスから絶対パスに変更)。

diff --git a/init.rb b/init.rb
index b2ecced..d4f8418 100644
--- a/init.rb
+++ b/init.rb
@@ -1,5 +1,5 @@
 require 'redmine'
-require_dependency 'redmine_extended_reminder/hooks'
+require_dependency File.expand_path('../lib/redmine_extended_reminder/hooks', __FILE__)

 Rails.configuration.to_prepare do
   require_dependency 'redmine_extended_reminder/mailer_model_patch'

Rails.configuration.to_prepare を Rails.configuration.after_initialize に置き換え

当初ファイル名とファイルパスを調整すればZeitwerkに対応できると思っていました。コード修正後 rake zeitwerk:check コマンドを実行すると結果は「All is good!」でしたが、実際にRedmineを起動するとプラグインの動きが今までと異なりました。具体的には、個人設定の言語やプラグインの通知日数などを変更すると動的に変化するよう作成されているリマインダーメールの本文がRedmine起動時の状態のままで動的に変わりませんでした。


redmine_extended_reminderプラグインを使用したリマインダーメールの出力例

解決方法は https://www.redmine.org/issues/36245#note-14 が参考になり、Rails.configuration.to_prepareRails.configuration.after_initializeに置き換えたところ、リマインダーメールが適切に(動的に変化して)出力しました。

今までサポートしていた古いRedmineでもプラグインが動作することを確認しましょう

プラグインをZeitwerkに対応した後は、今までプラグインがサポートしていた古いRedmineでも動作することを確認してください。init.rbにrequires_redmine version_or_higher: '5.0.0'を記述してサポート対象のRedmineのバージョンを5.0以降に限定してもよろしいかと思いますが、プラグインを使用している利用者がいらっしゃるのなら古いRedmineでも今までどおり動作してくれるほうが利用者からの印象は良いと思います。

ちなみに今回検証した redmine_extended_reminder プラグインはプルリクエストを作成し、以下の修正パッチを投稿しました(古いRedmineでもこのプラグインが動作するように見直しました)。

diff --git a/init.rb b/init.rb
index b2ecced..92d4699 100644
--- a/init.rb
+++ b/init.rb
@@ -1,8 +1,9 @@
 require 'redmine'
-require_dependency 'redmine_extended_reminder/hooks'
+require_dependency File.expand_path('../lib/redmine_extended_reminder/hooks', __FILE__)

-Rails.configuration.to_prepare do
-  require_dependency 'redmine_extended_reminder/mailer_model_patch'
+zeitwerk_enabled = Rails.version > '6.0' && Rails.autoloaders.zeitwerk_enabled?
+Rails.configuration.__send__(zeitwerk_enabled ? :after_initialize : :to_prepare) do
+  require_dependency File.expand_path('../lib/redmine_extended_reminder/mailer_model_patch', __FILE__)
 end

 Redmine::Plugin.register :redmine_extended_reminder do
こちらの記事もオススメです!
久しぶりに印象に残ったRedmineの改善
My Redmineのお客様から作業時間一覧についてお問い合わせをいただきRedmineを改善。
The Redmine Award 大賞をいただいたのでこれまでのRedmine活動を振り返る
これまでのRedmineに関する情報発信や開発への協力を評価いただきました。
VS Codeの便利な拡張機能3選(校正・目次自動作成・Vim)
VS Codeを使っていて特に便利だと思った拡張機能(校正・目次自動作成・Vim)を3つ紹介。
よく探し物をする私とAirTag
紛失防止タグ AirTagを利用しています。サウンドを再生機能と正確な場所を見つける機能ですぐ発見できます。
シンプルなUIでRedmineのチケットを作成・更新できる「RedMica Bridge」ベータ版をリリース
シンプルなUIで簡単にチケットに入力できる「RedMica Bridge」は、複数のRedmineを横断してチケットを管理できるサービスです。
ファーエンドテクノロジーからのお知らせ(2024/04/24更新)
入門Redmine 第6版 出版記念企画セミナー「Redmineのアクセス制御」【2024/5/30開催】
入門Redmine 第6版(2024年3月23日発売)の書籍から「Redmineのアクセス制御」について解説します。
My Redmine 初回ご契約で「入門Redmine 第6版」プレゼントのお知らせ
Redmineのクラウドサービス「My Redmine」を初めてご契約いただいたお客様にRedmine解説書「入門Redmine 第6版」を進呈いたします。
2024年度ブランドパートナーに島根県在住のモデル ユイさんを継続起用
ユイさん(モデルスタジオミューズ所属)をファーエンドテクノロジーの2024年度ブランドパートナーとして継続して起用します。
My Redmine スタンダードプランおよびAdminサポートデスクプランの料金改定のお知らせ【2024年4月ご利用分より】
2024年4月ご利用分より、My Redmine スタンダードプラン(民間企業・個人向け及び官公庁向け)とAdminサポートデスクプランの料金を改定いたします。
Redmineの最新情報をメールでお知らせする「Redmine News」配信中
新バージョンやセキュリティ修正のリリース情報、そのほか最新情報を迅速にお届け