Railsで開発するのはもう一年たちました。 しかし、Railsの仕組みはよく分からないのです。最近LighttpdのFastCgiでRailsを動かすときに、迷いました。
週末家でいらいらして、Railsのソースコードを読んで見ました。
みんな知っているように、RailsはActiveRecord, ActiveSupport, ActionControllerとActionViewによって構成されています。ActiveRecordはO/R Mappingで、ActiveSupportはrubyを拡張するライブラリです。 Railsの中心にあるのはやはりActionControllerです。
一つのRequstを送る際に、Railsはどういう流れで処理するのかに注目しつつ、ソースコードを解読しました。
Mongrelにしても、FastCgiにしても、最初はCgiRequestを作って、Dispatcher.dispatchを呼び出します。 Dispatcher.dispatchは主に下の二行です。
controller = ActionController::Routing::Routes.recognize(request) controller.process(request, response).out(output)
↓ Routingはroutes.rbにしたがって、urlを解析して、controllerのクラスを返します。
def self.process(request, response) new.process(request, response) end
↓
def process(request, response, method = :perform_action, *arguments) #:nodoc: initialize_template_class(response) #ActionViewのクラスの取る assign_shortcuts(request, response) #params, session, requestなどのショートカットを設定する initialize_current_url assign_names #@action_nameの書き込み forget_variables_added_to_assigns log_processing send(method, *arguments) #!!perform_actionの実行 assign_default_content_type_and_charset response ensure process_cleanup end
↓
def perform_action if self.class.action_methods.include?(action_name) #Actionの呼び出し send(action_name) #Actionの中でrenderとredirect_toが実行されなかったら、デフォルトのテンプレートを表示する render unless performed? elsif respond_to? :method_missing #Actionがない場合、method_missingを呼び出す send(:method_missing, action_name) render unless performed? elsif template_exists? && template_public? render else raise UnknownAction, "No action responded to #{action_name}", caller end end
これでRailsの流れをちょっと捕まえました。
しかし、そんなに単純ではありません。 様々な処理が見えていません! FilterでもBenchmarkでも、Sessionでも。 いったいどこにあるのか? 答えはここです。(benchmark.rb)
module Benchmarking #:nodoc: def self.included(base) base.extend(ClassMethods) base.class_eval do alias_method_chain :perform_action, :benchmark alias_method_chain :render, :benchmark end end #省略 end
alias_method_chain :perform_action, :benchmarkをやると、下の二行と同じです。
alias_method :perform_action_without_benchmark, :perform_action alias_method :perform_action, :perform_action_with_benchmark
これでperform_actionはテンプレートメソッドみたいに、名前変えずにbenchmarkの動作が組み込まれました。むしろ、rubyでのAOPとみなされるでしょう。 さらに、ちょっと考えてみると、perform_actionに対して、alias_method_chainは何回適応しても、うまくいけます。すっごいです!
「ModelやControllerはいつロードされましたのか?」ともう一つの疑問を抱いてきました。 答えはdependencies.rbです。DependenciesはObjectのconst_missingとrequireをオーバーライドしています。それで毎度Controllerなどをリフレクションでインスタンス化する際に(const_missingが呼び出される)、Railsのネーミングルールによって特定のフォルダーから読み込んで、ロードするのです。
これでRailsの仕組みをおおざっぱに理解できるでしょうか。 何か間違ったら、皆さんぜひ指摘してください。