Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/trunk/actionpack/lib/action_controller/components.rb

Revision 7719, 6.0 kB (checked in by bitsweat, 9 months ago)

Ruby 1.9 compat, consistent load paths

Line 
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.
4   #
5   #   class WeblogController < ActionController::Base
6   #     # Performs a method and then lets hello_world output its render
7   #     def delegate_action
8   #       do_other_stuff_before_hello_world
9   #       render_component :controller => "greeter",  :action => "hello_world", :params => { :person => "david" }
10   #     end
11   #   end
12   #
13   #   class GreeterController < ActionController::Base
14   #     def hello_world
15   #       render :text => "#{params[:person]} says, Hello World!"
16   #     end
17   #   end
18   #
19   # The same can be done in a view to do a partial rendering:
20   #
21   #   Let's see a greeting:
22   #   <%= render_component :controller => "greeter", :action => "hello_world" %>
23   #
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:
26   #
27   # <%= render_component :controller => GreeterController, :action => "hello_world" %>
28   #
29   # == When to use components
30   #
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.
35   #
36   # So to repeat: Components are a special-purpose approach that can often be replaced with better use of partials and filters.
37   module Components
38     def self.included(base) #:nodoc:
39       base.class_eval do
40         include InstanceMethods
41         extend ClassMethods
42
43         helper do
44           def render_component(options)
45             @controller.send!(:render_component_as_string, options)
46           end
47         end
48
49         # If this controller was instantiated to process a component request,
50         # +parent_controller+ points to the instantiator of this controller.
51         attr_accessor :parent_controller
52
53         alias_method_chain :process_cleanup, :components
54         alias_method_chain :set_session_options, :components
55         alias_method_chain :flash, :components
56
57         alias_method :component_request?, :parent_controller
58       end
59     end
60
61     module ClassMethods
62       # Track parent controller to identify component requests
63       def process_with_components(request, response, parent_controller = nil) #:nodoc:
64         controller = new
65         controller.parent_controller = parent_controller
66         controller.process(request, response)
67       end
68     end
69
70     module InstanceMethods
71       # Extracts the action_name from the request parameters and performs that action.
72       def process_with_components(request, response, method = :perform_action, *arguments) #:nodoc:
73         flash.discard if component_request?
74         process_without_components(request, response, method, *arguments)
75       end
76
77       protected
78         # Renders the component specified as the response for the current method
79         def render_component(options) #:doc:
80           component_logging(options) do
81             render_for_text(component_response(options, true).body, response.headers["Status"])
82           end
83         end
84
85         # Returns the component response as a string
86         def render_component_as_string(options) #:doc:
87           component_logging(options) do
88             response = component_response(options, false)
89
90             if redirected = response.redirected_to
91               render_component_as_string(redirected)
92             else
93               response.body
94             end
95           end
96         end
97
98         def flash_with_components(refresh = false) #:nodoc:
99           if !defined?(@_flash) || refresh
100             @_flash =
101               if defined?(@parent_controller)
102                 @parent_controller.flash
103               else
104                 flash_without_components
105               end
106           end
107           @_flash
108         end
109
110       private
111         def component_response(options, reuse_response)
112           klass    = component_class(options)
113           request  = request_for_component(klass.controller_name, options)
114           new_response = reuse_response ? response : response.dup
115
116           klass.process_with_components(request, new_response, self)
117         end
118
119         # determine the controller class for the component request
120         def component_class(options)
121           if controller = options[:controller]
122             controller.is_a?(Class) ? controller : "#{controller.camelize}Controller".constantize
123           else
124             self.class
125           end
126         end
127
128         # Create a new request object based on the current request.
129         # The new request inherits the session from the current request,
130         # bypassing any session options set for the component controller's class
131         def request_for_component(controller_name, options)
132           new_request         = request.dup
133           new_request.session = request.session
134
135           new_request.instance_variable_set(
136             :@parameters,
137             (options[:params] || {}).with_indifferent_access.update(
138               "controller" => controller_name, "action" => options[:action], "id" => options[:id]
139             )
140           )
141
142           new_request
143         end
144
145         def component_logging(options)
146           if logger
147             logger.info "Start rendering component (#{options.inspect}): "
148             result = yield
149             logger.info "\n\nEnd of component rendering"
150             result
151           else
152             yield
153           end
154         end
155
156         def set_session_options_with_components(request)
157           set_session_options_without_components(request) unless component_request?
158         end
159
160         def process_cleanup_with_components
161           process_cleanup_without_components unless component_request?
162         end
163     end
164   end
165 end
Note: See TracBrowser for help on using the browser.