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の仕組みをおおざっぱに理解できるでしょうか。 何か間違ったら、皆さんぜひ指摘してください。