1 module ActionController
#:nodoc:
2 # Components allow you to call other actions for their rendered response while executing another action. You can either delegate
3 # the entire response rendering or you can mix a partial response in with your other content.
5 # class WeblogController < ActionController::Base
6 # # Performs a method and then lets hello_world output its render
8 # do_other_stuff_before_hello_world
9 # render_component :controller => "greeter", :action => "hello_world", :params => { :person => "david" }
13 # class GreeterController < ActionController::Base
15 # render :text => "#{params[:person]} says, Hello World!"
19 # The same can be done in a view to do a partial rendering:
21 # Let's see a greeting:
22 # <%= render_component :controller => "greeter", :action => "hello_world" %>
24 # It is also possible to specify the controller as a class constant, bypassing the inflector
25 # code to compute the controller class at runtime:
27 # <%= render_component :controller => GreeterController, :action => "hello_world" %>
29 # == When to use components
31 # Components should be used with care. They're significantly slower than simply splitting reusable parts into partials and
32 # conceptually more complicated. Don't use components as a way of separating concerns inside a single application. Instead,
33 # reserve components to those rare cases where you truly have reusable view and controller elements that can be employed
34 # across many applications at once.
36 # So to repeat: Components are a special-purpose approach that can often be replaced with better use of partials and filters.
38 def self.included(base
) #:nodoc:
40 include InstanceMethods
41 include ActiveSupport
::Deprecation
45 # If this controller was instantiated to process a component request,
46 # +parent_controller+ points to the instantiator of this controller.
47 attr_accessor
:parent_controller
49 alias_method_chain
:process_cleanup, :components
50 alias_method_chain
:set_session_options, :components
51 alias_method_chain
:flash, :components
53 alias_method
:component_request?, :parent_controller
58 # Track parent controller to identify component requests
59 def process_with_components(request
, response
, parent_controller
= nil) #:nodoc:
61 controller
.parent_controller
= parent_controller
62 controller
.process(request
, response
)
67 def render_component(options
)
68 @controller.__send__(:render_component_as_string, options
)
72 module InstanceMethods
73 # Extracts the action_name from the request parameters and performs that action.
74 def process_with_components(request
, response
, method
= :perform_action, *arguments
) #:nodoc:
75 flash
.discard
if component_request
?
76 process_without_components(request
, response
, method
, *arguments
)
80 # Renders the component specified as the response for the current method
81 def render_component(options
) #:doc:
82 component_logging(options
) do
83 render_for_text(component_response(options
, true).body
, response
.headers
["Status"])
86 deprecate
:render_component => "Please install render_component plugin from http://github.com/rails/render_component/tree/master"
88 # Returns the component response as a string
89 def render_component_as_string(options
) #:doc:
90 component_logging(options
) do
91 response
= component_response(options
, false)
93 if redirected
= response
.redirected_to
94 render_component_as_string(redirected
)
100 deprecate
:render_component_as_string => "Please install render_component plugin from http://github.com/rails/render_component/tree/master"
102 def flash_with_components(refresh
= false) #:nodoc:
103 if !defined?(@_flash) || refresh
105 if defined?(@parent_controller)
106 @parent_controller.flash
108 flash_without_components
115 def component_response(options
, reuse_response
)
116 klass
= component_class(options
)
117 request
= request_for_component(klass
.controller_name
, options
)
118 new_response
= reuse_response
? response
: response
.dup
120 klass
.process_with_components(request
, new_response
, self)
123 # determine the controller class for the component request
124 def component_class(options
)
125 if controller
= options
[:controller]
126 controller
.is_a
?(Class
) ? controller
: "#{controller.camelize}Controller".constantize
132 # Create a new request object based on the current request.
133 # The new request inherits the session from the current request,
134 # bypassing any session options set for the component controller's class
135 def request_for_component(controller_name
, options
)
136 new_request
= request
.dup
137 new_request
.session
= request
.session
139 new_request
.instance_variable_set(
141 (options
[:params] || {}).with_indifferent_access
.update(
142 "controller" => controller_name
, "action" => options
[:action], "id" => options
[:id]
149 def component_logging(options
)
151 logger
.info
"Start rendering component (#{options.inspect}): "
153 logger
.info
"\n\nEnd of component rendering"
160 def set_session_options_with_components(request
)
161 set_session_options_without_components(request
) unless component_request
?
164 def process_cleanup_with_components
165 process_cleanup_without_components
unless component_request
?