actionpack 7.1.3.4 → 7.2.0.beta1

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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -541
  3. data/lib/abstract_controller/asset_paths.rb +2 -0
  4. data/lib/abstract_controller/base.rb +102 -98
  5. data/lib/abstract_controller/caching/fragments.rb +50 -53
  6. data/lib/abstract_controller/caching.rb +2 -0
  7. data/lib/abstract_controller/callbacks.rb +66 -64
  8. data/lib/abstract_controller/collector.rb +6 -6
  9. data/lib/abstract_controller/deprecator.rb +2 -0
  10. data/lib/abstract_controller/error.rb +2 -0
  11. data/lib/abstract_controller/helpers.rb +70 -85
  12. data/lib/abstract_controller/logger.rb +2 -0
  13. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  14. data/lib/abstract_controller/rendering.rb +13 -12
  15. data/lib/abstract_controller/translation.rb +11 -10
  16. data/lib/abstract_controller/url_for.rb +8 -6
  17. data/lib/abstract_controller.rb +2 -0
  18. data/lib/action_controller/api/api_rendering.rb +2 -0
  19. data/lib/action_controller/api.rb +74 -72
  20. data/lib/action_controller/base.rb +155 -117
  21. data/lib/action_controller/caching.rb +15 -12
  22. data/lib/action_controller/deprecator.rb +2 -0
  23. data/lib/action_controller/form_builder.rb +20 -17
  24. data/lib/action_controller/log_subscriber.rb +3 -1
  25. data/lib/action_controller/metal/allow_browser.rb +119 -0
  26. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  27. data/lib/action_controller/metal/conditional_get.rb +188 -174
  28. data/lib/action_controller/metal/content_security_policy.rb +25 -24
  29. data/lib/action_controller/metal/cookies.rb +4 -2
  30. data/lib/action_controller/metal/data_streaming.rb +64 -55
  31. data/lib/action_controller/metal/default_headers.rb +5 -3
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
  34. data/lib/action_controller/metal/exceptions.rb +11 -9
  35. data/lib/action_controller/metal/flash.rb +12 -10
  36. data/lib/action_controller/metal/head.rb +12 -10
  37. data/lib/action_controller/metal/helpers.rb +63 -55
  38. data/lib/action_controller/metal/http_authentication.rb +209 -201
  39. data/lib/action_controller/metal/implicit_render.rb +17 -15
  40. data/lib/action_controller/metal/instrumentation.rb +15 -12
  41. data/lib/action_controller/metal/live.rb +113 -107
  42. data/lib/action_controller/metal/logging.rb +6 -4
  43. data/lib/action_controller/metal/mime_responds.rb +151 -142
  44. data/lib/action_controller/metal/parameter_encoding.rb +34 -32
  45. data/lib/action_controller/metal/params_wrapper.rb +57 -59
  46. data/lib/action_controller/metal/permissions_policy.rb +13 -12
  47. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  48. data/lib/action_controller/metal/redirecting.rb +108 -82
  49. data/lib/action_controller/metal/renderers.rb +50 -49
  50. data/lib/action_controller/metal/rendering.rb +103 -75
  51. data/lib/action_controller/metal/request_forgery_protection.rb +162 -133
  52. data/lib/action_controller/metal/rescue.rb +11 -9
  53. data/lib/action_controller/metal/streaming.rb +138 -136
  54. data/lib/action_controller/metal/strong_parameters.rb +525 -480
  55. data/lib/action_controller/metal/testing.rb +2 -0
  56. data/lib/action_controller/metal/url_for.rb +17 -15
  57. data/lib/action_controller/metal.rb +58 -57
  58. data/lib/action_controller/railtie.rb +3 -0
  59. data/lib/action_controller/railties/helpers.rb +2 -0
  60. data/lib/action_controller/renderer.rb +42 -36
  61. data/lib/action_controller/template_assertions.rb +4 -2
  62. data/lib/action_controller/test_case.rb +146 -126
  63. data/lib/action_controller.rb +10 -3
  64. data/lib/action_dispatch/constants.rb +2 -0
  65. data/lib/action_dispatch/deprecator.rb +2 -0
  66. data/lib/action_dispatch/http/cache.rb +27 -26
  67. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  68. data/lib/action_dispatch/http/content_security_policy.rb +44 -38
  69. data/lib/action_dispatch/http/filter_parameters.rb +9 -5
  70. data/lib/action_dispatch/http/filter_redirect.rb +15 -1
  71. data/lib/action_dispatch/http/headers.rb +22 -22
  72. data/lib/action_dispatch/http/mime_negotiation.rb +30 -41
  73. data/lib/action_dispatch/http/mime_type.rb +29 -22
  74. data/lib/action_dispatch/http/mime_types.rb +2 -0
  75. data/lib/action_dispatch/http/parameters.rb +11 -9
  76. data/lib/action_dispatch/http/permissions_policy.rb +27 -37
  77. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  78. data/lib/action_dispatch/http/request.rb +71 -71
  79. data/lib/action_dispatch/http/response.rb +61 -61
  80. data/lib/action_dispatch/http/upload.rb +18 -16
  81. data/lib/action_dispatch/http/url.rb +75 -73
  82. data/lib/action_dispatch/journey/formatter.rb +13 -6
  83. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  84. data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
  85. data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
  86. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  87. data/lib/action_dispatch/journey/nodes/node.rb +6 -5
  88. data/lib/action_dispatch/journey/parser.rb +4 -3
  89. data/lib/action_dispatch/journey/parser_extras.rb +2 -0
  90. data/lib/action_dispatch/journey/path/pattern.rb +4 -1
  91. data/lib/action_dispatch/journey/route.rb +9 -7
  92. data/lib/action_dispatch/journey/router/utils.rb +16 -15
  93. data/lib/action_dispatch/journey/router.rb +4 -2
  94. data/lib/action_dispatch/journey/routes.rb +4 -2
  95. data/lib/action_dispatch/journey/scanner.rb +4 -2
  96. data/lib/action_dispatch/journey/visitors.rb +2 -0
  97. data/lib/action_dispatch/journey.rb +2 -0
  98. data/lib/action_dispatch/log_subscriber.rb +2 -0
  99. data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
  100. data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
  101. data/lib/action_dispatch/middleware/callbacks.rb +3 -1
  102. data/lib/action_dispatch/middleware/cookies.rb +119 -104
  103. data/lib/action_dispatch/middleware/debug_exceptions.rb +13 -5
  104. data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
  105. data/lib/action_dispatch/middleware/debug_view.rb +2 -0
  106. data/lib/action_dispatch/middleware/exception_wrapper.rb +6 -11
  107. data/lib/action_dispatch/middleware/executor.rb +8 -0
  108. data/lib/action_dispatch/middleware/flash.rb +63 -51
  109. data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
  110. data/lib/action_dispatch/middleware/public_exceptions.rb +8 -6
  111. data/lib/action_dispatch/middleware/reloader.rb +5 -3
  112. data/lib/action_dispatch/middleware/remote_ip.rb +77 -72
  113. data/lib/action_dispatch/middleware/request_id.rb +14 -9
  114. data/lib/action_dispatch/middleware/server_timing.rb +4 -2
  115. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
  116. data/lib/action_dispatch/middleware/session/cache_store.rb +13 -8
  117. data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
  118. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
  119. data/lib/action_dispatch/middleware/show_exceptions.rb +31 -21
  120. data/lib/action_dispatch/middleware/ssl.rb +43 -40
  121. data/lib/action_dispatch/middleware/stack.rb +11 -10
  122. data/lib/action_dispatch/middleware/static.rb +33 -31
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +1 -1
  124. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -1
  125. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
  126. data/lib/action_dispatch/railtie.rb +2 -4
  127. data/lib/action_dispatch/request/session.rb +23 -21
  128. data/lib/action_dispatch/request/utils.rb +2 -0
  129. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  130. data/lib/action_dispatch/routing/inspector.rb +5 -3
  131. data/lib/action_dispatch/routing/mapper.rb +670 -635
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
  133. data/lib/action_dispatch/routing/redirection.rb +37 -32
  134. data/lib/action_dispatch/routing/route_set.rb +59 -45
  135. data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
  136. data/lib/action_dispatch/routing/url_for.rb +130 -125
  137. data/lib/action_dispatch/routing.rb +150 -148
  138. data/lib/action_dispatch/system_test_case.rb +91 -81
  139. data/lib/action_dispatch/system_testing/browser.rb +10 -3
  140. data/lib/action_dispatch/system_testing/driver.rb +3 -1
  141. data/lib/action_dispatch/system_testing/server.rb +2 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
  143. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +8 -6
  145. data/lib/action_dispatch/testing/assertions/response.rb +26 -23
  146. data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
  147. data/lib/action_dispatch/testing/assertions.rb +2 -0
  148. data/lib/action_dispatch/testing/integration.rb +223 -222
  149. data/lib/action_dispatch/testing/request_encoder.rb +2 -0
  150. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  151. data/lib/action_dispatch/testing/test_process.rb +12 -8
  152. data/lib/action_dispatch/testing/test_request.rb +3 -1
  153. data/lib/action_dispatch/testing/test_response.rb +27 -26
  154. data/lib/action_dispatch.rb +22 -28
  155. data/lib/action_pack/gem_version.rb +6 -4
  156. data/lib/action_pack/version.rb +3 -1
  157. data/lib/action_pack.rb +17 -16
  158. metadata +30 -13
