Railsアップグレード時に bin/rails app:updateしたときに生成されるconfig/initializers/new_framework_xxx.rbが効かなかったときのハマりメモ。Railsのバージョンは5.1.4です。

原因と対応方法に関しては以下の記事がとても詳しいです。

本記事ではもう少し堀下げて解説していきます。

Railsの初期化ですがだいたいこんな感じで行われます。

  •  config/environment.rb
    •  config/application.rb
      • config/boot.rb
      • Bundler.require
        • Gemfileのgemをrequire
      • XXX::Application
    • Rails.application.initialize!
      • イニシャライザの呼び出し

イニシャライザの呼び出しはRailtieやconfig/environments/xxx.rb、config/initializers/xxx.rb の呼び出しが含まれており、順番も決まっています。

代表的なRailsのコンポーネントと設定ファイルだと以下の順番でRailtieのinitializerや設定ファイルの中身が読み込まれます。

  • config/environments/xxx.rb
  • action_controller
  • active_record
  • config/initializers/xxx.rb
  • action_view

また、各コンポーネントのRailtieのinitializerではRails.application.configの設定値を各コンポーネントにコピーしています。

例えばActiveRecordだとこんな感じ

ActiveSupport.on_load(:active_record) のブロックが実行される際のコンテキストはActiveRecord::BaseなのでActiveRecord::Baseのクラスメソッドのセッターメソッドによってconfigの値がコピーされています。

つまりActiveRecord::Railtieのinitializerが起動してActiveRecord::Baseが一度読み込まれてしまうと、configの値を変更してもActiveRecord::Baseの変数を変えることにはならないので設定が効かない、ということになります。

すべてのgemがActiveRecord::Baseを直接利用しないように作られているのであれば良いのですが、たとえばActiveRecord::Base.send :include, XXX というように ActiveSupport.on_load(:active_record) のフックを利用しないで拡張するgemがある場合は、gemを読み込んだ時点でオートロードによってActiveRecord::Baseが読みこまれてしまいます。

結果としてActiveRecord::Railtieのinitializerを実行した瞬間に設定値がセットされ、後続のconfig/initializers/xxx.rb等でのRails.application.config経由のActiveRecord::Baseへの設定ができなくなります(ただし、ActiveRecord::Base直接変更することはできます)

この問題の嫌なところはgemに依存するという部分で、プロジェクト単位でも利用するgemが変わってくる上に、同一プロジェクトでもRailsの環境(development, test, production)で読み込まれるgemが変わってくるのでそれぞれの環境で挙動が変わってきます。実際、developmentのgemに問題が有って、CIはちゃんと動くのに開発環境ではエラーが多発…ということがありました。

解決策

上の記事でも書かれておりますが、gemを直していくのが根本的な解決です。

が、config/application.rbにRails関連の設定を全て入れちゃうのも手です。initializerよりも確実に早く実行されるので設定が効くようになります。