render_componentの挙動に関するメモ
This article was migrated from http://rai
render_com
長いので先に結論だけ書いておくと、render_com
でレンダリングされるコンポーネントのコードから、呼び出し元の外部コントローラにアクセスするには、ActionCont
を介せばOKです。
$ vim vendor/rails/actionp ack/lib/ac tion_contr oller/comp onents.rb module ActionCont roller module Components base.helpe r do def render_com ponent(options) @controller .send(:render_co mponent_as _string, options) end end module ClassMetho ds # Track parent controller to identify component requests def process_wi th_compone nts(request, response, parent_con troller = nil) controller = new controller .parent_co ntroller = parent_con troller controller .process(request, response) end end module InstanceMe thods # Extracts the action_nam e from the request parameters and performs that action. def process_wi th_compone nts(request, response, method = :perform_a ction, *arguments) flash.disc ard if component_ request? process_wi thout_comp onents(request, response, method, *arguments) end protected # Returns the component response as a string def render_com ponent_as_ string(options) component_ logging(options) do response = component_ response(options, false) if redirected = response.r edirected_ to render_com ponent_as_ string(redirected ) else response.b ody end end end private def component_ response(options, reuse_resp onse) klass = component_ class(options) request = request_fo r_componen t(klass.cont roller_nam e, options) response = reuse_resp onse ? @response : @response.d up klass.proc ess_with_c omponents(request, response, self) end # determine the controller class for the component request def component_ class(options) if controller = options[:controlle r] controller .is_a?(Class) ? controller : "#{ controller .camelize }Controller ".constanti ze else self.class end end # Create a new request object based on the current request. # The new request inherits the session from the current request, # bypassing any session options set for the component controller 's class def request_fo r_componen t(controller _name, options) request = @request.du p request.se ssion = @request.se ssion request.in stance_var iable_set( :@parameters , (options[:params] || {}).with_indi fferent_ac cess.updat e( "controller " => controller _name, "action" => options[:action], "id" => options[:id])) request end end end
request_fo
の中身:
with_indif
は、ActiveSuppinstance_v
というのは、RubyのObjectクラスのメソッドで、こんな感じにインスタンス変数を外部から設定できてしまいます:
obj = Object.newp obj.instan ce_variabl e_set("@foo", 1) # => 1 p obj.instan ce_variabl e_set(:@foo, 2) # => 2 p obj.instan ce_variabl e_get(:@foo) # => 2
というわけで、上記のようにcontroller
、action
、id
の指定を挿げ替えてrequestオブジェクトを偽装しているわけです。
そして、最終的にklass.proc
が呼び出され、render_com
の呼び出し元のcontroller
をparent_con
に設定し、制御を実行しています。
したがって、レンダリングされるコンポーネントの内部からは、parent_con
を介して呼び出し元のcontroller
とやり取りを行う事ができます。
This article was migrated from http://rai