@@ -1,33 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionController
4
- # = Action Controller Implicit Render
6
+ # # Action Controller Implicit Render
5
7
  #
6
- # Handles implicit rendering for a controller action that does not
7
- # explicitly respond with +render+, +respond_to+, +redirect+, or +head+.
8
+ # Handles implicit rendering for a controller action that does not explicitly
9
+ # respond with `render`, `respond_to`, `redirect`, or `head`.
8
10
  #
9
- # For API controllers, the implicit response is always <tt>204 No Content</tt>.
11
+ # For API controllers, the implicit response is always `204 No Content`.
10
12
  #
11
- # For all other controllers, we use these heuristics to decide whether to
12
- # render a template, raise an error for a missing template, or respond with
13
- # <tt>204 No Content</tt>:
13
+ # For all other controllers, we use these heuristics to decide whether to render
14
+ # a template, raise an error for a missing template, or respond with `204 No
15
+ # Content`:
14
16
  #
15
- # First, if we DO find a template, it's rendered. Template lookup accounts
16
- # for the action name, locales, format, variant, template handlers, and more
17
- # (see +render+ for details).
17
+ # First, if we DO find a template, it's rendered. Template lookup accounts for
18
+ # the action name, locales, format, variant, template handlers, and more (see
19
+ # `render` for details).
18
20
  #
