actionpack 3.2.19 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +850 -401
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -288
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +39 -37
  7. data/lib/abstract_controller/callbacks.rb +101 -82
  8. data/lib/abstract_controller/collector.rb +7 -3
  9. data/lib/abstract_controller/helpers.rb +25 -13
  10. data/lib/abstract_controller/layouts.rb +74 -74
  11. data/lib/abstract_controller/logger.rb +1 -2
  12. data/lib/abstract_controller/rendering.rb +30 -13
  13. data/lib/abstract_controller/translation.rb +16 -1
  14. data/lib/abstract_controller/url_for.rb +6 -6
  15. data/lib/abstract_controller/view_paths.rb +1 -1
  16. data/lib/abstract_controller.rb +1 -8
  17. data/lib/action_controller/base.rb +46 -22
  18. data/lib/action_controller/caching/fragments.rb +23 -53
  19. data/lib/action_controller/caching.rb +46 -33
  20. data/lib/action_controller/deprecated/integration_test.rb +3 -0
  21. data/lib/action_controller/deprecated.rb +5 -1
  22. data/lib/action_controller/log_subscriber.rb +16 -8
  23. data/lib/action_controller/metal/conditional_get.rb +76 -32
  24. data/lib/action_controller/metal/data_streaming.rb +20 -26
  25. data/lib/action_controller/metal/exceptions.rb +19 -6
  26. data/lib/action_controller/metal/flash.rb +24 -9
  27. data/lib/action_controller/metal/force_ssl.rb +70 -12
  28. data/lib/action_controller/metal/head.rb +25 -4
  29. data/lib/action_controller/metal/helpers.rb +5 -9
  30. data/lib/action_controller/metal/hide_actions.rb +0 -1
  31. data/lib/action_controller/metal/http_authentication.rb +107 -83
  32. data/lib/action_controller/metal/implicit_render.rb +1 -1
  33. data/lib/action_controller/metal/instrumentation.rb +2 -1
  34. data/lib/action_controller/metal/live.rb +175 -0
  35. data/lib/action_controller/metal/mime_responds.rb +161 -47
  36. data/lib/action_controller/metal/params_wrapper.rb +112 -74
  37. data/lib/action_controller/metal/rack_delegation.rb +9 -3
  38. data/lib/action_controller/metal/redirecting.rb +15 -20
  39. data/lib/action_controller/metal/renderers.rb +11 -9
  40. data/lib/action_controller/metal/rendering.rb +9 -1
  41. data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
  42. data/lib/action_controller/metal/responder.rb +20 -19
  43. data/lib/action_controller/metal/streaming.rb +12 -18
  44. data/lib/action_controller/metal/strong_parameters.rb +520 -0
  45. data/lib/action_controller/metal/testing.rb +13 -18
  46. data/lib/action_controller/metal/url_for.rb +28 -25
  47. data/lib/action_controller/metal.rb +17 -32
  48. data/lib/action_controller/model_naming.rb +12 -0
  49. data/lib/action_controller/railtie.rb +33 -17
  50. data/lib/action_controller/railties/helpers.rb +22 -0
  51. data/lib/action_controller/record_identifier.rb +18 -72
  52. data/lib/action_controller/test_case.rb +251 -131
  53. data/lib/action_controller/vendor/html-scanner.rb +4 -19
  54. data/lib/action_controller.rb +15 -6
  55. data/lib/action_dispatch/http/cache.rb +63 -11
  56. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  57. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  58. data/lib/action_dispatch/http/headers.rb +49 -17
  59. data/lib/action_dispatch/http/mime_negotiation.rb +24 -1
  60. data/lib/action_dispatch/http/mime_type.rb +154 -100
  61. data/lib/action_dispatch/http/mime_types.rb +1 -1
  62. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  63. data/lib/action_dispatch/http/parameters.rb +28 -28
  64. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  65. data/lib/action_dispatch/http/request.rb +64 -18
  66. data/lib/action_dispatch/http/response.rb +130 -35
  67. data/lib/action_dispatch/http/upload.rb +63 -20
  68. data/lib/action_dispatch/http/url.rb +98 -35
  69. data/lib/action_dispatch/journey/backwards.rb +5 -0
  70. data/lib/action_dispatch/journey/formatter.rb +146 -0
  71. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  72. data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
  73. data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
  74. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  75. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  76. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  77. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  78. data/lib/action_dispatch/journey/nodes/node.rb +124 -0
  79. data/lib/action_dispatch/journey/parser.rb +206 -0
  80. data/lib/action_dispatch/journey/parser.y +47 -0
  81. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  82. data/lib/action_dispatch/journey/path/pattern.rb +196 -0
  83. data/lib/action_dispatch/journey/route.rb +124 -0
  84. data/lib/action_dispatch/journey/router/strexp.rb +24 -0
  85. data/lib/action_dispatch/journey/router/utils.rb +54 -0
  86. data/lib/action_dispatch/journey/router.rb +166 -0
  87. data/lib/action_dispatch/journey/routes.rb +75 -0
  88. data/lib/action_dispatch/journey/scanner.rb +61 -0
  89. data/lib/action_dispatch/journey/visitors.rb +197 -0
  90. data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
  91. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  92. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  93. data/lib/action_dispatch/journey.rb +5 -0
  94. data/lib/action_dispatch/middleware/callbacks.rb +9 -4
  95. data/lib/action_dispatch/middleware/cookies.rb +259 -114
  96. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
  97. data/lib/action_dispatch/middleware/exception_wrapper.rb +29 -3
  98. data/lib/action_dispatch/middleware/flash.rb +58 -58
  99. data/lib/action_dispatch/middleware/params_parser.rb +14 -29
  100. data/lib/action_dispatch/middleware/public_exceptions.rb +30 -14
  101. data/lib/action_dispatch/middleware/reloader.rb +6 -6
  102. data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
  103. data/lib/action_dispatch/middleware/request_id.rb +2 -6
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  105. data/lib/action_dispatch/middleware/session/cookie_store.rb +82 -28
  106. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  107. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
  108. data/lib/action_dispatch/middleware/ssl.rb +70 -0
  109. data/lib/action_dispatch/middleware/stack.rb +6 -1
  110. data/lib/action_dispatch/middleware/static.rb +2 -1
  111. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
  112. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
  113. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +7 -9
  114. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
  115. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +127 -5
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
  118. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
  119. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
  120. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  121. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
  122. data/lib/action_dispatch/railtie.rb +16 -6
  123. data/lib/action_dispatch/request/session.rb +181 -0
  124. data/lib/action_dispatch/routing/inspector.rb +240 -0
  125. data/lib/action_dispatch/routing/mapper.rb +540 -291
  126. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
  127. data/lib/action_dispatch/routing/redirection.rb +46 -29
  128. data/lib/action_dispatch/routing/route_set.rb +207 -164
  129. data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
  130. data/lib/action_dispatch/routing/url_for.rb +48 -33
  131. data/lib/action_dispatch/routing.rb +48 -83
  132. data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
  133. data/lib/action_dispatch/testing/assertions/response.rb +32 -40
  134. data/lib/action_dispatch/testing/assertions/routing.rb +42 -41
  135. data/lib/action_dispatch/testing/assertions/selector.rb +17 -22
  136. data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
  137. data/lib/action_dispatch/testing/integration.rb +65 -51
  138. data/lib/action_dispatch/testing/test_process.rb +9 -6
  139. data/lib/action_dispatch/testing/test_request.rb +7 -3
  140. data/lib/action_dispatch.rb +21 -15
  141. data/lib/action_pack/version.rb +7 -6
  142. data/lib/action_pack.rb +1 -1
  143. data/lib/action_view/base.rb +15 -34
  144. data/lib/action_view/buffers.rb +7 -1
  145. data/lib/action_view/context.rb +4 -4
  146. data/lib/action_view/dependency_tracker.rb +93 -0
  147. data/lib/action_view/digestor.rb +85 -0
  148. data/lib/action_view/flows.rb +1 -4
  149. data/lib/action_view/helpers/active_model_helper.rb +3 -4
  150. data/lib/action_view/helpers/asset_tag_helper.rb +215 -352
  151. data/lib/action_view/helpers/asset_url_helper.rb +355 -0
  152. data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
  153. data/lib/action_view/helpers/cache_helper.rb +150 -18
  154. data/lib/action_view/helpers/capture_helper.rb +44 -31
  155. data/lib/action_view/helpers/csrf_helper.rb +0 -2
  156. data/lib/action_view/helpers/date_helper.rb +269 -248
  157. data/lib/action_view/helpers/debug_helper.rb +10 -11
  158. data/lib/action_view/helpers/form_helper.rb +931 -537
  159. data/lib/action_view/helpers/form_options_helper.rb +341 -166
  160. data/lib/action_view/helpers/form_tag_helper.rb +190 -90
  161. data/lib/action_view/helpers/javascript_helper.rb +23 -16
  162. data/lib/action_view/helpers/number_helper.rb +148 -329
  163. data/lib/action_view/helpers/output_safety_helper.rb +3 -3
  164. data/lib/action_view/helpers/record_tag_helper.rb +17 -22
  165. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  166. data/lib/action_view/helpers/sanitize_helper.rb +3 -6
  167. data/lib/action_view/helpers/tag_helper.rb +46 -33
  168. data/lib/action_view/helpers/tags/base.rb +147 -0
  169. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  170. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  171. data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
  172. data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
  173. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  174. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  175. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  176. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  177. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  178. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  179. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  180. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  181. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  182. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  183. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  184. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  185. data/lib/action_view/helpers/tags/label.rb +65 -0
  186. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  187. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  188. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  189. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  190. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  191. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  192. data/lib/action_view/helpers/tags/select.rb +40 -0
  193. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  194. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  195. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  196. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  197. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  198. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  199. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  200. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  201. data/lib/action_view/helpers/tags.rb +39 -0
  202. data/lib/action_view/helpers/text_helper.rb +130 -114
  203. data/lib/action_view/helpers/translation_helper.rb +32 -16
  204. data/lib/action_view/helpers/url_helper.rb +211 -270
  205. data/lib/action_view/helpers.rb +2 -4
  206. data/lib/action_view/locale/en.yml +1 -105
  207. data/lib/action_view/log_subscriber.rb +6 -4
  208. data/lib/action_view/lookup_context.rb +15 -28
  209. data/lib/action_view/model_naming.rb +12 -0
  210. data/lib/action_view/path_set.rb +8 -20
  211. data/lib/action_view/railtie.rb +6 -22
  212. data/lib/action_view/record_identifier.rb +84 -0
  213. data/lib/action_view/renderer/abstract_renderer.rb +25 -19
  214. data/lib/action_view/renderer/partial_renderer.rb +158 -81
  215. data/lib/action_view/renderer/renderer.rb +8 -12
  216. data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
  217. data/lib/action_view/renderer/template_renderer.rb +12 -10
  218. data/lib/action_view/routing_url_for.rb +107 -0
  219. data/lib/action_view/template/error.rb +22 -12
  220. data/lib/action_view/template/handlers/builder.rb +1 -1
  221. data/lib/action_view/template/handlers/erb.rb +40 -19
  222. data/lib/action_view/template/handlers/raw.rb +11 -0
  223. data/lib/action_view/template/handlers.rb +12 -9
  224. data/lib/action_view/template/resolver.rb +107 -53
  225. data/lib/action_view/template/text.rb +12 -8
  226. data/lib/action_view/template/types.rb +57 -0
  227. data/lib/action_view/template.rb +25 -23
  228. data/lib/action_view/test_case.rb +67 -42
  229. data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
  230. data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
  231. data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +13 -2
  232. data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +9 -9
  233. data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
  234. data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
  235. data/lib/action_view/vendor/html-scanner.rb +20 -0
  236. data/lib/action_view.rb +17 -8
  237. metadata +184 -214
  238. data/lib/action_controller/caching/actions.rb +0 -185
  239. data/lib/action_controller/caching/pages.rb +0 -187
  240. data/lib/action_controller/caching/sweeping.rb +0 -97
  241. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  242. data/lib/action_controller/metal/compatibility.rb +0 -65
  243. data/lib/action_controller/metal/session_management.rb +0 -14
  244. data/lib/action_controller/railties/paths.rb +0 -25
  245. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  246. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  247. data/lib/action_dispatch/middleware/head.rb +0 -18
  248. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  249. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  250. data/lib/action_view/asset_paths.rb +0 -142
  251. data/lib/action_view/helpers/asset_paths.rb +0 -7
  252. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  253. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  254. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  255. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  256. data/lib/sprockets/assets.rake +0 -99
  257. data/lib/sprockets/bootstrap.rb +0 -37
  258. data/lib/sprockets/compressors.rb +0 -83
  259. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  260. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  261. data/lib/sprockets/helpers.rb +0 -6
  262. data/lib/sprockets/railtie.rb +0 -62
  263. data/lib/sprockets/static_compiler.rb +0 -56
