actionpack 4.2.10 → 6.1.4.1

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 +5 -5
  2. data/CHANGELOG.md +318 -460
  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 +110 -119
  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 +84 -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 +298 -421
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +108 -58
  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 +82 -50
  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 +91 -44
  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 +124 -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 +16 -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 -49
  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
@@ -113,6 +127,11 @@ module ActionController
113
127
  class Buffer < ActionDispatch::Response::Buffer #:nodoc:
114
128
  include MonitorMixin
115
129
 
130
+ class << self
131
+ attr_accessor :queue_size
132
+ end
133
+ @queue_size = 10
134
+
116
135
  # Ignore that the client has disconnected.
117
136
  #
118
137
  # If this value is `true`, calling `write` after the client
@@ -122,17 +141,17 @@ module ActionController
122
141
  attr_accessor :ignore_disconnect
123
142
 
124
143
  def initialize(response)
144
+ super(response, build_queue(self.class.queue_size))
125
145
  @error_callback = lambda { true }
126
146
  @cv = new_cond
127
147
  @aborted = false
128
148
  @ignore_disconnect = false
129
- super(response, SizedQueue.new(10))
130
149
  end
131
150
 
132
151
  def write(string)
133
152
  unless @response.committed?
134
- @response.headers["Cache-Control"] = "no-cache"
135
- @response.headers.delete "Content-Length"
153
+ @response.headers["Cache-Control"] ||= "no-cache"
154
+ @response.delete_header "Content-Length"
136
155
  end
137
156
 
138
157
  super
@@ -149,14 +168,6 @@ module ActionController
149
168
  end
150
169
  end
151
170
 
152
- def each
153
- @response.sending!
154
- while str = @buf.pop
155
- yield str
156
- end
157
- @response.sent!
158
- end
159
-
160
171
  # Write a 'close' event to the buffer; the producer/writing thread
161
172
  # uses this to notify us that it's finished supplying content.
162
173
  #
@@ -189,12 +200,6 @@ module ActionController
189
200
  !@aborted
190
201
  end
191
202
 
192
- def await_close
193
- synchronize do
194
- @cv.wait_until { @closed }
195
- end
196
- end
197
-
198
203
  def on_error(&block)
199
204
  @error_callback = block
200
205
  end
@@ -202,60 +207,38 @@ module ActionController
202
207
  def call_on_error
203
208
  @error_callback.call
204
209
  end
205
- end
206
210
 
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'
211
+ private
212
+ def each_chunk(&block)
213
+ loop do
214
+ str = nil
215
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
216
+ str = @buf.pop
217
+ end
218
+ break unless str
219
+ yield str
217
220
  end
218
-
219
- super
220
- end
221
-
222
- def merge(other)
223
- self.class.new @response, __getobj__.merge(other)
224
221
  end
225
222
 
226
- def to_hash
227
- __getobj__.dup
223
+ def build_queue(queue_size)
224
+ queue_size ? SizedQueue.new(queue_size) : Queue.new
228
225
  end
229
- end
226
+ end
230
227
 
228
+ class Response < ActionDispatch::Response #:nodoc: all
231
229
  private
230
+ def before_committed
231
+ super
232
+ jar = request.cookie_jar
233
+ # The response can be committed multiple times
234
+ jar.write self unless committed?
235
+ end
232
236
 
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
237
+ def build_buffer(response, body)
238
+ buf = Live::Buffer.new response
239
+ body.each { |part| buf.write part }
240
+ buf
241
+ end
259
242
  end
260
243
 
261
244
  def process(name)
@@ -264,51 +247,43 @@ module ActionController
264
247
 
265
248
  error = nil
266
249
  # 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
250
+ # response code and headers back up the Rack stack, and still process
251
+ # the body in parallel with sending data to the client.
252
+ new_controller_thread {
253
+ ActiveSupport::Dependencies.interlock.running do
254
+ t2 = Thread.current
255
+
256
+ # Since we're processing the view in a different thread, copy the
257
+ # thread locals from the main thread to the child thread. :'(
258
+ locals.each { |k, v| t2[k] = v }
259
+
260
+ begin
261
+ super(name)
262
+ rescue => e
263
+ if @_response.committed?
264
+ begin
265
+ @_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
266
+ @_response.stream.call_on_error
267
+ rescue => exception
268
+ log_error(exception)
269
+ ensure
270
+ log_error(e)
271
+ @_response.stream.close
272
+ end
273
+ else
274
+ error = e
289
275
  end
290
- else
291
- error = e
276
+ ensure
277
+ @_response.commit!
292
278
  end
293
- ensure
294
- @_response.commit!
295
279
  end
296
280
  }
297
281
 
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"
282
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
283
+ @_response.await_commit
311
284
  end
285
+
286
+ raise error if error
312
287
  end
313
288
 
314
289
  def response_body=(body)
@@ -316,13 +291,29 @@ module ActionController
316
291
  response.close if response
317
292
  end
318
293
 
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
294
+ private
295
+ # Spawn a new thread to serve up the controller in. This is to get
296
+ # around the fact that Rack isn't based around IOs and we need to use
297
+ # a thread to stream data from the response bodies. Nobody should call
298
+ # this method except in Rails internals. Seriously!
299
+ def new_controller_thread # :nodoc:
300
+ Thread.new {
301
+ t2 = Thread.current
302
+ t2.abort_on_exception = true
303
+ yield
304
+ }
305
+ end
306
+
307
+ def log_error(exception)
308
+ logger = ActionController::Base.logger
309
+ return unless logger
310
+
311
+ logger.fatal do
312
+ message = +"\n#{exception.class} (#{exception.message}):\n"
313
+ message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
314
+ message << " " << exception.backtrace.join("\n ")
315
+ "#{message}\n\n"
316
+ end
325
317
  end
326
- end
327
318
  end
328
319
  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