Railtieのサブクラスで呼び出したinitializerブロックが読み込まれる仕組みをコードを読んで紐解いてみました。Railsは5.1.2です

Railtieでのinitializer呼び出し

initializerメソッドはRails::Initializableモジュールに定義されています。Rails::Initializable.initializerメソッドではInitializerのインスタンスを作ってCollectionに追加します。opts[:after]の行はTSortによるソート用に定義しており、基本的にはinitializerが呼び出された順にブロックの処理が走るようになっています。

これらはクラスメソッドなので各Railtieのクラスインスタンス変数に@initializersとしてInitializerのCollectionが格納されることになります。

これらの呼び出しはRails::Applicationから行います。

config/environment.rbで Rails.application.initialize! を呼びだします

Rails::Initializable#run_initializersは自身のinitializersをTSortでトポロジカルソートしたものを順にrunしていきます。

Initializer#runはRails::Initializable.initializerで定義したブロックを実行するメソッドです。

Rails::Initializableに#initializersが定義されているのですが、Rails::Applicationにも#initializersが定義されているので、こちらが呼び出されます。

railties_initializersはordered_railtiesのinitializersの集合になります。

ordered_railtiesにはrailtiesの中身が入り、railtiesはRailtiesのインスタンスです。Rails::Engine::Railties#eachメソッドで::Rails::Railtieのサブクラス、::Rails::Engineのサブクラスのインスタンスがセットされます。これによってRailtieを継承したクラスのinitializerで渡したブロックが順に実行されることになります。

TSortによるトポロジカルソート

さて、Railtieでは以下のようにbefore, afterキーワード引数によってinitializerの実行順の制御をすることができました。

これらのInitializerの順番を制御するためにRubyのTSortというライブラリを使っています。TSortはトポロジカルソートができるライブラリで、各ノードの関係性を使ったソートができます。

TSortの簡単な利用例は公式のリファレンスに書いてあるとおり、こんな感じです↓

例だと

  • 1は2、3より後
  • 2は3より後
  • 3の後は特に指定なし
  • 4の後は特に指定なし

という条件によってソートをしています。(※トポロジカルソートというのは、出力辺より前にそのノードを並び替えることらしいが、RubyのTSortはその逆順になっているっぽい)

initializerでは以下のようなCollectionクラスを設定してinitializerメソッドで定義されたInitializerクラスを良い感じにソートしています。

自分のブロックの前に実行すべきノードをselectで集めることで、before/afterの実行順序制御を実現しています。