@@ -0,0 +1,175 @@
1
+ require 'action_dispatch/http/response'
2
+ require 'delegate'
3
+
4
+ module ActionController
5
+ # Mix this module in to your controller, and all actions in that controller
6
+ # will be able to stream data to the client as it's written.
7
+ #
8
+ # class MyController < ActionController::Base
9
+ # include ActionController::Live
10
+ #
11
+ # def stream
12
+ # response.headers['Content-Type'] = 'text/event-stream'
13
+ # 100.times {
14
+ # response.stream.write "hello world\n"
15
+ # sleep 1
16
+ # }
17
+ # ensure
18
+ # response.stream.close
19
+ # end
20
+ # end
21
+ #
22
+ # There are a few caveats with this use. You *cannot* write headers after the
23
+ # response has been committed (Response#committed? will return truthy).
24
+ # Calling +write+ or +close+ on the response stream will cause the response
25
+ # object to be committed. Make sure all headers are set before calling write
26
+ # or close on your stream.
27
+ #
28
+ # You *must* call close on your stream when you're finished, otherwise the
29
+ # socket may be left open forever.
30
+ #
31
+ # The final caveat is that your actions are executed in a separate thread than
32
+ # the main thread. Make sure your actions are thread safe, and this shouldn't
33
+ # be a problem (don't share state across threads, etc).
34
+ module Live
35
+ class Buffer < ActionDispatch::Response::Buffer #:nodoc:
36
+ def initialize(response)
37
+ @error_callback = nil
38
+ super(response, SizedQueue.new(10))
39
+ end
40
+
41
+ def write(string)
42
+ unless @response.committed?
43
+ @response.headers["Cache-Control"] = "no-cache"
44
+ @response.headers.delete "Content-Length"
45
+ end
46
+
47
+ super
48
+ end
49
+
50
+ def each
51
+ while str = @buf.pop
52
+ yield str
53
+ end
54
+ end
55
+
56
+ def close
57
+ super
58
+ @buf.push nil
59
+ end
60
+
61
+ def on_error(&block)
62
+ @error_callback = block
63
+ end
64
+
65
+ def call_on_error
66
+ @error_callback.call
67
+ end
68
+ end
69
+
70
+ class Response < ActionDispatch::Response #:nodoc: all
71
+ class Header < DelegateClass(Hash)
72
+ def initialize(response, header)
73
+ @response = response
74
+ super(header)
75
+ end
76
+
77
+ def []=(k,v)
78
+ if @response.committed?
79
+ raise ActionDispatch::IllegalStateError, 'header already sent'
80
+ end
81
+
82
+ super
83
+ end
84
+
85
+ def merge(other)
86
+ self.class.new @response, __getobj__.merge(other)
87
+ end
88
+
89
+ def to_hash
90
+ __getobj__.dup
91
+ end
92
+ end
93
+
94
+ def commit!
95
+ headers.freeze
96
+ super
97
+ end
98
+
99
+ private
100
+
101
+ def build_buffer(response, body)
102
+ buf = Live::Buffer.new response
103
+ body.each { |part| buf.write part }
104
+ buf
105
+ end
106
+
107
+ def merge_default_headers(original, default)
108
+ Header.new self, super
109
+ end
110
+
111
+ def handle_conditional_get!
112
+ super unless committed?
113
+ end
114
+ end
115
+
116
+ def process(name)
117
+ t1 = Thread.current
118
+ locals = t1.keys.map { |key| [key, t1[key]] }
119
+
120
+ # This processes the action in a child thread. It lets us return the
121
+ # response code and headers back up the rack stack, and still process
122
+ # the body in parallel with sending data to the client
123
+ Thread.new {
124
+ t2 = Thread.current
125
+ t2.abort_on_exception = true
126
+
127
+ # Since we're processing the view in a different thread, copy the
128
+ # thread locals from the main thread to the child thread. :'(
129
+ locals.each { |k,v| t2[k] = v }
130
+
131
+ begin
132
+ super(name)
133
+ rescue => e
134
+ begin
135
+ @_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
136
+ @_response.stream.call_on_error
137
+ rescue => exception
138
+ log_error(exception)
139
+ ensure
140
+ log_error(e)
141
+ @_response.stream.close
142
+ end
143
+ ensure
144
+ @_response.commit!
145
+ end
146
+ }
147
+
148
+ @_response.await_commit
149
+ end
150
+
151
+ def log_error(exception)
152
+ logger = ActionController::Base.logger
153
+ return unless logger
154
+
155
+ message = "\n#{exception.class} (#{exception.message}):\n"
156
+ message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
157
+ message << " " << exception.backtrace.join("\n ")
158
+ logger.fatal("#{message}\n\n")
159
+ end
160
+
161
+ def response_body=(body)
162
+ super
163
+ response.stream.close if response
164
+ end
165
+
166
+ def set_response!(request)
167
+ if request.env["HTTP_VERSION"] == "HTTP/1.0"
168
+ super
169
+ else
170
+ @_response = Live::Response.new
171
+ @_response.request = request
172
+ end
173
+ end
174
+ end
175
+ end
@@ -1,13 +1,10 @@
1
+ require 'active_support/core_ext/array/extract_options'
1
2
  require 'abstract_controller/collector'