19
21
  # Second, if we DON'T find a template but the controller action does have
20
- # templates for other formats, variants, etc., then we trust that you meant
21
- # to provide a template for this response, too, and we raise
22
+ # templates for other formats, variants, etc., then we trust that you meant to
23
+ # provide a template for this response, too, and we raise
22
24
  # ActionController::UnknownFormat with an explanation.
23
25
  #
24
26
  # Third, if we DON'T find a template AND the request is a page load in a web
25
- # browser (technically, a non-XHR GET request for an HTML response) where
26
- # you reasonably expect to have rendered a template, then we raise
27
+ # browser (technically, a non-XHR GET request for an HTML response) where you
28
+ # reasonably expect to have rendered a template, then we raise
27
29
  # ActionController::MissingExactTemplate with an explanation.
28
30
  #
29
31
  # Finally, if we DON'T find a template AND the request isn't a browser page
30
- # load, then we implicitly respond with <tt>204 No Content</tt>.
32
+ # load, then we implicitly respond with `204 No Content`.
31
33
  module ImplicitRender
32
34
  # :stopdoc:
33
35
  include BasicImplicitRender
@@ -1,14 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "benchmark"
4
6
  require "abstract_controller/logger"
5
7
 
6
8
  module ActionController
7
- # = Action Controller \Instrumentation
9
+ # # Action Controller Instrumentation
8
10
  #
9
- # Adds instrumentation to several ends in ActionController::Base. It also provides
10
- # some hooks related with process_action. This allows an ORM like Active Record
11
- # and/or DataMapper to plug in ActionController and show related information.
11
+ # Adds instrumentation to several ends in ActionController::Base. It also
12
+ # provides some hooks related with process_action. This allows an ORM like
13
+ # Active Record and/or DataMapper to plug in ActionController and show related
14
+ # information.
12
15
  #
13
16
  # Check ActiveRecord::Railties::ControllerRuntime for an example.
14
17
  module Instrumentation
@@ -91,23 +94,23 @@ module ActionController
91
94
  # A hook which allows you to clean up any time, wrongly taken into account in
92
95
  # views, like database querying time.
93
96
  #
