Railsで学ぶフレームワーク作り - Dispatcher編 その5 Routeの構築部分 freezeメソッドの解析
route.freezeがカオス!前回書いていたたんですが、ようやく何をやってるのか(何故このような処理をしているのか)を理解しました。
freezeメソッドは、以下のようになっています。
def freeze unless frozen? write_generation! write_recognition! prepare_matching! parameter_shell significant_keys defaults to_s end super end
write_generationは、dispatch部分には関係ないので次のwrite_recognition!メソッドをみていきましょう。dispatchロジックに深く関係するrecognizeメソッドを触ってますね。
def write_recognition! # Create an if structure to extract the params from a match if it occurs. body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams" body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend" # Build the method declaration and compile it method_decl = "def recognize(path, env = {})\n#{body}\nend" instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" method_decl end
何をやってるのかというと、
- recognition_extractionでparamterを抽出
- recognition_conditionsでpathのmatchの判定条件を生成
- recognizeメソッドの文字列を生成
- recognizeメソッドをinstance_evalを使って今のrouteインスタンスにインストール(今のrouteインスタンスのメソッドの書き換え)
ということをやっています。
インスタンスのメソッド書き換えなんてデバッグ大変そうだし、なんでこんなことやるんだろうと思って不思議に思ってたんですが、method_declをdumpしてみたところ理由がわかりました。
Railsのディスパッチルールは、conf/routes.rbでDSL的に書くのですが、実行時にディスパッチロジックを高速化するために、一旦正規表現をベースにしたrubyコードにこのディスパッチルールを変換しているのでした。
要するにユーザーへのDSLを提供することでディスパッチルールを簡単にかけるようにするのと同時に、実行時のディスパッチロジックの高速化するためにDSLをrubyコードにコンパイルしたかったということですね。
いやぁ、しかし複雑ですね。初見だと、何やってるのかさっぱりわかりませんでした。dispatcherは一通りよみおえましたね。
railsが黒魔術を使っているといわれることの理由は少しわかった気がします。