2
- require 'active_support/core_ext/class/attribute'
3
- require 'active_support/core_ext/object/inclusion'
4
3
 
5
4
  module ActionController #:nodoc:
6
5
  module MimeResponds
7
6
  extend ActiveSupport::Concern
8
7
 
9
- include ActionController::ImplicitRender
10
-
11
8
  included do
12
9
  class_attribute :responder, :mimes_for_respond_to
13
10
  self.responder = ActionController::Responder
@@ -18,8 +15,6 @@ module ActionController #:nodoc:
18
15
  # Defines mime types that are rendered by default when invoking
19
16
  # <tt>respond_with</tt>.
20
17
  #
21
- # Examples:
22
- #
23
18
  # respond_to :html, :xml, :json
24
19
  #
25
20
  # Specifies that all actions in the controller respond to requests
@@ -29,13 +24,13 @@ module ActionController #:nodoc:
29
24
  # <tt>:except</tt> with an array of actions or a single action:
30
25
  #
31
26
  # respond_to :html
32
- # respond_to :xml, :json, :except => [ :edit ]
27
+ # respond_to :xml, :json, except: [ :edit ]
33
28
  #
34
29
  # This specifies that all actions respond to <tt>:html</tt>
35
30
  # and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
36
31
  # <tt>:json</tt>.
