halorgium-actionpack 3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. data/CHANGELOG +5179 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/lib/abstract_controller.rb +16 -0
  5. data/lib/abstract_controller/base.rb +158 -0
  6. data/lib/abstract_controller/callbacks.rb +113 -0
  7. data/lib/abstract_controller/exceptions.rb +12 -0
  8. data/lib/abstract_controller/helpers.rb +151 -0
  9. data/lib/abstract_controller/layouts.rb +250 -0
  10. data/lib/abstract_controller/localized_cache.rb +49 -0
  11. data/lib/abstract_controller/logger.rb +61 -0
  12. data/lib/abstract_controller/rendering_controller.rb +188 -0
  13. data/lib/action_controller.rb +72 -0
  14. data/lib/action_controller/base.rb +168 -0
  15. data/lib/action_controller/caching.rb +80 -0
  16. data/lib/action_controller/caching/actions.rb +163 -0
  17. data/lib/action_controller/caching/fragments.rb +116 -0
  18. data/lib/action_controller/caching/pages.rb +154 -0
  19. data/lib/action_controller/caching/sweeping.rb +97 -0
  20. data/lib/action_controller/deprecated.rb +4 -0
  21. data/lib/action_controller/deprecated/integration_test.rb +2 -0
  22. data/lib/action_controller/deprecated/performance_test.rb +1 -0
  23. data/lib/action_controller/dispatch/dispatcher.rb +57 -0
  24. data/lib/action_controller/metal.rb +129 -0
  25. data/lib/action_controller/metal/benchmarking.rb +73 -0
  26. data/lib/action_controller/metal/compatibility.rb +145 -0
  27. data/lib/action_controller/metal/conditional_get.rb +86 -0
  28. data/lib/action_controller/metal/configuration.rb +28 -0
  29. data/lib/action_controller/metal/cookies.rb +105 -0
  30. data/lib/action_controller/metal/exceptions.rb +55 -0
  31. data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
  32. data/lib/action_controller/metal/flash.rb +162 -0
  33. data/lib/action_controller/metal/head.rb +27 -0
  34. data/lib/action_controller/metal/helpers.rb +115 -0
  35. data/lib/action_controller/metal/hide_actions.rb +47 -0
  36. data/lib/action_controller/metal/http_authentication.rb +312 -0
  37. data/lib/action_controller/metal/layouts.rb +171 -0
  38. data/lib/action_controller/metal/mime_responds.rb +317 -0
  39. data/lib/action_controller/metal/rack_convenience.rb +27 -0
  40. data/lib/action_controller/metal/redirector.rb +22 -0
  41. data/lib/action_controller/metal/render_options.rb +103 -0
  42. data/lib/action_controller/metal/rendering_controller.rb +57 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
  44. data/lib/action_controller/metal/rescuable.rb +13 -0
  45. data/lib/action_controller/metal/responder.rb +200 -0
  46. data/lib/action_controller/metal/session.rb +15 -0
  47. data/lib/action_controller/metal/session_management.rb +45 -0
  48. data/lib/action_controller/metal/streaming.rb +188 -0
  49. data/lib/action_controller/metal/testing.rb +39 -0
  50. data/lib/action_controller/metal/url_for.rb +41 -0
  51. data/lib/action_controller/metal/verification.rb +130 -0
  52. data/lib/action_controller/middleware.rb +38 -0
  53. data/lib/action_controller/notifications.rb +10 -0
  54. data/lib/action_controller/polymorphic_routes.rb +183 -0
  55. data/lib/action_controller/record_identifier.rb +91 -0
  56. data/lib/action_controller/testing/process.rb +111 -0
  57. data/lib/action_controller/testing/test_case.rb +345 -0
  58. data/lib/action_controller/translation.rb +13 -0
  59. data/lib/action_controller/url_rewriter.rb +204 -0
  60. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  61. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  62. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  65. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  66. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  67. data/lib/action_dispatch.rb +70 -0
  68. data/lib/action_dispatch/http/headers.rb +33 -0
  69. data/lib/action_dispatch/http/mime_type.rb +231 -0
  70. data/lib/action_dispatch/http/mime_types.rb +23 -0
  71. data/lib/action_dispatch/http/request.rb +539 -0
  72. data/lib/action_dispatch/http/response.rb +290 -0
  73. data/lib/action_dispatch/http/status_codes.rb +42 -0
  74. data/lib/action_dispatch/http/utils.rb +20 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +50 -0
  76. data/lib/action_dispatch/middleware/params_parser.rb +79 -0
  77. data/lib/action_dispatch/middleware/rescue.rb +26 -0
  78. data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
  79. data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
  80. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
  81. data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
  82. data/lib/action_dispatch/middleware/stack.rb +116 -0
  83. data/lib/action_dispatch/middleware/static.rb +44 -0
  84. data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
  89. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
  91. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
  92. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
  93. data/lib/action_dispatch/routing.rb +381 -0
  94. data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
  95. data/lib/action_dispatch/routing/mapper.rb +327 -0
  96. data/lib/action_dispatch/routing/route.rb +49 -0
  97. data/lib/action_dispatch/routing/route_set.rb +497 -0
  98. data/lib/action_dispatch/testing/assertions.rb +8 -0
  99. data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
  100. data/lib/action_dispatch/testing/assertions/model.rb +19 -0
  101. data/lib/action_dispatch/testing/assertions/response.rb +145 -0
  102. data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
  103. data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
  104. data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
  105. data/lib/action_dispatch/testing/integration.rb +504 -0
  106. data/lib/action_dispatch/testing/performance_test.rb +15 -0
  107. data/lib/action_dispatch/testing/test_request.rb +83 -0
  108. data/lib/action_dispatch/testing/test_response.rb +131 -0
  109. data/lib/action_pack.rb +24 -0
  110. data/lib/action_pack/version.rb +9 -0
  111. data/lib/action_view.rb +58 -0
  112. data/lib/action_view/base.rb +308 -0
  113. data/lib/action_view/context.rb +44 -0
  114. data/lib/action_view/erb/util.rb +48 -0
  115. data/lib/action_view/helpers.rb +62 -0
  116. data/lib/action_view/helpers/active_model_helper.rb +306 -0
  117. data/lib/action_view/helpers/ajax_helper.rb +68 -0
  118. data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
  119. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  120. data/lib/action_view/helpers/cache_helper.rb +39 -0
  121. data/lib/action_view/helpers/capture_helper.rb +168 -0
  122. data/lib/action_view/helpers/date_helper.rb +988 -0
  123. data/lib/action_view/helpers/debug_helper.rb +38 -0
  124. data/lib/action_view/helpers/form_helper.rb +1102 -0
  125. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  126. data/lib/action_view/helpers/form_tag_helper.rb +495 -0
  127. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  128. data/lib/action_view/helpers/number_helper.rb +311 -0
  129. data/lib/action_view/helpers/prototype_helper.rb +1309 -0
  130. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  131. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  132. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  133. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  134. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  135. data/lib/action_view/helpers/tag_helper.rb +151 -0
  136. data/lib/action_view/helpers/text_helper.rb +594 -0
  137. data/lib/action_view/helpers/translation_helper.rb +39 -0
  138. data/lib/action_view/helpers/url_helper.rb +639 -0
  139. data/lib/action_view/locale/en.yml +117 -0
  140. data/lib/action_view/paths.rb +80 -0
  141. data/lib/action_view/render/partials.rb +342 -0
  142. data/lib/action_view/render/rendering.rb +134 -0
  143. data/lib/action_view/safe_buffer.rb +28 -0
  144. data/lib/action_view/template/error.rb +101 -0
  145. data/lib/action_view/template/handler.rb +36 -0
  146. data/lib/action_view/template/handlers.rb +52 -0
  147. data/lib/action_view/template/handlers/builder.rb +17 -0
  148. data/lib/action_view/template/handlers/erb.rb +53 -0
  149. data/lib/action_view/template/handlers/rjs.rb +18 -0
  150. data/lib/action_view/template/resolver.rb +165 -0
  151. data/lib/action_view/template/template.rb +131 -0
  152. data/lib/action_view/template/text.rb +38 -0
  153. data/lib/action_view/test_case.rb +163 -0
  154. metadata +236 -0
