Skip to content
Newer
Older
100644 311 lines (288 sloc) 10.4 KB
ddfc072 Added AbstractController::Collector.
José Valim and Mikel Lindsaar authored
1 require 'abstract_controller/collector'
e5ab4b0 @jeremy Convert to class_attribute
jeremy authored
2 require 'active_support/core_ext/class/attribute'
a9f3c9d @sikachu Using Object#in? and Object#either? in various places
sikachu authored
3 require 'active_support/core_ext/object/inclusion'
ddfc072 Added AbstractController::Collector.
José Valim and Mikel Lindsaar authored
4
1c16649 @dhh Added better support for using the same actions to output for differe…
dhh authored
5 module ActionController #:nodoc:
c59456a @joliss removed :nodoc: from ActionController::MimeResponds
joliss authored
6 module MimeResponds
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
7 extend ActiveSupport::Concern
8
944b4d5 @josevalim Add missing dependency.
josevalim authored
9 include ActionController::ImplicitRender
10
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
11 included do
e5ab4b0 @jeremy Convert to class_attribute
jeremy authored
12 class_attribute :responder, :mimes_for_respond_to
2e37eff @josevalim Unify class_inheritable_accessor and extlib_inheritable_accessor and …
josevalim authored
13 self.responder = ActionController::Responder
fa0cf66 @josevalim Add a couple more tests to respond_with.
josevalim authored
14 clear_respond_to
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
15 end
16
17 module ClassMethods
79f9fcd @wincent doc: cleanup respond_to documentation
wincent authored
18 # Defines mime types that are rendered by default when invoking
19 # <tt>respond_with</tt>.
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
20 #
21 # Examples:
22 #
23 # respond_to :html, :xml, :json
24 #
79f9fcd @wincent doc: cleanup respond_to documentation
wincent authored
25 # Specifies that all actions in the controller respond to requests
26 # for <tt>:html</tt>, <tt>:xml</tt> and <tt>:json</tt>.
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
27 #
79f9fcd @wincent doc: cleanup respond_to documentation
wincent authored
28 # To specify on per-action basis, use <tt>:only</tt> and
29 # <tt>:except</tt> with an array of actions or a single action:
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
30 #
31 # respond_to :html
32 # respond_to :xml, :json, :except => [ :edit ]
33 #
79f9fcd @wincent doc: cleanup respond_to documentation
wincent authored
34 # This specifies that all actions respond to <tt>:html</tt>
35 # and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
36 # <tt>:json</tt>.
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
37 #
5850f16 @fxn removes the RJS template handler
fxn authored
38 # respond_to :json, :only => :create
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
39 #
79f9fcd @wincent doc: cleanup respond_to documentation
wincent authored
40 # This specifies that the <tt>:create</tt> action and no other responds
5850f16 @fxn removes the RJS template handler
fxn authored
41 # to <tt>:json</tt>.
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
42 def respond_to(*mimes)
43 options = mimes.extract_options!
44
50d262f @avakhov Support symbol and string actions in AC#respond_to options
avakhov authored
45 only_actions = Array(options.delete(:only)).map(&:to_s)
46 except_actions = Array(options.delete(:except)).map(&:to_s)
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
47
e5ab4b0 @jeremy Convert to class_attribute
jeremy authored
48 new = mimes_for_respond_to.dup
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
49 mimes.each do |mime|
50 mime = mime.to_sym
e5ab4b0 @jeremy Convert to class_attribute
jeremy authored
51 new[mime] = {}
52 new[mime][:only] = only_actions unless only_actions.empty?
53 new[mime][:except] = except_actions unless except_actions.empty?
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
54 end
e5ab4b0 @jeremy Convert to class_attribute
jeremy authored
55 self.mimes_for_respond_to = new.freeze
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
56 end
57
79f9fcd @wincent doc: cleanup respond_to documentation
wincent authored
58 # Clear all mime types in <tt>respond_to</tt>.
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
59 #
fa0cf66 @josevalim Add a couple more tests to respond_with.
josevalim authored
60 def clear_respond_to
e5ab4b0 @jeremy Convert to class_attribute
jeremy authored
61 self.mimes_for_respond_to = ActiveSupport::OrderedHash.new.freeze
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
62 end
63 end
3f445b3 @josevalim Refactor Responder to only calculate available mime types. Those are …
josevalim authored
64
01f032f Added responds_to to new base.
Yehuda Katz + Carl Lerche authored
65 # Without web-service support, an action which collects the data for displaying a list of people
66 # might look something like this:
67 #
68 # def index
1a6c4c8 @willcannings Correct deprecated AR usage in ActionController::MimeResponds documen…
willcannings authored
69 # @people = Person.all
01f032f Added responds_to to new base.
Yehuda Katz + Carl Lerche authored
70 # end
71 #
72 # Here's the same action, with web-service support baked in:
73 #
74 # def index
1a6c4c8 @willcannings Correct deprecated AR usage in ActionController::MimeResponds documen…
willcannings authored
75 # @people = Person.all
01f032f Added responds_to to new base.
Yehuda Katz + Carl Lerche authored
76 #
77 # respond_to do |format|
78 # format.html
79 # format.xml { render :xml => @people.to_xml }
80 # end
81 # end
82 #
83 # What that says is, "if the client wants HTML in response to this action, just respond as we
84 # would have before, but if the client wants XML, return them the list of people in XML format."
85 # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
86 #
87 # Supposing you have an action that adds a new person, optionally creating their company
88 # (by name) if it does not already exist, without web-services, it might look like this:
89 #
90 # def create
91 # @company = Company.find_or_create_by_name(params[:company][:name])
92 # @person = @company.people.create(params[:person])
93 #
94 # redirect_to(person_list_url)
95 # end
96 #
97 # Here's the same action, with web-service support baked in:
98 #
99 # def create
100 # company = params[:person].delete(:company)
101 # @company = Company.find_or_create_by_name(company[:name])
102 # @person = @company.people.create(params[:person])
103 #
104 # respond_to do |format|
105 # format.html { redirect_to(person_list_url) }
106 # format.js
107 # format.xml { render :xml => @person.to_xml(:include => @company) }
108 # end
109 # end
110 #
5850f16 @fxn removes the RJS template handler
fxn authored
111 # If the client wants HTML, we just redirect them back to the person list. If they want JavaScript,
112 # then it is an Ajax request and we render the JavaScript template associated with this action.
01f032f Added responds_to to new base.
Yehuda Katz + Carl Lerche authored
113 # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
114 # include the person's company in the rendered XML, so you get something like this:
115 #
116 # <person>
117 # <id>...</id>
118 # ...
119 # <company>
120 # <id>...</id>
121 # <name>...</name>
122 # ...
123 # </company>
124 # </person>
125 #
126 # Note, however, the extra bit at the top of that action:
127 #
128 # company = params[:person].delete(:company)
129 # @company = Company.find_or_create_by_name(company[:name])
130 #
131 # This is because the incoming XML document (if a web-service request is in process) can only contain a
132 # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
133 #
134 # person[name]=...&person[company][name]=...&...
135 #
136 # And, like this (xml-encoded):
137 #
138 # <person>
139 # <name>...</name>
140 # <company>
141 # <name>...</name>
142 # </company>
143 # </person>
144 #
145 # In other words, we make the request so that it operates on a single entity's person. Then, in the action,
146 # we extract the company data from the request, find or create the company, and then create the new person
147 # with the remaining data.
148 #
149 # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
150 # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
151 # and accept Rails' defaults, life will be much easier.
152 #
153 # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
7e075e6 Fixed many references to the old config/environment.rb and Rails::Ini…
Benjamin Quorning authored
154 # config/initializers/mime_types.rb as follows.
01f032f Added responds_to to new base.
Yehuda Katz + Carl Lerche authored
155 #
156 # Mime::Type.register "image/jpg", :jpg
09de34c @josevalim Added respond_with.
josevalim authored
157 #
158 # Respond to also allows you to specify a common block for different formats by using any:
159 #
160 # def index
1a6c4c8 @willcannings Correct deprecated AR usage in ActionController::MimeResponds documen…
willcannings authored
161 # @people = Person.all
09de34c @josevalim Added respond_with.
josevalim authored
162 #
163 # respond_to do |format|
164 # format.html
165 # format.any(:xml, :json) { render request.format.to_sym => @people }
166 # end
167 # end
168 #
169 # In the example above, if the format is xml, it will render:
170 #
171 # render :xml => @people
172 #
173 # Or if the format is json:
174 #
175 # render :json => @people
176 #
177 # Since this is a common pattern, you can use the class method respond_to
178 # with the respond_with method to have the same results:
179 #
180 # class PeopleController < ApplicationController
181 # respond_to :html, :xml, :json
182 #
183 # def index
1a6c4c8 @willcannings Correct deprecated AR usage in ActionController::MimeResponds documen…
willcannings authored
184 # @people = Person.all
09de34c @josevalim Added respond_with.
josevalim authored
185 # respond_with(@person)
186 # end
187 # end
188 #
189 # Be sure to check respond_with and respond_to documentation for more examples.
190 #
3f445b3 @josevalim Refactor Responder to only calculate available mime types. Those are …
josevalim authored
191 def respond_to(*mimes, &block)
67b2d08 @josevalim Ensure that the proper accept header value is set during tests.
josevalim authored
192 raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
3f78de6 @josevalim Ensure that blocks are also handled inside the responder.
josevalim authored
193
194 if response = retrieve_response_from_mimes(mimes, &block)
b45302d @joshk pass respond_with options to controller render when using a template …
joshk authored
195 response.call(nil)
3f78de6 @josevalim Ensure that blocks are also handled inside the responder.
josevalim authored
196 end
09de34c @josevalim Added respond_with.
josevalim authored
197 end
672941d @jeremy Introduce a default respond_to block for custom types. Closes #8174.
jeremy authored
198
6e0ac74 @josevalim Renamed ActionController::Renderer to ActionController::Responder and…
josevalim authored
199 # respond_with wraps a resource around a responder for default representation.
1fd65c8 @josevalim Encapsulate respond_with behavior in a presenter.
josevalim authored
200 # First it invokes respond_to, if a response cannot be found (ie. no block
201 # for the request was given and template was not available), it instantiates
6e0ac74 @josevalim Renamed ActionController::Renderer to ActionController::Responder and…
josevalim authored
202 # an ActionController::Responder with the controller and resource.
5b7e81e @josevalim Allow respond_with to deal with http verb accordingly.
josevalim authored
203 #
1fd65c8 @josevalim Encapsulate respond_with behavior in a presenter.
josevalim authored
204 # ==== Example
5b7e81e @josevalim Allow respond_with to deal with http verb accordingly.
josevalim authored
205 #
1fd65c8 @josevalim Encapsulate respond_with behavior in a presenter.
josevalim authored
206 # def index
207 # @users = User.all
208 # respond_with(@users)
09de34c @josevalim Added respond_with.
josevalim authored
209 # end
210 #
1fd65c8 @josevalim Encapsulate respond_with behavior in a presenter.
josevalim authored
211 # It also accepts a block to be given. It's used to overwrite a default
212 # response:
f59984c @josevalim Add nagivational behavior to respond_with.
josevalim authored
213 #
aebbbc7 @willcannings Fix respond_with example code so it makes sense
willcannings authored
214 # def create
215 # @user = User.new(params[:user])
1fd65c8 @josevalim Encapsulate respond_with behavior in a presenter.
josevalim authored
216 # flash[:notice] = "User was successfully created." if @user.save
5b7e81e @josevalim Allow respond_with to deal with http verb accordingly.
josevalim authored
217 #
1fd65c8 @josevalim Encapsulate respond_with behavior in a presenter.
josevalim authored
218 # respond_with(@user) do |format|
219 # format.html { render }
220 # end
7034272 @josevalim Add destroyed? to ActiveRecord, include tests for polymorphic urls fo…
josevalim authored
221 # end
222 #
6e0ac74 @josevalim Renamed ActionController::Renderer to ActionController::Responder and…
josevalim authored
223 # All options given to respond_with are sent to the underlying responder,
224 # except for the option :responder itself. Since the responder interface
aed135d @josevalim Renamed presenter to renderer, added some documentation and defined i…
josevalim authored
225 # is quite simple (it just needs to respond to call), you can even give
226 # a proc to it.
7034272 @josevalim Add destroyed? to ActiveRecord, include tests for polymorphic urls fo…
josevalim authored
227 #
a64abdd @amaia Explain in the method doc that you need to call respond_to at the cla…
amaia authored
228 # In order to use respond_with, first you need to declare the formats your
229 # controller responds to in the class level with a call to <tt>respond_to</tt>.
230 #
4f9047e @josevalim Ensure collections are not treated as nested resources.
josevalim authored
231 def respond_with(*resources, &block)
d2e7c1b @josevalim Raise an error if respond_with is invoked and no format is declared.
josevalim authored
232 raise "In order to use respond_with, first you need to declare the formats your " <<
e5ab4b0 @jeremy Convert to class_attribute
jeremy authored
233 "controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
d2e7c1b @josevalim Raise an error if respond_with is invoked and no format is declared.
josevalim authored
234
235 if response = retrieve_response_from_mimes(&block)
b1667c7 @chriseppstein Correctly handle the case of an API response that returns a hash by t…
chriseppstein authored
236 options = resources.size == 1 ? {} : resources.extract_options!
3f78de6 @josevalim Ensure that blocks are also handled inside the responder.
josevalim authored
237 options.merge!(:default_response => response)
e5ab4b0 @jeremy Convert to class_attribute
jeremy authored
238 (options.delete(:responder) || self.class.responder).call(self, resources, options)
684a6b3 @josevalim Attempt to render the template inside the responder, so it can be use…
josevalim authored
239 end
aed135d @josevalim Renamed presenter to renderer, added some documentation and defined i…
josevalim authored
240 end
7a4a679 @josevalim Remove any resource logic from respond_to.
josevalim authored
241
242 protected
243
09de34c @josevalim Added respond_with.
josevalim authored
244 # Collect mimes declared in the class method respond_to valid for the
245 # current action.
246 #
247 def collect_mimes_from_class_level #:nodoc:
50d262f @avakhov Support symbol and string actions in AC#respond_to options
avakhov authored
248 action = action_name.to_s
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
249
e5ab4b0 @jeremy Convert to class_attribute
jeremy authored
250 self.class.mimes_for_respond_to.keys.select do |mime|
251 config = self.class.mimes_for_respond_to[mime]
09de34c @josevalim Added respond_with.
josevalim authored
252
253 if config[:except]
a9f3c9d @sikachu Using Object#in? and Object#either? in various places
sikachu authored
254 !action.in?(config[:except])
09de34c @josevalim Added respond_with.
josevalim authored
255 elsif config[:only]
a9f3c9d @sikachu Using Object#in? and Object#either? in various places
sikachu authored
256 action.in?(config[:only])
09de34c @josevalim Added respond_with.
josevalim authored
257 else
258 true
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
259 end
260 end
09de34c @josevalim Added respond_with.
josevalim authored
261 end
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
262
3f78de6 @josevalim Ensure that blocks are also handled inside the responder.
josevalim authored
263 # Collects mimes and return the response for the negotiated format. Returns
264 # nil if :not_acceptable was sent to the client.
684a6b3 @josevalim Attempt to render the template inside the responder, so it can be use…
josevalim authored
265 #
c59456a @joliss removed :nodoc: from ActionController::MimeResponds
joliss authored
266 def retrieve_response_from_mimes(mimes=nil, &block) #:nodoc:
d2e7c1b @josevalim Raise an error if respond_with is invoked and no format is declared.
josevalim authored
267 mimes ||= collect_mimes_from_class_level
b45302d @joshk pass respond_with options to controller render when using a template …
joshk authored
268 collector = Collector.new(mimes) { |options| default_render(options || {}) }
3f78de6 @josevalim Ensure that blocks are also handled inside the responder.
josevalim authored
269 block.call(collector) if block_given?
684a6b3 @josevalim Attempt to render the template inside the responder, so it can be use…
josevalim authored
270
271 if format = request.negotiate_mime(collector.order)
f2c0a35 @josevalim Finish cleaning up rendering stack from views and move assigns evalua…
josevalim authored
272 self.content_type ||= format.to_s
a09e992 @josevalim Ensure details are frozen after @details_keys lookup. The implementat…
josevalim authored
273 lookup_context.freeze_formats([format.to_sym])
3f78de6 @josevalim Ensure that blocks are also handled inside the responder.
josevalim authored
274 collector.response_for(format)
684a6b3 @josevalim Attempt to render the template inside the responder, so it can be use…
josevalim authored
275 else
276 head :not_acceptable
3f78de6 @josevalim Ensure that blocks are also handled inside the responder.
josevalim authored
277 nil
684a6b3 @josevalim Attempt to render the template inside the responder, so it can be use…
josevalim authored
278 end
279 end
280
6e0ac74 @josevalim Renamed ActionController::Renderer to ActionController::Responder and…
josevalim authored
281 class Collector #:nodoc:
ddfc072 Added AbstractController::Collector.
José Valim and Mikel Lindsaar authored
282 include AbstractController::Collector
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
283 attr_accessor :order
3f445b3 @josevalim Refactor Responder to only calculate available mime types. Those are …
josevalim authored
284
a98d9d6 @joshk move the setting up of the mime collector into the collector on init
joshk authored
285 def initialize(mimes, &block)
3f78de6 @josevalim Ensure that blocks are also handled inside the responder.
josevalim authored
286 @order, @responses, @default_response = [], {}, block
74049b5 @spastorino implicit self here
spastorino authored
287 mimes.each { |mime| send(mime) }
7af12d0 @dhh Added synonym and custom type handling to respond_to [DHH]
dhh authored
288 end
6480d49 @jamis Add MimeResponds::Responder#any for managing multiple types with iden…
jamis authored
289
290 def any(*args, &block)
011e469 @lifo Make MimeResponds::Responder#any work without explicit types. Closes #…
lifo authored
291 if args.any?
292 args.each { |type| send(type, &block) }
293 else
3e8ba61 @josevalim Refactor even more Responder. Move mime negotiation to request and ad…
josevalim authored
294 custom(Mime::ALL, &block)
011e469 @lifo Make MimeResponds::Responder#any work without explicit types. Closes #…
lifo authored
295 end
6dea52c @dhh Finish custom handling [DHH]
dhh authored
296 end
7e280c3 @josevalim Remove Mime::ALL from Mime::SET.
josevalim authored
297 alias :all :any
3f445b3 @josevalim Refactor Responder to only calculate available mime types. Those are …
josevalim authored
298
299 def custom(mime_type, &block)
6e55916 @spastorino There's no need for ternary op here
spastorino authored
300 mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
3f445b3 @josevalim Refactor Responder to only calculate available mime types. Those are …
josevalim authored
301 @order << mime_type
302 @responses[mime_type] ||= block
6dc1288 @wycats Remove method missing use in respond_to
wycats authored
303 end
672941d @jeremy Introduce a default respond_to block for custom types. Closes #8174.
jeremy authored
304
3f445b3 @josevalim Refactor Responder to only calculate available mime types. Those are …
josevalim authored
305 def response_for(mime)
3f78de6 @josevalim Ensure that blocks are also handled inside the responder.
josevalim authored
306 @responses[mime] || @responses[Mime::ALL] || @default_response
3f445b3 @josevalim Refactor Responder to only calculate available mime types. Those are …
josevalim authored
307 end
1c16649 @dhh Added better support for using the same actions to output for differe…
dhh authored
308 end
309 end
0ee1cb2 @jeremy Ruby 1.9 compat, consistent load paths
jeremy authored
310 end
Something went wrong with that request. Please try again.