37
32
  #
38
- # respond_to :json, :only => :create
33
+ # respond_to :json, only: :create
39
34
  #
40
35
  # This specifies that the <tt>:create</tt> action and no other responds
41
36
  # to <tt>:json</tt>.
@@ -58,7 +53,7 @@ module ActionController #:nodoc:
58
53
  # Clear all mime types in <tt>respond_to</tt>.
59
54
  #
60
55
  def clear_respond_to
61
- self.mimes_for_respond_to = ActiveSupport::OrderedHash.new.freeze
56
+ self.mimes_for_respond_to = Hash.new.freeze
62
57
  end
63
58
  end
64
59
 
@@ -76,7 +71,7 @@ module ActionController #:nodoc:
76
71
  #
77
72
  # respond_to do |format|
78
73
  # format.html
79
- # format.xml { render :xml => @people.to_xml }
74
+ # format.xml { render xml: @people }
80
75
  # end
81
76
  # end
82
77
  #
@@ -88,7 +83,7 @@ module ActionController #:nodoc:
88
83
  # (by name) if it does not already exist, without web-services, it might look like this:
89
84
  #
90
85
  # def create
91
- # @company = Company.find_or_create_by_name(params[:company][:name])
86
+ # @company = Company.find_or_create_by(name: params[:company][:name])
92
87
  # @person = @company.people.create(params[:person])