@@ -0,0 +1,171 @@
1
+ module ActionController
2
+ # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
3
+ # repeated setups. The inclusion pattern has pages that look like this:
4
+ #
5
+ # <%= render "shared/header" %>
6
+ # Hello World
7
+ # <%= render "shared/footer" %>
8
+ #
9
+ # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
10
+ # and if you ever want to change the structure of these two includes, you'll have to change all the templates.
11
+ #
12
+ # With layouts, you can flip it around and have the common structure know where to insert changing content. This means
13
+ # that the header and footer are only mentioned in one place, like this:
14
+ #
15
+ # // The header part of this layout
16
+ # <%= yield %>
17
+ # // The footer part of this layout
18
+ #
19
+ # And then you have content pages that look like this:
20
+ #
21
+ # hello world
22
+ #
23
+ # At rendering time, the content page is computed and then inserted in the layout, like this:
24
+ #
25
+ # // The header part of this layout
26
+ # hello world
27
+ # // The footer part of this layout
28
+ #
29
+ # == Accessing shared variables
30
+ #
31
+ # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
32
+ # references that won't materialize before rendering time:
33
+ #
34
+ # <h1><%= @page_title %></h1>
35
+ # <%= yield %>
36
+ #
37
+ # ...and content pages that fulfill these references _at_ rendering time:
38
+ #
39
+ # <% @page_title = "Welcome" %>
40
+ # Off-world colonies offers you a chance to start a new life
41
+ #
42
+ # The result after rendering is:
43
+ #
44
+ # <h1>Welcome</h1>
45
+ # Off-world colonies offers you a chance to start a new life
46
+ #
47
+ # == Layout assignment
48
+ #
49
+ # You can either specify a layout declaratively (using the #layout class method) or give
50
+ # it the same name as your controller, and place it in <tt>app/views/layouts</tt>.
51
+ # If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance.
52
+ #
53
+ # For instance, if you have PostsController and a template named <tt>app/views/layouts/posts.html.erb</tt>,
54
+ # that template will be used for all actions in PostsController and controllers inheriting
55
+ # from PostsController.
56
+ #
57
+ # If you use a module, for instance Weblog::PostsController, you will need a template named
58
+ # <tt>app/views/layouts/weblog/posts.html.erb</tt>.
59
+ #
60
+ # Since all your controllers inherit from ApplicationController, they will use
61
+ # <tt>app/views/layouts/application.html.erb</tt> if no other layout is specified
62
+ # or provided.
63
+ #
64
+ # == Inheritance Examples
65
+ #
66
+ # class BankController < ActionController::Base
67
+ # layout "bank_standard"
68
+ #
69
+ # class InformationController < BankController
70
+ #
71
+ # class TellerController < BankController
72
+ # # teller.html.erb exists
73
+ #
74
+ # class TillController < TellerController
75
+ #
76
+ # class VaultController < BankController
77
+ # layout :access_level_layout
78
+ #
79
+ # class EmployeeController < BankController
80
+ # layout nil
81
+ #
82
+ # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites
83
+ # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
84
+ #
85
+ # The TellerController uses +teller.html.erb+, and TillController inherits that layout and
86
+ # uses it as well.
87
+ #
88
+ # == Types of layouts
89
+ #
90
+ # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
91
+ # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
92
+ # be done either by specifying a method reference as a symbol or using an inline method (as a proc).
93
+ #
94
+ # The method reference is the preferred approach to variable layouts and is used like this:
95
+ #
96
+ # class WeblogController < ActionController::Base
97
+ # layout :writers_and_readers
98
+ #
99
+ # def index
100
+ # # fetching posts
101
+ # end
102
+ #
103
+ # private
104
+ # def writers_and_readers
105
+ # logged_in? ? "writer_layout" : "reader_layout"
106
+ # end
107
+ #
108
+ # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
109
+ # is logged in or not.
110
+ #
111
+ # If you want to use an inline method, such as a proc, do something like this:
112
+ #
113
+ # class WeblogController < ActionController::Base
114
+ # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
115
+ #
116
+ # Of course, the most common way of specifying a layout is still just as a plain template name:
117
+ #
118
+ # class WeblogController < ActionController::Base
119
+ # layout "weblog_standard"
120
+ #
121
+ # If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
122
+ # Otherwise, it will be looked up relative to the template root.
123
+ #
124
+ # == Conditional layouts
125
+ #
126
+ # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
127
+ # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
128
+ # <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
129
+ #
130
+ # class WeblogController < ActionController::Base
131
+ # layout "weblog_standard", :except => :rss
132
+ #
133
+ # # ...
134
+ #
135
+ # end
136
+ #
137
+ # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
138
+ # around the rendered view.
139
+ #
140
+ # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
141
+ # #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
142
+ #
143
+ # == Using a different layout in the action render call
144
+ #
145
+ # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
146
+ # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
147
+ # You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
148
+ #
149
+ # class WeblogController < ActionController::Base
150
+ # layout "weblog_standard"
151
+ #
152
+ # def help
153
+ # render :action => "help", :layout => "help"
154
+ # end
155
+ # end
156
+ #
157
+ # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
158
+ module Layouts
159
+ extend ActiveSupport::Concern
160
+
161
+ include ActionController::RenderingController
162
+ include AbstractController::Layouts
163
+
164
+ module ClassMethods
165
+ # If no layout is provided, look for a layout with this name.
166
+ def _implied_layout_name
167
+ controller_path
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,317 @@
1
+ module ActionController #:nodoc:
2
+ module MimeResponds #:nodoc:
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ extlib_inheritable_accessor :responder, :mimes_for_respond_to, :instance_writer => false
7
+ self.responder = ActionController::Responder
8
+ clear_respond_to
9
+ end
10
+
11
+ module ClassMethods
12
+ # Defines mimes that are rendered by default when invoking respond_with.
13
+ #
14
+ # Examples:
15
+ #
16
+ # respond_to :html, :xml, :json
17
+ #
18
+ # All actions on your controller will respond to :html, :xml and :json.
19
+ #
20
+ # But if you want to specify it based on your actions, you can use only and
21
+ # except:
22
+ #
23
+ # respond_to :html
24
+ # respond_to :xml, :json, :except => [ :edit ]
25
+ #
26
+ # The definition above explicits that all actions respond to :html. And all
27
+ # actions except :edit respond to :xml and :json.
28
+ #
29
+ # You can specify also only parameters:
30
+ #
31
+ # respond_to :rjs, :only => :create
32
+ #
33
+ def respond_to(*mimes)
34
+ options = mimes.extract_options!
35
+
36
+ only_actions = Array(options.delete(:only))
37
+ except_actions = Array(options.delete(:except))
38
+
39
+ mimes.each do |mime|
40
+ mime = mime.to_sym
41
+ mimes_for_respond_to[mime] = {}
42
+ mimes_for_respond_to[mime][:only] = only_actions unless only_actions.empty?
43
+ mimes_for_respond_to[mime][:except] = except_actions unless except_actions.empty?
44
+ end
45
+ end
46
+
47
+ # Clear all mimes in respond_to.
48
+ #
49
+ def clear_respond_to
50
+ self.mimes_for_respond_to = ActiveSupport::OrderedHash.new
51
+ end
52
+ end
53
+
54
+ # Without web-service support, an action which collects the data for displaying a list of people
55
+ # might look something like this:
56
+ #
57
+ # def index
58
+ # @people = Person.find(:all)
59
+ # end
60
+ #
61
+ # Here's the same action, with web-service support baked in:
62
+ #
63
+ # def index
64
+ # @people = Person.find(:all)
65
+ #
66
+ # respond_to do |format|
67
+ # format.html
68
+ # format.xml { render :xml => @people.to_xml }
69
+ # end
70
+ # end
71
+ #
72
+ # What that says is, "if the client wants HTML in response to this action, just respond as we
73
+ # would have before, but if the client wants XML, return them the list of people in XML format."
74
+ # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
75
+ #
76
+ # Supposing you have an action that adds a new person, optionally creating their company
77
+ # (by name) if it does not already exist, without web-services, it might look like this:
78
+ #
79
+ # def create
80
+ # @company = Company.find_or_create_by_name(params[:company][:name])
81
+ # @person = @company.people.create(params[:person])
82
+ #
83
+ # redirect_to(person_list_url)
84
+ # end
85
+ #
86
+ # Here's the same action, with web-service support baked in:
87
+ #
88
+ # def create
89
+ # company = params[:person].delete(:company)
90
+ # @company = Company.find_or_create_by_name(company[:name])
91
+ # @person = @company.people.create(params[:person])
92
+ #
93
+ # respond_to do |format|
94
+ # format.html { redirect_to(person_list_url) }
95
+ # format.js
96
+ # format.xml { render :xml => @person.to_xml(:include => @company) }
97
+ # end
98
+ # end
99
+ #
100
+ # If the client wants HTML, we just redirect them back to the person list. If they want Javascript
101
+ # (format.js), then it is an RJS request and we render the RJS template associated with this action.
102
+ # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
103
+ # include the person's company in the rendered XML, so you get something like this:
104
+ #
105
+ # <person>
106
+ # <id>...</id>
107
+ # ...
108
+ # <company>
109
+ # <id>...</id>
110
+ # <name>...</name>
111
+ # ...
112
+ # </company>
113
+ # </person>
114
+ #
115
+ # Note, however, the extra bit at the top of that action:
116
+ #
117
+ # company = params[:person].delete(:company)
118
+ # @company = Company.find_or_create_by_name(company[:name])
119
+ #
120
+ # This is because the incoming XML document (if a web-service request is in process) can only contain a
121
+ # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
122
+ #
123
+ # person[name]=...&person[company][name]=...&...
124
+ #
125
+ # And, like this (xml-encoded):
126
+ #
127
+ # <person>
128
+ # <name>...</name>
129
+ # <company>
130
+ # <name>...</name>
131
+ # </company>
132
+ # </person>
133
+ #
134
+ # In other words, we make the request so that it operates on a single entity's person. Then, in the action,
135
+ # we extract the company data from the request, find or create the company, and then create the new person
136
+ # with the remaining data.
137
+ #
138
+ # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
139
+ # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
140
+ # and accept Rails' defaults, life will be much easier.
141
+ #
142
+ # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
143
+ # environment.rb as follows.
144
+ #
145
+ # Mime::Type.register "image/jpg", :jpg
146
+ #
147
+ # Respond to also allows you to specify a common block for different formats by using any:
148
+ #
149
+ # def index
150
+ # @people = Person.find(:all)
151
+ #
152
+ # respond_to do |format|
153
+ # format.html
154
+ # format.any(:xml, :json) { render request.format.to_sym => @people }
155
+ # end
156
+ # end
157
+ #
158
+ # In the example above, if the format is xml, it will render:
159
+ #
160
+ # render :xml => @people
161
+ #
162
+ # Or if the format is json:
163
+ #
164
+ # render :json => @people
165
+ #
166
+ # Since this is a common pattern, you can use the class method respond_to
167
+ # with the respond_with method to have the same results:
168
+ #
169
+ # class PeopleController < ApplicationController
170
+ # respond_to :html, :xml, :json
171
+ #
172
+ # def index
173
+ # @people = Person.find(:all)
174
+ # respond_with(@person)
175
+ # end
176
+ # end
177
+ #
178
+ # Be sure to check respond_with and respond_to documentation for more examples.
179
+ #
180
+ def respond_to(*mimes, &block)
181
+ raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
182
+
183
+ if response = retrieve_response_from_mimes(mimes, &block)
184
+ response.call
185
+ end
186
+ end
187
+
188
+ # respond_with wraps a resource around a responder for default representation.
189
+ # First it invokes respond_to, if a response cannot be found (ie. no block
190
+ # for the request was given and template was not available), it instantiates
191
+ # an ActionController::Responder with the controller and resource.
192
+ #
193
+ # ==== Example
194
+ #
195
+ # def index
196
+ # @users = User.all
197
+ # respond_with(@users)
198
+ # end
199
+ #
200
+ # It also accepts a block to be given. It's used to overwrite a default
201
+ # response:
202
+ #
203
+ # def destroy
204
+ # @user = User.find(params[:id])
205
+ # flash[:notice] = "User was successfully created." if @user.save
206
+ #
207
+ # respond_with(@user) do |format|
208
+ # format.html { render }
209
+ # end
210
+ # end
211
+ #
212
+ # All options given to respond_with are sent to the underlying responder,
213
+ # except for the option :responder itself. Since the responder interface
214
+ # is quite simple (it just needs to respond to call), you can even give
215
+ # a proc to it.
216
+ #
217
+ def respond_with(*resources, &block)
218
+ if response = retrieve_response_from_mimes([], &block)
219
+ options = resources.extract_options!
220
+ options.merge!(:default_response => response)
221
+ (options.delete(:responder) || responder).call(self, resources, options)
222
+ end
223
+ end
224
+
225
+ protected
226
+
227
+ # Collect mimes declared in the class method respond_to valid for the
228
+ # current action.
229
+ #
230
+ def collect_mimes_from_class_level #:nodoc:
231
+ action = action_name.to_sym
232
+
233
+ mimes_for_respond_to.keys.select do |mime|
234
+ config = mimes_for_respond_to[mime]
235
+
236
+ if config[:except]
237
+ !config[:except].include?(action)
238
+ elsif config[:only]
239
+ config[:only].include?(action)
240
+ else
241
+ true
242
+ end
243
+ end
244
+ end
245
+
246
+ # Collects mimes and return the response for the negotiated format. Returns
247
+ # nil if :not_acceptable was sent to the client.
248
+ #
249
+ def retrieve_response_from_mimes(mimes, &block)
250
+ collector = Collector.new { default_render }
251
+ mimes = collect_mimes_from_class_level if mimes.empty?
252
+ mimes.each { |mime| collector.send(mime) }
253
+ block.call(collector) if block_given?
254
+
255
+ if format = request.negotiate_mime(collector.order)
256
+ self.formats = [format.to_sym]
257
+ collector.response_for(format)
258
+ else
259
+ head :not_acceptable
260
+ nil
261
+ end
262
+ end
263
+
264
+ class Collector #:nodoc:
265
+ attr_accessor :order
266
+
267
+ def initialize(&block)
268
+ @order, @responses, @default_response = [], {}, block
269
+ end
270
+
271
+ def any(*args, &block)
272
+ if args.any?
273
+ args.each { |type| send(type, &block) }
274
+ else
275
+ custom(Mime::ALL, &block)
276
+ end
277
+ end
278
+ alias :all :any
279
+
280
+ def custom(mime_type, &block)
281
+ mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
282
+ @order << mime_type
283
+ @responses[mime_type] ||= block
284
+ end
285
+
286
+ def response_for(mime)
287
+ @responses[mime] || @responses[Mime::ALL] || @default_response
288
+ end
289
+
290
+ def self.generate_method_for_mime(mime)
291
+ sym = mime.is_a?(Symbol) ? mime : mime.to_sym
292
+ const = sym.to_s.upcase
293
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
294
+ def #{sym}(&block) # def html(&block)
295
+ custom(Mime::#{const}, &block) # custom(Mime::HTML, &block)
296
+ end # end
297
+ RUBY
298
+ end
299
+
300
+ Mime::SET.each do |mime|
301
+ generate_method_for_mime(mime)
302
+ end
303
+
304
+ def method_missing(symbol, &block)
305
+ mime_constant = Mime.const_get(symbol.to_s.upcase)
306
+
307
+ if Mime::SET.include?(mime_constant)
308
+ self.class.generate_method_for_mime(mime_constant)
309
+ send(symbol, &block)
310
+ else
311
+ super
312
+ end
313
+ end
314
+
315
+ end
316
+ end
317
+ end