respond_toを調べてみた

「respond_toを調べてみた」の編集履歴(バックアップ)一覧はこちら

respond_toを調べてみた」(2012/01/09 (月) 08:11:19) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

どうにも、Railsのrespond_toメソッドがなんでこんな記述で動くのか良く判らなかったので、じっくり調べてみた。 -基本の使い方のおさらい --まずは、コントローラにこんな感じで書いて、 def index @books = Book.all respond_to do |format| format.html # index.html.erb format.json { render json: @books } end end --で、http://localhost:3000/books にアクセスすればHTMLが返却され、 --http://localhost:3000/books.json にアクセスすればJSONが返却される、と。 -たぶん、yieldを使って書いてありそうなんだけど、ちょっと理解しがたいのでRailsのソースを見てみた。 --まずは、当たりも付かないので、Rubyの下をソースを全grepして、怪しいファイルを発見。うーん、かっこ悪いw lib\ruby\gems\1.9.1\gems\actionpack-3.1.0\lib\action_controller\metal\mime_responds.rb(42): def respond_to(*mimes) lib\ruby\gems\1.9.1\gems\actionpack-3.1.0\lib\action_controller\metal\mime_responds.rb(191): def respond_to(*mimes, &block) --まあ、この「mime_responds.rb」が怪しいっぽいので、ソースを見ると… include ActionController::ImplicitRender included do class_attribute :responder, :mimes_for_respond_to self.responder = ActionController::Responder clear_respond_to end module ClassMethods def respond_to(*mimes) options = mimes.extract_options! only_actions = Array(options.delete(:only)) except_actions = Array(options.delete(:except)) new = mimes_for_respond_to.dup mimes.each do |mime| mime = mime.to_sym new[mime] = {} new[mime][:only] = only_actions unless only_actions.empty? new[mime][:except] = except_actions unless except_actions.empty? end self.mimes_for_respond_to = new.freeze end def clear_respond_to self.mimes_for_respond_to = ActiveSupport::OrderedHash.new.freeze end end def respond_to(*mimes, &block) raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given? if response = retrieve_response_from_mimes(mimes, &block) response.call(nil) end end def retrieve_response_from_mimes(mimes=nil, &block) #:nodoc: mimes ||= collect_mimes_from_class_level collector = Collector.new(mimes) { |options| default_render(options || {}) } block.call(collector) if block_given? if format = request.negotiate_mime(collector.order) self.content_type ||= format.to_s lookup_context.freeze_formats([format.to_sym]) collector.response_for(format) else head :not_acceptable nil end end def collect_mimes_from_class_level #:nodoc: action = action_name.to_sym self.class.mimes_for_respond_to.keys.select do |mime| config = self.class.mimes_for_respond_to[mime] if config[:except] !action.in?(config[:except]) elsif config[:only] action.in?(config[:only]) else true end end end ---む、これっぽいか? ---おお?これって、ClassMethods::respond_to(*mimes)って、使ってる?うーん、良く判らん…。 -ソース解析すると、こんな感じか? --まず、respond_to(*mimes, &block)が呼び出され、その中でmimesがあってかつblockが渡されてれば、retrieve_response_from_mimesを実行 --retrieve_response_from_mimesだと、渡されたmimesが正しいかをcollect_mimes_from_class_levelでチェックして、collector を生成 ---collector は、mimesの分だけdefault_renderを使ってoptionsを作る --その後、block_given?でブロックがあれば、block.call(collector)で「ブロックで指定されたメソッドを実行」 ---なんだけど、どうやって「ブロックで指定されたメソッドを実行」してるんだ? -あ、判った。Rubyだとメソッド呼び出しでのdo ~ endが結構特殊なんだ! --例えば、こんなメソッドを定義して、 def foo(p1,p2,&block) block.call(p1,p2) end --こんなコードで呼び出すと、 foo("p1","p2") do |p1,p2| p p1 p p2 end --こんな感じで表示される "p1" "p2" --これは、block.call(p1,p2)実行時に、fooのパラメータのp1,p2で、do~end内が繰り返されずに実行されるんだねー。 ---だから、fooをこんな感じで書くと foo("p1","p2") do |p1,p2| p p2 p p1 p "dummy" end ---こんな感じで表示されるんだ! "p2" "p1" "dummy" -しかし、結局mimesとかは、何が入ってるんだか、良く判らんからちゃんと見てみると? --まずは、respond_to(*mimes, &block)で呼び出される時は、*mimesとなっている ---*配列は、「配列でもらって、使う時は展開する」書式なんだー ---なので、ここでは、retrieve_response_from_mimes(mimes=nil, &block)の時に、mimesの配列が列挙されるんだねー。 --で、retrieve_response_from_mimes(mimes=nil, &block)では、列挙されたmimesが渡されてるはず、と。 ---あー、だから、retrieve_response_from_mimes内では、 以下のコードで「mime」のひとつずつに対して、true/false判定するんだねー mimes ||= collect_mimes_from_class_level
どうにも、Railsのrespond_toメソッドがなんでこんな記述で動くのか良く判らなかったので、じっくり調べてみた。 -基本の使い方のおさらい --まずは、コントローラにこんな感じで書いて、 def index @books = Book.all respond_to do |format| format.html # index.html.erb format.json { render json: @books } end end --で、http://localhost:3000/books にアクセスすればHTMLが返却され、 --http://localhost:3000/books.json にアクセスすればJSONが返却される、と。 -たぶん、yieldを使って書いてありそうなんだけど、ちょっと理解しがたいのでRailsのソースを見てみた。 --まずは、当たりも付かないので、Rubyの下をソースを全grepして、怪しいファイルを発見。うーん、かっこ悪いw lib\ruby\gems\1.9.1\gems\actionpack-3.1.0\lib\action_controller\metal\mime_responds.rb(42): def respond_to(*mimes) lib\ruby\gems\1.9.1\gems\actionpack-3.1.0\lib\action_controller\metal\mime_responds.rb(191): def respond_to(*mimes, &block) --まあ、この「mime_responds.rb」が怪しいっぽいので、ソースを見ると… include ActionController::ImplicitRender included do class_attribute :responder, :mimes_for_respond_to self.responder = ActionController::Responder clear_respond_to end module ClassMethods def respond_to(*mimes) options = mimes.extract_options! only_actions = Array(options.delete(:only)) except_actions = Array(options.delete(:except)) new = mimes_for_respond_to.dup mimes.each do |mime| mime = mime.to_sym new[mime] = {} new[mime][:only] = only_actions unless only_actions.empty? new[mime][:except] = except_actions unless except_actions.empty? end self.mimes_for_respond_to = new.freeze end def clear_respond_to self.mimes_for_respond_to = ActiveSupport::OrderedHash.new.freeze end end def respond_to(*mimes, &block) raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given? if response = retrieve_response_from_mimes(mimes, &block) response.call(nil) end end def retrieve_response_from_mimes(mimes=nil, &block) #:nodoc: mimes ||= collect_mimes_from_class_level collector = Collector.new(mimes) { |options| default_render(options || {}) } block.call(collector) if block_given? if format = request.negotiate_mime(collector.order) self.content_type ||= format.to_s lookup_context.freeze_formats([format.to_sym]) collector.response_for(format) else head :not_acceptable nil end end def collect_mimes_from_class_level #:nodoc: action = action_name.to_sym self.class.mimes_for_respond_to.keys.select do |mime| config = self.class.mimes_for_respond_to[mime] if config[:except] !action.in?(config[:except]) elsif config[:only] action.in?(config[:only]) else true end end end ---む、これっぽいか? ---おお?これって、ClassMethods::respond_to(*mimes)って、使ってる?うーん、良く判らん…。 -ソース解析すると、こんな感じか? --まず、respond_to(*mimes, &block)が呼び出され、その中でmimesがあってかつblockが渡されてれば、retrieve_response_from_mimesを実行 --retrieve_response_from_mimesだと、渡されたmimesが正しいかをcollect_mimes_from_class_levelでチェックして、collector を生成 ---collector は、mimesの分だけdefault_renderを使ってoptionsを作る --その後、block_given?でブロックがあれば、block.call(collector)で「ブロックで指定されたメソッドを実行」 ---なんだけど、どうやって「ブロックで指定されたメソッドを実行」してるんだ? -あ、判った。Rubyだとメソッド呼び出しでのdo ~ endが結構特殊なんだ! --例えば、こんなメソッドを定義して、 def foo(p1,p2,&block) block.call(p1,p2) end --こんなコードで呼び出すと、 foo("p1","p2") do |p1,p2| p p1 p p2 end --こんな感じで表示される "p1" "p2" --これは、block.call(p1,p2)実行時に、fooのパラメータのp1,p2で、do~end内が繰り返されずに実行されるんだねー。 ---だから、fooをこんな感じで書くと foo("p1","p2") do |p1,p2| p p2 p p1 p "dummy" end ---こんな感じで表示されるんだ! "p2" "p1" "dummy" -しかし、結局mimesとかは、何が入ってるんだか、良く判らんからちゃんと見てみると? --まずは、respond_to(*mimes, &block)で呼び出される時は、*mimesとなっている ---*配列は、「配列でもらって、使う時は展開する」書式なんだー ---なので、ここでは、retrieve_response_from_mimes(mimes=nil, &block)の時に、mimesの配列が列挙されるんだねー。 --で、retrieve_response_from_mimes(mimes=nil, &block)では、列挙されたmimesが渡されてるはず、と。 ---あー、だから、retrieve_response_from_mimes内では、 以下のコードで「mime」のひとつずつに対して、true/false判定するんだねー mimes ||= collect_mimes_from_class_level ---- #counter

表示オプション

横に並べて表示:
変化行の前後のみ表示: