actionpack 4.2.11.1 → 6.1.3.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +291 -489
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +81 -51
  7. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +61 -33
  10. data/lib/abstract_controller/collector.rb +9 -13
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +115 -99
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
  15. data/lib/abstract_controller/rendering.rb +48 -47
  16. data/lib/abstract_controller/translation.rb +17 -8
  17. data/lib/abstract_controller/url_for.rb +2 -0
  18. data/lib/abstract_controller.rb +13 -5
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/base.rb +29 -24
  22. data/lib/action_controller/caching.rb +12 -57
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +17 -19
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +134 -46
  27. data/lib/action_controller/metal/content_security_policy.rb +51 -0
  28. data/lib/action_controller/metal/cookies.rb +6 -4
  29. data/lib/action_controller/metal/data_streaming.rb +30 -50
  30. data/lib/action_controller/metal/default_headers.rb +17 -0
  31. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  32. data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
  33. data/lib/action_controller/metal/exceptions.rb +63 -15
  34. data/lib/action_controller/metal/flash.rb +9 -8
  35. data/lib/action_controller/metal/head.rb +26 -21
  36. data/lib/action_controller/metal/helpers.rb +37 -18
  37. data/lib/action_controller/metal/http_authentication.rb +81 -73
  38. data/lib/action_controller/metal/implicit_render.rb +53 -9
  39. data/lib/action_controller/metal/instrumentation.rb +32 -35
  40. data/lib/action_controller/metal/live.rb +102 -120
  41. data/lib/action_controller/metal/logging.rb +20 -0
  42. data/lib/action_controller/metal/mime_responds.rb +49 -47
  43. data/lib/action_controller/metal/parameter_encoding.rb +82 -0
  44. data/lib/action_controller/metal/params_wrapper.rb +83 -66
  45. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  46. data/lib/action_controller/metal/redirecting.rb +53 -32
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +77 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
  50. data/lib/action_controller/metal/rescue.rb +10 -17
  51. data/lib/action_controller/metal/streaming.rb +12 -11
  52. data/lib/action_controller/metal/strong_parameters.rb +714 -186
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/metal.rb +104 -87
  56. data/lib/action_controller/railtie.rb +28 -10
  57. data/lib/action_controller/railties/helpers.rb +3 -1
  58. data/lib/action_controller/renderer.rb +141 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +296 -422
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +107 -56
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +286 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +32 -25
  66. data/lib/action_dispatch/http/filter_redirect.rb +10 -12
  67. data/lib/action_dispatch/http/headers.rb +55 -22
  68. data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
  69. data/lib/action_dispatch/http/mime_type.rb +153 -121
  70. data/lib/action_dispatch/http/mime_types.rb +20 -6
  71. data/lib/action_dispatch/http/parameters.rb +90 -40
  72. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  74. data/lib/action_dispatch/http/request.rb +226 -121
  75. data/lib/action_dispatch/http/response.rb +248 -113
  76. data/lib/action_dispatch/http/upload.rb +21 -7
  77. data/lib/action_dispatch/http/url.rb +182 -100
  78. data/lib/action_dispatch/journey/formatter.rb +90 -43
  79. data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
  80. data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
  81. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
  82. data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
  83. data/lib/action_dispatch/journey/nodes/node.rb +29 -15
  84. data/lib/action_dispatch/journey/parser.rb +17 -16
  85. data/lib/action_dispatch/journey/parser.y +4 -3
  86. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  87. data/lib/action_dispatch/journey/path/pattern.rb +58 -54
  88. data/lib/action_dispatch/journey/route.rb +100 -32
  89. data/lib/action_dispatch/journey/router/utils.rb +29 -18
  90. data/lib/action_dispatch/journey/router.rb +55 -51
  91. data/lib/action_dispatch/journey/routes.rb +17 -17
  92. data/lib/action_dispatch/journey/scanner.rb +26 -17
  93. data/lib/action_dispatch/journey/visitors.rb +98 -54
  94. data/lib/action_dispatch/journey.rb +5 -5
  95. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  96. data/lib/action_dispatch/middleware/callbacks.rb +3 -6
  97. data/lib/action_dispatch/middleware/cookies.rb +347 -217
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  101. data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
  102. data/lib/action_dispatch/middleware/executor.rb +21 -0
  103. data/lib/action_dispatch/middleware/flash.rb +78 -54
  104. data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
  106. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  107. data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
  108. data/lib/action_dispatch/middleware/request_id.rb +17 -10
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
  114. data/lib/action_dispatch/middleware/ssl.rb +118 -35
  115. data/lib/action_dispatch/middleware/stack.rb +82 -41
  116. data/lib/action_dispatch/middleware/static.rb +156 -89
  117. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
  121. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  125. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  135. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  137. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  138. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  139. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  140. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
  141. data/lib/action_dispatch/railtie.rb +27 -13
  142. data/lib/action_dispatch/request/session.rb +109 -61
  143. data/lib/action_dispatch/request/utils.rb +90 -23
  144. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  145. data/lib/action_dispatch/routing/inspector.rb +141 -102
  146. data/lib/action_dispatch/routing/mapper.rb +811 -473
  147. data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
  148. data/lib/action_dispatch/routing/redirection.rb +37 -27
  149. data/lib/action_dispatch/routing/route_set.rb +363 -331
  150. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  151. data/lib/action_dispatch/routing/url_for.rb +66 -26
  152. data/lib/action_dispatch/routing.rb +36 -36
  153. data/lib/action_dispatch/system_test_case.rb +190 -0
  154. data/lib/action_dispatch/system_testing/browser.rb +86 -0
  155. data/lib/action_dispatch/system_testing/driver.rb +67 -0
  156. data/lib/action_dispatch/system_testing/server.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
  158. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
  159. data/lib/action_dispatch/testing/assertion_response.rb +46 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +44 -22
  161. data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
  162. data/lib/action_dispatch/testing/assertions.rb +6 -4
  163. data/lib/action_dispatch/testing/integration.rb +391 -220
  164. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  165. data/lib/action_dispatch/testing/test_process.rb +53 -22
  166. data/lib/action_dispatch/testing/test_request.rb +27 -34
  167. data/lib/action_dispatch/testing/test_response.rb +11 -11
  168. data/lib/action_dispatch.rb +35 -21
  169. data/lib/action_pack/gem_version.rb +6 -4
  170. data/lib/action_pack/version.rb +3 -1
  171. data/lib/action_pack.rb +4 -2
  172. metadata +78 -48
  173. data/lib/action_controller/metal/force_ssl.rb +0 -97
  174. data/lib/action_controller/metal/hide_actions.rb +0 -40
  175. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  176. data/lib/action_controller/middleware.rb +0 -39
  177. data/lib/action_controller/model_naming.rb +0 -12
  178. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  179. data/lib/action_dispatch/journey/backwards.rb +0 -5
  180. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  181. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  182. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  183. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  184. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  185. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  186. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  187. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,9 +1,11 @@