94
- # def cleanup_view_runtime
95
- # super - time_taken_in_something_expensive
96
- # end
97
+ # def cleanup_view_runtime
98
+ # super - time_taken_in_something_expensive
99
+ # end
97
100
  def cleanup_view_runtime # :doc:
98
101
  yield
99
102
  end
100
103
 
101
- # Every time after an action is processed, this method is invoked
102
- # with the payload, so you can add more information.
104
+ # Every time after an action is processed, this method is invoked with the
105
+ # payload, so you can add more information.
103
106
  def append_info_to_payload(payload) # :doc:
104
107
  payload[:view_runtime] = view_runtime
105
108
  end
106
109
 
107
110
  module ClassMethods
108
- # A hook which allows other frameworks to log what happened during
109
- # controller process action. This method should return an array
110
- # with the messages to be added.
111
+ # A hook which allows other frameworks to log what happened during controller
112
+ # process action. This method should return an array with the messages to be
113
+ # added.
111
114
  def log_process_action(payload) # :nodoc:
112
115
  messages, view_runtime = [], payload[:view_runtime]
113
116
  messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
@@ -1,56 +1,58 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "action_dispatch/http/response"
4
6
  require "delegate"
5
7
  require "active_support/json"
6
8
 
7
9
  module ActionController
8
- # = Action Controller \Live
10
+ # # Action Controller Live
9
11
  #
10
- # Mix this module into your controller, and all actions in that controller
11
- # will be able to stream data to the client as it's written.
12
+ # Mix this module into your controller, and all actions in that controller will
13
+ # be able to stream data to the client as it's written.
12
14
  #
13
- # class MyController < ActionController::Base
14
- # include ActionController::Live
15
+ # class MyController < ActionController::Base
16
+ # include ActionController::Live
15
17
  #
16
- # def stream
17
- # response.headers['Content-Type'] = 'text/event-stream'
18
- # 100.times {
19
- # response.stream.write "hello world\n"
20
- # sleep 1
21
- # }
22
- # ensure
23
- # response.stream.close
18
+ # def stream
19
+ # response.headers['Content-Type'] = 'text/event-stream'
20
+ # 100.times {
21
+ # response.stream.write "hello world\n"
22
+ # sleep 1
23
+ # }
24
+ # ensure
25
+ # response.stream.close
26
+ # end
24
27
  # end
25
- # end
26
28
  #
27
- # There are a few caveats with this module. You *cannot* write headers after the
28
- # response has been committed (Response#committed? will return truthy).
29
- # Calling +write+ or +close+ on the response stream will cause the response
30
- # object to be committed. Make sure all headers are set before calling write
31
- # or close on your stream.
29
+ # There are a few caveats with this module. You **cannot** write headers after
30
+ # the response has been committed (Response#committed? will return truthy).
31
+ # Calling `write` or `close` on the response stream will cause the response
32
+ # object to be committed. Make sure all headers are set before calling write or
33
+ # close on your stream.
32
34
  #
33
- # You *must* call close on your stream when you're finished, otherwise the
35
+ # You **must** call close on your stream when you're finished, otherwise the
34
36
  # socket may be left open forever.
35
37
  #
36
38
  # The final caveat is that your actions are executed in a separate thread than
37
- # the main thread. Make sure your actions are thread safe, and this shouldn't
38
- # be a problem (don't share state across threads, etc).
39
+ # the main thread. Make sure your actions are thread safe, and this shouldn't be
40
+ # a problem (don't share state across threads, etc).
39
41
  #
40
- # Note that \Rails includes +Rack::ETag+ by default, which will buffer your
42
+ # Note that Rails includes `Rack::ETag` by default, which will buffer your
41
43
  # response. As a result, streaming responses may not work properly with Rack
42
- # 2.2.x, and you may need to implement workarounds in your application.
43
- # You can either set the +ETag+ or +Last-Modified+ response headers or remove
44
- # +Rack::ETag+ from the middleware stack to address this issue.
44
+ # 2.2.x, and you may need to implement workarounds in your application. You can
45
+ # either set the `ETag` or `Last-Modified` response headers or remove
46
+ # `Rack::ETag` from the middleware stack to address this issue.
45
47
  #
46
- # Here's an example of how you can set the +Last-Modified+ header if your Rack
48
+ # Here's an example of how you can set the `Last-Modified` header if your Rack
47
49
  # version is 2.2.x:
48
50
  #
49
- # def stream
50
- # response.headers["Content-Type"] = "text/event-stream"
51
- # response.headers["Last-Modified"] = Time.now.httpdate # Add this line if your Rack version is 2.2.x
52
- # ...
53
- # end
51
+ # def stream
52
+ # response.headers["Content-Type"] = "text/event-stream"
53
+ # response.headers["Last-Modified"] = Time.now.httpdate # Add this line if your Rack version is 2.2.x
54
+ # ...
55
+ # end
54
56
  module Live
55
57
  extend ActiveSupport::Concern
56
58
 
@@ -66,44 +68,44 @@ module ActionController
66
68
  end
67
69
  end
68
70
 
69
- # = Action Controller \Live Server Sent Events
71
+ # # Action Controller Live Server Sent Events
70
72
  #
71
- # This class provides the ability to write an SSE (Server Sent Event)
72
- # to an IO stream. The class is initialized with a stream and can be used
73
- # to either write a JSON string or an object which can be converted to JSON.
73
+ # This class provides the ability to write an SSE (Server Sent Event) to an IO
74
+ # stream. The class is initialized with a stream and can be used to either write
75
+ # a JSON string or an object which can be converted to JSON.
74
76
  #
75
77
  # Writing an object will convert it into standard SSE format with whatever
76
78
  # options you have configured. You may choose to set the following options:
77
79
  #
78
- # 1) Event. If specified, an event with this name will be dispatched on
79
- # the browser.
80
- # 2) Retry. The reconnection time in milliseconds used when attempting
81
- # to send the event.
82
- # 3) Id. If the connection dies while sending an SSE to the browser, then
83
- # the server will receive a +Last-Event-ID+ header with value equal to +id+.
80
+ # 1) Event. If specified, an event with this name will be dispatched on
81
+ # the browser.
82
+ # 2) Retry. The reconnection time in milliseconds used when attempting
83
+ # to send the event.
84
+ # 3) Id. If the connection dies while sending an SSE to the browser, then
85
+ # the server will receive a +Last-Event-ID+ header with value equal to +id+.
84
86
  #
85
- # After setting an option in the constructor of the SSE object, all future
86
- # SSEs sent across the stream will use those options unless overridden.
87
+ # After setting an option in the constructor of the SSE object, all future SSEs
88
+ # sent across the stream will use those options unless overridden.
87
89
  #
88
90
  # Example Usage:
89
91
  #
90
- # class MyController < ActionController::Base
91
- # include ActionController::Live
92
+ # class MyController < ActionController::Base
93
+ # include ActionController::Live
92
94
  #
93
- # def index
94
- # response.headers['Content-Type'] = 'text/event-stream'
95
- # sse = SSE.new(response.stream, retry: 300, event: "event-name")
96
- # sse.write({ name: 'John'})
97
- # sse.write({ name: 'John'}, id: 10)
98
- # sse.write({ name: 'John'}, id: 10, event: "other-event")
99
- # sse.write({ name: 'John'}, id: 10, event: "other-event", retry: 500)
100
- # ensure
101
- # sse.close
95
+ # def index
96
+ # response.headers['Content-Type'] = 'text/event-stream'
97
+ # sse = SSE.new(response.stream, retry: 300, event: "event-name")
98
+ # sse.write({ name: 'John'})
99
+ # sse.write({ name: 'John'}, id: 10)
100
+ # sse.write({ name: 'John'}, id: 10, event: "other-event")
101
+ # sse.write({ name: 'John'}, id: 10, event: "other-event", retry: 500)
102
+ # ensure
103
+ # sse.close
104
+ # end
102
105
  # end
103
- # end
104
106
  #
105
- # Note: SSEs are not currently supported by IE. However, they are supported
106
- # by Chrome, Firefox, Opera, and Safari.
107
+ # Note: SSEs are not currently supported by IE. However, they are supported by
108
+ # Chrome, Firefox, Opera, and Safari.
107
109
  class SSE
108
110
  PERMITTED_OPTIONS = %w( retry event id )
109
111
 
@@ -153,10 +155,9 @@ module ActionController
153
155
 
154
156
  # Ignore that the client has disconnected.
155
157
  #
