2014年5月29日木曜日

Padrinoの起動時のロード順・フック実行について

今回のテーマは実は、Padrinoにおいてconfig/boot.rbにはbefore_loadフックとafter_loadフックの登録を行うコード記述があるけれど、config/boot.rbファイルのトップレベルに直接書くのと before_loadやafter_loadの中に書くのではどう違うのか、という疑問が切っ掛けでした。

Padrinoが生成するconfig/boot.rbは末尾で Padrino.load! を呼び出していますが、実はその中でbefore_loadとafter_loadを呼んでいます。Padino.load!の定義は、ソースコードを調べると次のようになってます。

def load!
  return false if loaded?
  began_at = Time.now
  @_called_from = first_caller
  set_encoding
  set_load_paths(*load_paths)
  Logger.setup!
  require_dependencies("#{root}/config/database.rb")
  Reloader.lock!
  before_load.each(&:call)
  require_dependencies(*dependency_paths)
  after_load.each(&:call)
  logger.devel "Loaded Padrino in #{Time.now - began_at} seconds"
  Thread.current[:padrino_loaded] = true
end

つまりbefore_loadとafter_loadの違いは、Padrinoの各種依存ファイルのrequireの前か後か、という違いです。

上記Padino.load!をよく見てみると、Loggerだけ扱いが別になっています。Logger関連の設定はbefore_loadやafter_loadの中に書いてもそれだけでは有効になりません。config/boot.rbのトップレベルかつPadrino.load! の前に記述するか、手動でLogger.setup!を呼ぶ必要があります。

で、ようやく本題ですが、上記のPadrino.load!を見ながら、実際フレームワーク全体がどう読み込まれるのか調べてみたくなりました。Loggerのログレベルをdevelに設定すると、起動時にロードしたファイルをログに出してくれるようになるので、ロードの順番がわかります。まとめるとPadrinoのロードの主要手順はだいたいこんな感じになっているようです。

  • Loggerの設定
  • config/database.rbのロード
  • before_loadフックの実行
  • lib/の中にあるファイルのロード
  • models/の中にあるファイルのロード
  • (shared/lib/の中にあるファイルのロード)※デフォルトでは存在しない
  • (shared/models/の中にあるファイルのロード)※デフォルトでは存在しない
  • config/apps.rbのロード
  • app/app.rbのロード
  • アプリケーション名::Appのインスタンス生成
  • after_loadフックの実行
  • app/controllers/の中にあるファイルのロード
  • app/helpers/の中にあるファイルのロード

なるほど。ちなみに意外だったのは、controllersとhelpersのロード順が予想と逆だったこと。でもどうせメソッド定義なので特にロード順に影響はないはずです。

ロードの順番に影響を受けるようなコードの書き方をすることは少ない(というかなるべく避けるべき)と思いますが、デバッグの役に立つこともありますので一応知っておくとよいと思われます。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。

0 件のコメント:

コメントを投稿