1
- require 'action_dispatch/http/response'
2
- require 'delegate'
3
- require 'active_support/json'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/http/response"
4
+ require "delegate"
5
+ require "active_support/json"
4
6
 
5
7
  module ActionController
6
- # Mix this module in to your controller, and all actions in that controller
8
+ # Mix this module into your controller, and all actions in that controller
7
9
  # will be able to stream data to the client as it's written.
8
10
  #
9
11
  # class MyController < ActionController::Base
@@ -20,7 +22,7 @@ module ActionController
20
22
  # end
21
23
  # end
22
24
  #
23
- # There are a few caveats with this use. You *cannot* write headers after the
25
+ # There are a few caveats with this module. You *cannot* write headers after the
24
26
  # response has been committed (Response#committed? will return truthy).
25
27
  # Calling +write+ or +close+ on the response stream will cause the response
26
28
  # object to be committed. Make sure all headers are set before calling write
@@ -33,6 +35,20 @@ module ActionController
33
35
  # the main thread. Make sure your actions are thread safe, and this shouldn't
34
36
  # be a problem (don't share state across threads, etc).
35
37
  module Live
38
+ extend ActiveSupport::Concern
39
+
40
+ module ClassMethods
41
+ def make_response!(request)
42
+ if request.get_header("HTTP_VERSION") == "HTTP/1.0"
43
+ super
44
+ else
45
+ Live::Response.new.tap do |res|
46
+ res.request = request
47
+ end
48
+ end
49
+ end
50
+ end
51
+
36
52
  # This class provides the ability to write an SSE (Server Sent Event)
37
53
  # to an IO stream. The class is initialized with a stream and can be used
38
54
  # to either write a JSON string or an object which can be converted to JSON.
@@ -70,8 +86,7 @@ module ActionController
70
86
  # Note: SSEs are not currently supported by IE. However, they are supported
71
87
  # by Chrome, Firefox, Opera, and Safari.
72
88
  class SSE
73
-
74
- WHITELISTED_OPTIONS = %w( retry event id )
89
+ PERMITTED_OPTIONS = %w( retry event id )
75
90
 
76
91
  def initialize(stream, options = {})
77
92
  @stream = stream
@@ -92,17 +107,16 @@ module ActionController
92
107
  end
93
108
 
94
109
  private
95
-
96
110
  def perform_write(json, options)
97
111
  current_options = @options.merge(options).stringify_keys
98
112
 
99
- WHITELISTED_OPTIONS.each do |option_name|
113
+ PERMITTED_OPTIONS.each do |option_name|
100
114
  if (option_value = current_options[option_name])
101
115
  @stream.write "#{option_name}: #{option_value}\n"
102
116
  end
103
117
  end
104
118
 
105
- message = json.gsub(/\n/, "\ndata: ")
119
+ message = json.gsub("\n", "\ndata: ")
106
120
  @stream.write "data: #{message}\n\n"
107
121
  end
108
122
  end
@@ -122,17 +136,17 @@ module ActionController
122
136
  attr_accessor :ignore_disconnect
123
137
 
124
138
  def initialize(response)
139
+ super(response, SizedQueue.new(10))
125
140
  @error_callback = lambda { true }
126
141
  @cv = new_cond
127
142
  @aborted = false
128
143
  @ignore_disconnect = false
129
- super(response, SizedQueue.new(10))
130
144
  end
131
145
 
132
146
  def write(string)
133
147
  unless @response.committed?
134
- @response.headers["Cache-Control"] = "no-cache"
135
- @response.headers.delete "Content-Length"
148
+ @response.headers["Cache-Control"] ||= "no-cache"
149
+ @response.delete_header "Content-Length"
136
150
  end
137
151
 
138
152
  super
@@ -149,14 +163,6 @@ module ActionController
149
163
  end
150
164
  end
151
165
 
152
- def each
153
- @response.sending!
154
- while str = @buf.pop
155
- yield str
156
- end
157
- @response.sent!
158
- end
159
-
160
166
  # Write a 'close' event to the buffer; the producer/writing thread
161
167
  # uses this to notify us that it's finished supplying content.
162
168
  #
@@ -189,12 +195,6 @@ module ActionController
189
195
  !@aborted
190
196
  end
191
197
 
192
- def await_close
193
- synchronize do
194
- @cv.wait_until { @closed }
195
- end
196
- end
197
-
198
198
  def on_error(&block)
199
199
  @error_callback = block
200
200
  end
@@ -202,60 +202,34 @@ module ActionController
202
202
  def call_on_error
203
203
  @error_callback.call
204
204
  end
205
- end
206
205
 
207
- class Response < ActionDispatch::Response #:nodoc: all
208
- class Header < DelegateClass(Hash) # :nodoc:
209
- def initialize(response, header)
210
- @response = response
211
- super(header)
212
- end
213
-
214
- def []=(k,v)
215
- if @response.committed?
216
- raise ActionDispatch::IllegalStateError, 'header already sent'
206
+ private
207
+ def each_chunk(&block)
208
+ loop do
209
+ str = nil
210
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
211
+ str = @buf.pop
212
+ end
213
+ break unless str
214
+ yield str
217
215
  end
218
-
219
- super
220
216
  end
217
+ end
221
218
 
222
- def merge(other)
223
- self.class.new @response, __getobj__.merge(other)
219
+ class Response < ActionDispatch::Response #:nodoc: all
220
+ private
221
+ def before_committed
222
+ super
223
+ jar = request.cookie_jar
224
+ # The response can be committed multiple times
225
+ jar.write self unless committed?
224
226
  end
225
227
 
226
- def to_hash
227
- __getobj__.dup
228
+ def build_buffer(response, body)
229
+ buf = Live::Buffer.new response
230
+ body.each { |part| buf.write part }
231
+ buf
228
232
  end
229
- end
230
-
231
- private
232
-
233
- def before_committed
234
- super
235
- jar = request.cookie_jar
236
- # The response can be committed multiple times
237
- jar.write self unless committed?
238
- end
239
-
240
- def before_sending
241
- super
242
- request.cookie_jar.commit!
243
- headers.freeze
244
- end
245
-
246
- def build_buffer(response, body)
247
- buf = Live::Buffer.new response
248
- body.each { |part| buf.write part }
249
- buf
250
- end
251
-
252
- def merge_default_headers(original, default)
253
- Header.new self, super
254
- end
255
-
256
- def handle_conditional_get!
257
- super unless committed?
258
- end
259
233
  end
260
234
 
261
235
  def process(name)
@@ -264,51 +238,43 @@ module ActionController
264
238
 
265
239
  error = nil
266
240
  # This processes the action in a child thread. It lets us return the
267
- # response code and headers back up the rack stack, and still process
268
- # the body in parallel with sending data to the client
269
- Thread.new {
270
- t2 = Thread.current
271
- t2.abort_on_exception = true
272
-
273
- # Since we're processing the view in a different thread, copy the
274
- # thread locals from the main thread to the child thread. :'(
275
- locals.each { |k,v| t2[k] = v }
276
-
277
- begin
278
- super(name)
279
- rescue => e
280
- if @_response.committed?
281
- begin
282
- @_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
283
- @_response.stream.call_on_error
284
- rescue => exception
285
- log_error(exception)
286
- ensure
287
- log_error(e)
288
- @_response.stream.close
241
+ # response code and headers back up the Rack stack, and still process
242
+ # the body in parallel with sending data to the client.
243
+ new_controller_thread {
244
+ ActiveSupport::Dependencies.interlock.running do
245
+ t2 = Thread.current
246
+
247
+ # Since we're processing the view in a different thread, copy the
248
+ # thread locals from the main thread to the child thread. :'(
249
+ locals.each { |k, v| t2[k] = v }
250
+
251
+ begin
252
+ super(name)
253
+ rescue => e
254
+ if @_response.committed?
255
+ begin
256
+ @_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
257
+ @_response.stream.call_on_error
258
+ rescue => exception
259
+ log_error(exception)
260
+ ensure
261
+ log_error(e)
262
+ @_response.stream.close
263
+ end
264
+ else
265
+ error = e
289
266
  end
290
- else
291
- error = e
267
+ ensure
268
+ @_response.commit!
292
269
  end
293
- ensure
294
- @_response.commit!
295
270
  end
296
271
  }
297
272
 
298
- @_response.await_commit
299
- raise error if error
300
- end
301
-
302
- def log_error(exception)
303
- logger = ActionController::Base.logger
304
- return unless logger
305
-
306
- logger.fatal do
307
- message = "\n#{exception.class} (#{exception.message}):\n"
308
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
309
- message << " " << exception.backtrace.join("\n ")
310
- "#{message}\n\n"
273
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
274
+ @_response.await_commit
311
275
  end
276
+
277
+ raise error if error
312
278
  end
313
279
 
314
280
  def response_body=(body)
@@ -316,13 +282,29 @@ module ActionController
316
282
  response.close if response
317
283
  end
318
284
 
319
- def set_response!(request)
320
- if request.env["HTTP_VERSION"] == "HTTP/1.0"
321
- super
322
- else
323
- @_response = Live::Response.new
324
- @_response.request = request
285
+ private
286
+ # Spawn a new thread to serve up the controller in. This is to get
287
+ # around the fact that Rack isn't based around IOs and we need to use
288
+ # a thread to stream data from the response bodies. Nobody should call
289
+ # this method except in Rails internals. Seriously!
290
+ def new_controller_thread # :nodoc:
291
+ Thread.new {
292
+ t2 = Thread.current
293
+ t2.abort_on_exception = true
294
+ yield
295
+ }
296
+ end
297
+
298
+ def log_error(exception)
299
+ logger = ActionController::Base.logger
300
+ return unless logger
301
+
302
+ logger.fatal do
303
+ message = +"\n#{exception.class} (#{exception.message}):\n"
304
+ message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
305
+ message << " " << exception.backtrace.join("\n ")
306
+ "#{message}\n\n"
307
+ end
325
308
  end
326
- end
327
309
  end
328
310
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController
4
+ module Logging
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ # Set a different log level per request.
9
+ #
10
+ # # Use the debug log level if a particular cookie is set.
11
+ # class ApplicationController < ActionController::Base
12
+ # log_at :debug, if: -> { cookies[:debug] }
13
+ # end
14
+ #
15
+ def log_at(level, **options)
16
+ around_action ->(_, action) { logger.log_at(level, &action) }, **options
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,29 +1,9 @@
1
- require 'abstract_controller/collector'
1
+ # frozen_string_literal: true
2
+
3
+ require "abstract_controller/collector"
2
4
 
3
5
  module ActionController #:nodoc:
4
6
  module MimeResponds
5
- extend ActiveSupport::Concern
6
-
7
- # :stopdoc:
8
- module ClassMethods
9
- def respond_to(*)
10
- raise NoMethodError, "The controller-level `respond_to' feature has " \
11
- "been extracted to the `responders` gem. Add it to your Gemfile to " \
12
- "continue using this feature:\n" \
13
- " gem 'responders', '~> 2.0'\n" \
14
- "Consult the Rails upgrade guide for details."
15
- end
16
- end
17
-
18
- def respond_with(*)
19
- raise NoMethodError, "The `respond_with' feature has been extracted " \
20
- "to the `responders` gem. Add it to your Gemfile to continue using " \
21
- "this feature:\n" \
22
- " gem 'responders', '~> 2.0'\n" \
23
- "Consult the Rails upgrade guide for details."
24
- end
25
- # :startdoc:
26
-
27
7
  # Without web-service support, an action which collects the data for displaying a list of people
28
8
  # might look something like this:
29
9
  #
@@ -31,6 +11,13 @@ module ActionController #:nodoc:
31
11
  # @people = Person.all
32
12
  # end
33
13
  #
14
+ # That action implicitly responds to all formats, but formats can also be explicitly enumerated:
15
+ #
16
+ # def index
17
+ # @people = Person.all
18
+ # respond_to :html, :js
19
+ # end
20
+ #
34
21
  # Here's the same action, with web-service support baked in:
35
22
  #
36
23
  # def index
@@ -38,11 +25,12 @@ module ActionController #:nodoc:
38
25
  #
39
26
  # respond_to do |format|
40
27
  # format.html
28
+ # format.js
41
29
  # format.xml { render xml: @people }
42
30
  # end
43
31
  # end
44
32
  #
45
- # What that says is, "if the client wants HTML in response to this action, just respond as we
33
+ # What that says is, "if the client wants HTML or JS in response to this action, just respond as we
46
34
  # would have before, but if the client wants XML, return them the list of people in XML format."
47
35
  # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
48
36
  #
@@ -113,11 +101,11 @@ module ActionController #:nodoc:
113
101
  # and accept Rails' defaults, life will be much easier.
114
102
  #
115
103
  # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
116
- # config/initializers/mime_types.rb as follows.
104
+ # +config/initializers/mime_types.rb+ as follows.
117
105
  #
118
106
  # Mime::Type.register "image/jpg", :jpg
119
107
  #
120
- # Respond to also allows you to specify a common block for different formats by using any:
108
+ # +respond_to+ also allows you to specify a common block for different formats by using +any+:
121
109
  #
122
110
  # def index
123
111
  # @people = Person.all
@@ -136,6 +124,14 @@ module ActionController #:nodoc:
136
124
  #
137
125
  # render json: @people
138
126
  #
127
+ # +any+ can also be used with no arguments, in which case it will be used for any format requested by
128
+ # the user:
129
+ #
130
+ # respond_to do |format|
131
+ # format.html
132
+ # format.any { redirect_to support_path }
133
+ # end
134
+ #
139
135
  # Formats can have different variants.
140
136
  #
141
137
  # The request variant is a specialization of the request format, like <tt>:tablet</tt>,
@@ -146,7 +142,7 @@ module ActionController #:nodoc:
146
142
  #
147
143
  # You can set the variant in a +before_action+:
148
144
  #
149
- # request.variant = :tablet if request.user_agent =~ /iPad/
145
+ # request.variant = :tablet if /iPad/.match?(request.user_agent)
150
146
  #
151
147
  # Respond to variants in the action just like you respond to formats:
152
148
  #
@@ -173,21 +169,21 @@ module ActionController #:nodoc:
173
169
  # format.html.none { render "trash" }
174
170
  # end
175
171
  #
176
- # Variants also support common `any`/`all` block that formats have.
172
+ # Variants also support common +any+/+all+ block that formats have.
177
173
  #
178
174
  # It works for both inline:
179
175
  #
180
176
  # respond_to do |format|
181
- # format.html.any { render text: "any" }
182
- # format.html.phone { render text: "phone" }
177
+ # format.html.any { render html: "any" }
178
+ # format.html.phone { render html: "phone" }
183
179
  # end
184
180
  #
185
181
  # and block syntax:
186
182
  #
187
183
  # respond_to do |format|
188
184
  # format.html do |variant|
189
- # variant.any(:tablet, :phablet){ render text: "any" }
190
- # variant.phone { render text: "phone" }
185
+ # variant.any(:tablet, :phablet){ render html: "any" }
186
+ # variant.phone { render html: "phone" }
191
187
  # end
192
188
  # end
193
189
  #
@@ -195,16 +191,13 @@ module ActionController #:nodoc:
195
191
  #
196
192
  # request.variant = [:tablet, :phone]
197
193
  #
198
- # which will work similarly to formats and MIME types negotiation. If there will be no
199
- # :tablet variant declared, :phone variant will be picked:
194
+ # This will work similarly to formats and MIME types negotiation. If there
195
+ # is no +:tablet+ variant declared, the +:phone+ variant will be used:
200
196
  #
201
197
  # respond_to do |format|
202
198
  # format.html.none
203
199
  # format.html.phone # this gets rendered
204
200
  # end
205
- #
206
- # Be sure to check the documentation of <tt>ActionController::MimeResponds.respond_to</tt>
207
- # for more examples.
208
201
  def respond_to(*mimes)
209
202
  raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
210
203
 
@@ -212,9 +205,13 @@ module ActionController #:nodoc:
212
205
  yield collector if block_given?
213
206
 
214
207
  if format = collector.negotiate_format(request)
208
+ if media_type && media_type != format
209
+ raise ActionController::RespondToMismatchError
210
+ end
215
211
  _process_format(format)
212
+ _set_rendered_content_type(format) unless collector.any_response?
216
213
  response = collector.response
217
- response ? response.call : render({})
214
+ response.call if response
218
215
  else
219
216
  raise ActionController::UnknownFormat
220
217
  end
@@ -250,7 +247,7 @@ module ActionController #:nodoc:
250
247
  @responses = {}
251
248
  @variant = variant
252
249
 
253
- mimes.each { |mime| @responses["Mime::#{mime.upcase}".constantize] = nil }
250
+ mimes.each { |mime| @responses[Mime[mime]] = nil }
254
251
  end
255
252
 
256
253
  def any(*args, &block)
@@ -271,6 +268,10 @@ module ActionController #:nodoc:
271
268
  end
272
269
  end
273
270
 
271
+ def any_response?
272
+ !@responses.fetch(format, false) && @responses[Mime::ALL]
273
+ end
274
+
274
275
  def response
275
276
  response = @responses.fetch(format, @responses[Mime::ALL])
276
277
  if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
@@ -296,8 +297,8 @@ module ActionController #:nodoc:
296
297
 
297
298
  def any(*args, &block)
298
299
  if block_given?
299
- if args.any? && args.none?{ |a| a == @variant }
300
- args.each{ |v| @variants[v] = block }
300
+ if args.any? && args.none? { |a| a == @variant }
301
+ args.each { |v| @variants[v] = block }
301
302
  else
302
303
  @variants[:any] = block
303
304
  end
@@ -310,16 +311,17 @@ module ActionController #:nodoc:
310
311
  end
311
312
 
312
313
  def variant
313
- if @variant.nil?
314
+ if @variant.empty?
314
315
  @variants[:none] || @variants[:any]
315
- elsif (@variants.keys & @variant).any?
316
- @variant.each do |v|
317
- return @variants[v] if @variants.key?(v)
318
- end
319
316
  else
320
- @variants[:any]
317
+ @variants[variant_key]
321
318
  end
322
319
  end
320
+
321
+ private
322
+ def variant_key
323
+ @variant.find { |variant| @variants.key?(variant) } || :any
324
+ end
323
325
  end
324
326
  end
325
327
  end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController
4
+ # Specify binary encoding for parameters for a given action.
5
+ module ParameterEncoding
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ def inherited(klass) # :nodoc:
10
+ super
11
+ klass.setup_param_encode
12
+ end
13
+
14
+ def setup_param_encode # :nodoc:
15
+ @_parameter_encodings = Hash.new { |h, k| h[k] = {} }
16
+ end
17
+
18
+ def action_encoding_template(action) # :nodoc:
19
+ if @_parameter_encodings.has_key?(action.to_s)
20
+ @_parameter_encodings[action.to_s]
21
+ end
22
+ end
23
+
24
+ # Specify that a given action's parameters should all be encoded as
25
+ # ASCII-8BIT (it "skips" the encoding default of UTF-8).
26
+ #
27
+ # For example, a controller would use it like this:
28
+ #
29
+ # class RepositoryController < ActionController::Base
30
+ # skip_parameter_encoding :show
31
+ #
32
+ # def show
33
+ # @repo = Repository.find_by_filesystem_path params[:file_path]
34
+ #
35
+ # # `repo_name` is guaranteed to be UTF-8, but was ASCII-8BIT, so
36
+ # # tag it as such
37
+ # @repo_name = params[:repo_name].force_encoding 'UTF-8'
38
+ # end
39
+ #
40
+ # def index
41
+ # @repositories = Repository.all
42
+ # end
43
+ # end
44
+ #
45
+ # The show action in the above controller would have all parameter values
46
+ # encoded as ASCII-8BIT. This is useful in the case where an application
47
+ # must handle data but encoding of the data is unknown, like file system data.
48
+ def skip_parameter_encoding(action)
49
+ @_parameter_encodings[action.to_s] = Hash.new { Encoding::ASCII_8BIT }
50
+ end
51
+
52
+ # Specify the encoding for a parameter on an action.
53
+ # If not specified the default is UTF-8.
54
+ #
55
+ # You can specify a binary (ASCII_8BIT) parameter with:
56
+ #
57
+ # class RepositoryController < ActionController::Base
58
+ # # This specifies that file_path is not UTF-8 and is instead ASCII_8BIT
59
+ # param_encoding :show, :file_path, Encoding::ASCII_8BIT
60
+ #
61
+ # def show
62
+ # @repo = Repository.find_by_filesystem_path params[:file_path]
63
+ #
64
+ # # params[:repo_name] remains UTF-8 encoded
65
+ # @repo_name = params[:repo_name]
66
+ # end
67
+ #
68
+ # def index
69
+ # @repositories = Repository.all
70
+ # end
71
+ # end
72
+ #
73
+ # The file_path parameter on the show action would be encoded as ASCII-8BIT,
74
+ # but all other arguments will remain UTF-8 encoded.
75
+ # This is useful in the case where an application must handle data
76
+ # but encoding of the data is unknown, like file system data.
77
+ def param_encoding(action, param, encoding)
78
+ @_parameter_encodings[action.to_s][param.to_s] = encoding
79
+ end
80
+ end
81
+ end
82
+ end