156
- # If this value is `true`, calling `write` after the client
157
- # disconnects will result in the written content being silently
158
- # discarded. If this value is `false` (the default), a
159
- # ClientDisconnected exception will be raised.
158
+ # If this value is `true`, calling `write` after the client disconnects will
159
+ # result in the written content being silently discarded. If this value is
160
+ # `false` (the default), a ClientDisconnected exception will be raised.
160
161
  attr_accessor :ignore_disconnect
161
162
 
162
163
  def initialize(response)
@@ -167,9 +168,10 @@ module ActionController
167
168
  @ignore_disconnect = false
168
169
  end
169
170
 
170
- # ActionDispatch::Response delegates #to_ary to the internal ActionDispatch::Response::Buffer,
171
- # defining #to_ary is an indicator that the response body can be buffered and/or cached by
172
- # Rack middlewares, this is not the case for Live responses so we undefine it for this Buffer subclass.
171
+ # ActionDispatch::Response delegates #to_ary to the internal
172
+ # ActionDispatch::Response::Buffer, defining #to_ary is an indicator that the
173
+ # response body can be buffered and/or cached by Rack middlewares, this is not
174
+ # the case for Live responses so we undefine it for this Buffer subclass.
173
175
  undef_method :to_ary
174
176
 
175
177
  def write(string)
@@ -184,21 +186,20 @@ module ActionController
184
186
  @buf.clear
185
187
 
186
188
  unless @ignore_disconnect
187
- # Raise ClientDisconnected, which is a RuntimeError (not an
188
- # IOError), because that's more appropriate for something beyond
189
- # the developer's control.
189
+ # Raise ClientDisconnected, which is a RuntimeError (not an IOError), because
190
+ # that's more appropriate for something beyond the developer's control.
190
191
  raise ClientDisconnected, "client disconnected"
191
192
  end
192
193
  end
193
194
  end
194
195
 
195
- # Same as +write+ but automatically include a newline at the end of the string.
196
+ # Same as `write` but automatically include a newline at the end of the string.
196
197
  def writeln(string)
197
198
  write string.end_with?("\n") ? string : "#{string}\n"
198
199
  end
199
200
 
200
- # Write a 'close' event to the buffer; the producer/writing thread
201
- # uses this to notify us that it's finished supplying content.
201
+ # Write a 'close' event to the buffer; the producer/writing thread uses this to
202
+ # notify us that it's finished supplying content.
202
203
  #
203
204
  # See also #abort.
204
205
  def close
@@ -209,9 +210,8 @@ module ActionController
209
210
  end
210
211
  end
211
212
 
212
- # Inform the producer/writing thread that the client has
213
- # disconnected; the reading thread is no longer interested in
214
- # anything that's being written.
213
+ # Inform the producer/writing thread that the client has disconnected; the
214
+ # reading thread is no longer interested in anything that's being written.
215
215
  #
216
216
  # See also #close.
217
217
  def abort
@@ -223,8 +223,8 @@ module ActionController
223
223
 
224
224
  # Is the client still connected and waiting for content?
225
225
  #
226
- # The result of calling `write` when this is `false` is determined
227
- # by `ignore_disconnect`.
226
+ # The result of calling `write` when this is `false` is determined by
227
+ # `ignore_disconnect`.
228
228
  def connected?
229
229
  !@aborted
230
230
  end
@@ -275,15 +275,15 @@ module ActionController
275
275
  locals = t1.keys.map { |key| [key, t1[key]] }
276
276
 
277
277
  error = nil