93
88
  #
94
89
  # redirect_to(person_list_url)
@@ -98,13 +93,13 @@ module ActionController #:nodoc:
98
93
  #
99
94
  # def create
100
95
  # company = params[:person].delete(:company)
101
- # @company = Company.find_or_create_by_name(company[:name])
96
+ # @company = Company.find_or_create_by(name: company[:name])
102
97
  # @person = @company.people.create(params[:person])
103
98
  #
104
99
  # respond_to do |format|
105
100
  # format.html { redirect_to(person_list_url) }
106
101
  # format.js
107
- # format.xml { render :xml => @person.to_xml(:include => @company) }
102
+ # format.xml { render xml: @person.to_xml(include: @company) }
108
103
  # end
109
104
  # end
110
105
  #
@@ -126,7 +121,7 @@ module ActionController #:nodoc:
126
121
  # Note, however, the extra bit at the top of that action:
127
122
  #
128
123
  # company = params[:person].delete(:company)
129
- # @company = Company.find_or_create_by_name(company[:name])
124
+ # @company = Company.find_or_create_by(name: company[:name])
130
125
  #
131
126
  # This is because the incoming XML document (if a web-service request is in process) can only contain a
132
127
  # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
@@ -168,11 +163,11 @@ module ActionController #:nodoc:
168
163
  #
