Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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