Froze rails gems
[depot.git] / vendor / rails / actionpack / lib / action_controller / components.rb
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 include ActiveSupport::Deprecation
42 extend ClassMethods
43 helper HelperMethods
44
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
48
49 alias_method_chain :process_cleanup, :components
50 alias_method_chain :set_session_options, :components
51 alias_method_chain :flash, :components
52
53 alias_method :component_request?, :parent_controller
54 end
55 end
56
57 module ClassMethods
58 # Track parent controller to identify component requests
59 def process_with_components(request, response, parent_controller = nil) #:nodoc:
60 controller = new
61 controller.parent_controller = parent_controller
62 controller.process(request, response)
63 end
64 end
65
66 module HelperMethods
67 def render_component(options)
68 @controller.__send__(:render_component_as_string, options)
69 end
70 end
71
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)
77 end
78
79 protected
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"])
84 end
85 end
86 deprecate :render_component => "Please install render_component plugin from http://github.com/rails/render_component/tree/master"
87
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)
92
93 if redirected = response.redirected_to
94 render_component_as_string(redirected)
95 else
96 response.body
97 end
98 end
99 end
100 deprecate :render_component_as_string => "Please install render_component plugin from http://github.com/rails/render_component/tree/master"
101
102 def flash_with_components(refresh = false) #:nodoc:
103 if !defined?(@_flash) || refresh
104 @_flash =
105 if defined?(@parent_controller)
106 @parent_controller.flash
107 else
108 flash_without_components
109 end
110 end
111 @_flash
112 end
113
114 private
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
119
120 klass.process_with_components(request, new_response, self)
121 end
122
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
127 else
128 self.class
129 end
130 end
131
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
138
139 new_request.instance_variable_set(
140 :@parameters,
141 (options[:params] || {}).with_indifferent_access.update(
142 "controller" => controller_name, "action" => options[:action], "id" => options[:id]
143 )
144 )
145
146 new_request
147 end
148
149 def component_logging(options)
150 if logger
151 logger.info "Start rendering component (#{options.inspect}): "
152 result = yield
153 logger.info "\n\nEnd of component rendering"
154 result
155 else
156 yield
157 end
158 end
159
160 def set_session_options_with_components(request)
161 set_session_options_without_components(request) unless component_request?
162 end
163
164 def process_cleanup_with_components
165 process_cleanup_without_components unless component_request?
166 end
167 end
168 end
169 end