169
164
  # In the example above, if the format is xml, it will render:
170
165
  #
171
- # render :xml => @people
166
+ # render xml: @people
172
167
  #
173
168
  # Or if the format is json:
174
169
  #
175
- # render :json => @people
170
+ # render json: @people
176
171
  #
177
172
  # Since this is a common pattern, you can use the class method respond_to
178
173
  # with the respond_with method to have the same results:
@@ -186,31 +181,117 @@ module ActionController #:nodoc:
186
181
  # end
187
182
  # end
188
183
  #
189
- # Be sure to check respond_with and respond_to documentation for more examples.
190
- #
184
+ # Be sure to check the documentation of +respond_with+ and
185
+ # <tt>ActionController::MimeResponds.respond_to</tt> for more examples.
191
186
  def respond_to(*mimes, &block)
192
187
  raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
193
188
 
194
189
  if collector = retrieve_collector_from_mimes(mimes, &block)
195
190
  response = collector.response
196
- response ? response.call : default_render({})
191
+ response ? response.call : render({})
197
192
  end
198
193
  end
199
194
 
200
- # respond_with wraps a resource around a responder for default representation.
201
- # First it invokes respond_to, if a response cannot be found (ie. no block
202
- # for the request was given and template was not available), it instantiates
203
- # an ActionController::Responder with the controller and resource.
195
+ # For a given controller action, respond_with generates an appropriate
196
+ # response based on the mime-type requested by the client.
204
197
  #
205
- # ==== Example
198
+ # If the method is called with just a resource, as in this example -
206
199
  #
