📕

RedmineプラグインのRedmine5(Rails6)向け対応

7 min read

Redmine5にてRailsが6.1となることが決まっており、Redmineのtrunkでは既にRails6対応が入っています。

特に一番影響のありそうなZeitwerkへの対応が先日(2021-11-18)入りました。

これによって私がメンテしている2つのプラグインが動作しなくなりました。

この記事では、Redmine5に向けて修正した内容をまとめていきます。
なお、問題の原因が、Rails6の変更によるものなのか、Redmine本体の変更によるものなのか、Ruby 3.0 によるものなのかといったところまでの確認は出来ていません。

確認した際の各種バージョンは下記の通りです。

  • Redmine: trunk r21289
  • Ruby: 3.0.0-p0
  • Rails: 6.1.4.1

修正は、Redmine5でしか動かないものではなく、今までのバージョンでも動くような形での修正を行っていきます。

現時点(2021-11-23)ではRedmine5のリリース予定日は決まっていないため、今後別の対応が必要になった場合には、本記事を更新していく予定です。

参考情報

今回の対応を行うにあたり、Redmine本体でのZeitwerkへの対応チケットが参考になりました。

また、Zeitwerkについては、下記が参考になりました。

対応内容

require や require_dependency で lib 配下からの指定が cannot load such file となる

プラグインフォルダ配下のlibフォルダにあるファイルは、libフォルダからのパスを指定することでロードできていましたが、Redmine5ではロードできません。

例えば下記のような記述でlib/view_customize/view_hook.rbをロードしようとすると

require_dependency 'view_customize/view_hook'

cannot load such file となります。

cannot load such file -- view_customize/view_hook (LoadError)
  /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/kernel.rb:35:in `require'
  /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/kernel.rb:35:in `require'
  /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/activesupport-6.1.4.1/lib/active_support/dependencies/zeitwerk_integration.rb:51:in `require_dependency'
  /var/lib/redmine/plugins/view_customize/after_init.rb:1:in `<top (required)>'
  /var/lib/redmine/plugins/view_customize/init.rb:22:in `require_relative'
  /var/lib/redmine/plugins/view_customize/init.rb:22:in `<top (required)>'
  /var/lib/redmine/lib/redmine/plugin_loader.rb:31:in `load'
  /var/lib/redmine/lib/redmine/plugin_loader.rb:31:in `run_initializer'
  /var/lib/redmine/lib/redmine/plugin_loader.rb:106:in `each'
  /var/lib/redmine/lib/redmine/plugin_loader.rb:106:in `block in load'
  ...

require_dependencyでもrequireのどちらでもダメです。

絶対パスで指定する形に変更することで対応しました。また、require_dependencyである必要は既に無いようなのでrequireに変えました。

require File.expand_path('../lib/view_customize/view_hook', __FILE__)

モジュール名とディレクトリ名がアンマッチだと uninitialized constant が発生する

lib/view_customize/view_hook.rbRedmineViewCustomize::ViewHook というクラスを定義していたところ、下記のようなエラーとなりました。

Error: The application encountered the following error: uninitialized constant ViewCustomize::ViewHook (NameError)
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader/helpers.rb:95:in `const_get'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader/helpers.rb:95:in `cget'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader.rb:232:in `block (2 levels) in eager_load'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader/helpers.rb:26:in `block in ls'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader/helpers.rb:18:in `each_child'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader/helpers.rb:18:in `ls'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader.rb:227:in `block in eager_load'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader.rb:212:in `synchronize'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader.rb:212:in `eager_load'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader.rb:312:in `each'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/zeitwerk-2.5.1/lib/zeitwerk/loader.rb:312:in `eager_load_all'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/railties-6.1.4.1/lib/rails/application/finisher.rb:133:in `block in <module:Finisher>'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/railties-6.1.4.1/lib/rails/initializable.rb:32:in `instance_exec'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/railties-6.1.4.1/lib/rails/initializable.rb:32:in `run'
    /var/lib/redmine/vendor/bundle/ruby/3.0.0/gems/railties-6.1.4.1/lib/rails/initializable.rb:61:in `block in run_initializers'
  ...

ディレクトリ名とモジュール名がアンマッチだと発生するようなので、ディレクトリ名をredmine_view_customizeに変えてlib/redmine_view_customize/view_hook.rbとすることで回避しました。
クラス名とファイル名のアンマッチでも起きるようなので要注意です。

この問題はdevelopmentモードでは発生せず、productionモードで発生していました。

ActiveSupport::Reloader.to_prepare で登録したコールバックが呼び出されない

developmentモードを考慮してActiveSupport::Reloader.to_prepareIssueに対してパッチ当てるようにしていましたが、これが全く呼び出されなくなりました。

((Rails.version > "5")? ActiveSupport::Reloader : ActionDispatch::Callbacks).to_prepare do
  require_dependency 'issue'
  unless Issue.included_modules.include? RedmineIssueAssignNotice::IssuePatch
    Issue.send(:include, RedmineIssueAssignNotice::IssuePatch)
  end
end

アプリケーション初期化時にも呼び出されないため、productionモードでも1回も呼び出されませんでした。

ActiveSupport::Reloader.to_prepareが動作しない理由はわからず、Zeitwerkに関するドキュメント見ると Rails.application.reloader.to_prepare での初期化方法が書いてあったので、こちらも試してみましたが結果は変わりませんでした。

developmentモードでのリロード時の考慮はそんな重要なことではないので、いったん下記のように1回のみ実行する形に変更して回避しました。

require 'issue'
unless Issue.included_modules.include? RedmineIssueAssignNotice::IssuePatch
  Issue.send(:include, RedmineIssueAssignNotice::IssuePatch)
end

Redmine本体側の問題

Redmineのtrunkの r21289 の時点では、プラグインのassetsが正しくコピーされないといった問題があります。
r21295 にて既に修正されている問題です。

エラーにはなりませんが、CSSや画像が読み込まれず、レイアウトが崩れる場合があるのでご注意ください。

Discussion

ログインするとコメントできます