278
- # This processes the action in a child thread. It lets us return the
279
- # response code and headers back up the Rack stack, and still process
280
- # the body in parallel with sending data to the client.
278
+ # This processes the action in a child thread. It lets us return the response
279
+ # code and headers back up the Rack stack, and still process the body in
280
+ # parallel with sending data to the client.
281
281
  new_controller_thread {
282
282
  ActiveSupport::Dependencies.interlock.running do
283
283
  t2 = Thread.current
284
284
 
285
- # Since we're processing the view in a different thread, copy the
286
- # thread locals from the main thread to the child thread. :'(
285
+ # Since we're processing the view in a different thread, copy the thread locals
286
+ # from the main thread to the child thread. :'(
287
287
  locals.each { |k, v| t2[k] = v }
288
288
  ActiveSupport::IsolatedExecutionState.share_with(t1)
289
289
 
@@ -321,46 +321,52 @@ module ActionController
321
321
  response.close if response
322
322
  end
323
323
 
324
- # Sends a stream to the browser, which is helpful when you're generating exports or other running data where you
325
- # don't want the entire file buffered in memory first. Similar to send_data, but where the data is generated live.
324
+ # Sends a stream to the browser, which is helpful when you're generating exports
325
+ # or other running data where you don't want the entire file buffered in memory
326
+ # first. Similar to send_data, but where the data is generated live.
326
327
  #
327
328
  # Options:
328
- # * <tt>:filename</tt> - suggests a filename for the browser to use.
329
- # * <tt>:type</tt> - specifies an HTTP content type.
330
- # You can specify either a string or a symbol for a registered type with <tt>Mime::Type.register</tt>, for example :json.
331
- # If omitted, type will be inferred from the file extension specified in <tt>:filename</tt>.
332
- # If no content type is registered for the extension, the default type 'application/octet-stream' will be used.
333
- # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
334
- # Valid values are 'inline' and 'attachment' (default).
329
+ # * `:filename` - suggests a filename for the browser to use.
330
+ # * `:type` - specifies an HTTP content type. You can specify either a string
331
+ # or a symbol for a registered type with `Mime::Type.register`, for example
332
+ # :json. If omitted, type will be inferred from the file extension specified
333
+ # in `:filename`. If no content type is registered for the extension, the
334
+ # default type 'application/octet-stream' will be used.
335
+ # * `:disposition` - specifies whether the file will be shown inline or
336
+ # downloaded. Valid values are 'inline' and 'attachment' (default).
337
+ #
335
338
  #
336
339
  # Example of generating a csv export:
337
340
  #
338
- # send_stream(filename: "subscribers.csv") do |stream|
339
- # stream.write "email_address,updated_at\n"
341
+ # send_stream(filename: "subscribers.csv") do |stream|
342
+ # stream.write "email_address,updated_at\n"
340
343
  #
341
- # @subscribers.find_each do |subscriber|
342
- # stream.write "#{subscriber.email_address},#{subscriber.updated_at}\n"
343
- # end
344
- # end
344
+ # @subscribers.find_each do |subscriber|
345
+ # stream.write "#{subscriber.email_address},#{subscriber.updated_at}\n"
346
+ # end
347
+ # end
345
348
  def send_stream(filename:, disposition: "attachment", type: nil)
346
- response.headers["Content-Type"] =
347
- (type.is_a?(Symbol) ? Mime[type].to_s : type) ||
348
- Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete("."))&.to_s ||
349
- "application/octet-stream"
349
+ payload = { filename: filename, disposition: disposition, type: type }
350
+ ActiveSupport::Notifications.instrument("send_stream.action_controller", payload) do
351
+ response.headers["Content-Type"] =
352
+ (type.is_a?(Symbol) ? Mime[type].to_s : type) ||
353
+ Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete("."))&.to_s ||
354
+ "application/octet-stream"
350
355
 
351
- response.headers["Content-Disposition"] =
352
- ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename)
356
+ response.headers["Content-Disposition"] =
357
+ ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename)
353
358
 
354
- yield response.stream
359
+ yield response.stream
360
+ end
355
361
  ensure
356
362
  response.stream.close
357
363
  end
358
364
 
359
365
  private
360
- # Spawn a new thread to serve up the controller in. This is to get
361
- # around the fact that Rack isn't based around IOs and we need to use
362
- # a thread to stream data from the response bodies. Nobody should call
363
- # this method except in Rails internals. Seriously!
366
+ # Spawn a new thread to serve up the controller in. This is to get around the
367
+ # fact that Rack isn't based around IOs and we need to use a thread to stream
368
+ # data from the response bodies. Nobody should call this method except in Rails
369
+ # internals. Seriously!
364
370
  def new_controller_thread # :nodoc:
365
371
  Thread.new {
366
372
  t2 = Thread.current
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionController
4
6
  module Logging
5
7
  extend ActiveSupport::Concern
@@ -7,10 +9,10 @@ module ActionController
7
9
  module ClassMethods
8
10
  # Set a different log level per request.
9
11
  #
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
12
+ # # Use the debug log level if a particular cookie is set.
13
+ # class ApplicationController < ActionController::Base
14
+ # log_at :debug, if: -> { cookies[:debug] }
15
+ # end
14
16
  #
15
17
  def log_at(level, **options)
16
18
  around_action ->(_, action) { logger.log_at(level, &action) }, **options