207
- # def index
208
- # @users = User.all
209
- # respond_with(@users)
200
+ # class PeopleController < ApplicationController
201
+ # respond_to :html, :xml, :json
202
+ #
203
+ # def index
204
+ # @people = Person.all
205
+ # respond_with @people
206
+ # end
207
+ # end
208
+ #
209
+ # then the mime-type of the response is typically selected based on the
210
+ # request's Accept header and the set of available formats declared
211
+ # by previous calls to the controller's class method +respond_to+. Alternatively
212
+ # the mime-type can be selected by explicitly setting <tt>request.format</tt> in
213
+ # the controller.
214
+ #
215
+ # If an acceptable format is not identified, the application returns a
216
+ # '406 - not acceptable' status. Otherwise, the default response is to render
217
+ # a template named after the current action and the selected format,
218
+ # e.g. <tt>index.html.erb</tt>. If no template is available, the behavior
219
+ # depends on the selected format:
220
+ #
221
+ # * for an html response - if the request method is +get+, an exception
222
+ # is raised but for other requests such as +post+ the response
223
+ # depends on whether the resource has any validation errors (i.e.
224
+ # assuming that an attempt has been made to save the resource,
225
+ # e.g. by a +create+ action) -
226
+ # 1. If there are no errors, i.e. the resource
227
+ # was saved successfully, the response +redirect+'s to the resource
228
+ # i.e. its +show+ action.
229
+ # 2. If there are validation errors, the response
230
+ # renders a default action, which is <tt>:new</tt> for a
231
+ # +post+ request or <tt>:edit</tt> for +patch+ or +put+.
232
+ # Thus an example like this -
233
+ #
234
+ # respond_to :html, :xml
235
+ #
236
+ # def create
237
+ # @user = User.new(params[:user])
238
+ # flash[:notice] = 'User was successfully created.' if @user.save
239
+ # respond_with(@user)
240
+ # end
241
+ #
242
+ # is equivalent, in the absence of <tt>create.html.erb</tt>, to -
243
+ #
244
+ # def create
245
+ # @user = User.new(params[:user])
246
+ # respond_to do |format|
247
+ # if @user.save
248
+ # flash[:notice] = 'User was successfully created.'
249
+ # format.html { redirect_to(@user) }
250
+ # format.xml { render xml: @user }
251
+ # else
252
+ # format.html { render action: "new" }
253
+ # format.xml { render xml: @user }
254
+ # end
255
+ # end
256
+ # end
257
+ #
258
+ # * for a javascript request - if the template isn't found, an exception is
259
+ # raised.
260
+ # * for other requests - i.e. data formats such as xml, json, csv etc, if
261
+ # the resource passed to +respond_with+ responds to <code>to_<format></code>,
262
+ # the method attempts to render the resource in the requested format
263
+ # directly, e.g. for an xml request, the response is equivalent to calling
264
+ # <code>render xml: resource</code>.
265
+ #
266
+ # === Nested resources
267
+ #
268
+ # As outlined above, the +resources+ argument passed to +respond_with+
269
+ # can play two roles. It can be used to generate the redirect url
270
+ # for successful html requests (e.g. for +create+ actions when
271
+ # no template exists), while for formats other than html and javascript
272
+ # it is the object that gets rendered, by being converted directly to the
273
+ # required format (again assuming no template exists).
274
+ #
275
+ # For redirecting successful html requests, +respond_with+ also supports
276
+ # the use of nested resources, which are supplied in the same way as
277
+ # in <code>form_for</code> and <code>polymorphic_url</code>. For example -
278
+ #
279
+ # def create
280
+ # @project = Project.find(params[:project_id])
281
+ # @task = @project.comments.build(params[:task])
282
+ # flash[:notice] = 'Task was successfully created.' if @task.save
283
+ # respond_with(@project, @task)
210
284
  # end
211
285
  #
212
- # It also accepts a block to be given. It's used to overwrite a default
213
- # response:
286
+ # This would cause +respond_with+ to redirect to <code>project_task_url</code>
287
+ # instead of <code>task_url</code>. For request formats other than html or
288
+ # javascript, if multiple resources are passed in this way, it is the last
289
+ # one specified that is rendered.
290
+ #
291
+ # === Customizing response behavior
292
+ #
293
+ # Like +respond_to+, +respond_with+ may also be called with a block that
294
+ # can be used to overwrite any of the default responses, e.g. -
214
295
  #
215
296
  # def create
216
297
  # @user = User.new(params[:user])
@@ -221,16 +302,26 @@ module ActionController #:nodoc:
221
302
  # end
222
303
  # end
223
304
  #
224
- # All options given to respond_with are sent to the underlying responder,
225
- # except for the option :responder itself. Since the responder interface
226
- # is quite simple (it just needs to respond to call), you can even give
227
- # a proc to it.
228
- #
229
- # In order to use respond_with, first you need to declare the formats your
230
- # controller responds to in the class level with a call to <tt>respond_to</tt>.
231
- #
305
+ # The argument passed to the block is an ActionController::MimeResponds::Collector
306
+ # object which stores the responses for the formats defined within the
307
+ # block. Note that formats with responses defined explicitly in this way
308
+ # do not have to first be declared using the class method +respond_to+.
309
+ #
310
+ # Also, a hash passed to +respond_with+ immediately after the specified
311
+ # resource(s) is interpreted as a set of options relevant to all
312
+ # formats. Any option accepted by +render+ can be used, e.g.
313
+ # respond_with @people, status: 200
314
+ # However, note that these options are ignored after an unsuccessful attempt
315
+ # to save a resource, e.g. when automatically rendering <tt>:new</tt>
316
+ # after a post request.
317
+ #
318
+ # Two additional options are relevant specifically to +respond_with+ -
319
+ # 1. <tt>:location</tt> - overwrites the default redirect location used after
320
+ # a successful html +post+ request.
321
+ # 2. <tt>:action</tt> - overwrites the default render action used after an
322
+ # unsuccessful html +post+ request.
232
323
  def respond_with(*resources, &block)
233
- raise "In order to use respond_with, first you need to declare the formats your " <<
324
+ raise "In order to use respond_with, first you need to declare the formats your " \
234
325
  "controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
235
326
 
236
327
  if collector = retrieve_collector_from_mimes(&block)
@@ -244,7 +335,6 @@ module ActionController #:nodoc:
244
335
 
245
336
  # Collect mimes declared in the class method respond_to valid for the
246
337
  # current action.
247
- #
248
338
  def collect_mimes_from_class_level #:nodoc:
249
339
  action = action_name.to_s
250
340
 
@@ -252,18 +342,21 @@ module ActionController #:nodoc:
252
342
  config = self.class.mimes_for_respond_to[mime]
253
343
 
254
344
  if config[:except]
255
- !action.in?(config[:except])
345
+ !config[:except].include?(action)
256
346
  elsif config[:only]
257
- action.in?(config[:only])
347
+ config[:only].include?(action)
258
348
  else
259
349
  true
260
350
  end
261
351
  end
262
352
  end
263
353
 
264
- # Collects mimes and return the response for the negotiated format. Returns
265
- # nil if :not_acceptable was sent to the client.
354
+ # Returns a Collector object containing the appropriate mime-type response
355
+ # for the current request, based on the available responses defined by a block.
356
+ # In typical usage this is the block passed to +respond_with+ or +respond_to+.
266
357
  #
358
+ # Sends :not_acceptable to the client and returns nil if no suitable format
359
+ # is available.
267
360
  def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc:
268
361
  mimes ||= collect_mimes_from_class_level
269
362
  collector = Collector.new(mimes)
@@ -276,12 +369,33 @@ module ActionController #:nodoc:
276
369
  lookup_context.rendered_format = lookup_context.formats.first
277
370
  collector
278
371
  else
279
- head :not_acceptable
280
- nil
372
+ raise ActionController::UnknownFormat
281
373
  end
282
374
  end
283
375
 
284
- class Collector #:nodoc:
376
+ # A container for responses available from the current controller for
377
+ # requests for different mime-types sent to a particular action.
378
+ #
379
+ # The public controller methods +respond_with+ and +respond_to+ may be called
380
+ # with a block that is used to define responses to different mime-types, e.g.
381
+ # for +respond_to+ :
382
+ #
383
+ # respond_to do |format|
384
+ # format.html
385
+ # format.xml { render xml: @people }
386
+ # end
387
+ #
388
+ # In this usage, the argument passed to the block (+format+ above) is an
389
+ # instance of the ActionController::MimeResponds::Collector class. This
390
+ # object serves as a container in which available responses can be stored by
391
+ # calling any of the dynamically generated, mime-type-specific methods such
392
+ # as +html+, +xml+ etc on the Collector. Each response is represented by a
393
+ # corresponding block if present.
394
+ #
395
+ # A subsequent call to #negotiate_format(request) will enable the Collector
396
+ # to determine which specific mime-type it should respond with for the current
397
+ # request, with this response then being accessible by calling #response.
398
+ class Collector
285
399
  include AbstractController::Collector
286
400
  attr_accessor :order, :format
287
401
 
@@ -306,7 +420,7 @@ module ActionController #:nodoc:
306
420
  end
307
421
 
308
422
  def response
309
- @responses[format] || @responses[Mime::ALL]
423
+ @responses.fetch(format, @responses[Mime::ALL])
310
424
  end
311
425
 
312
426
  def negotiate_format(request)