actionpack 7.1.5.1 → 7.2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -604
  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 +72 -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 +12 -13
  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 +75 -72
  20. data/lib/action_controller/base.rb +198 -126
  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 +123 -0
  26. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  27. data/lib/action_controller/metal/conditional_get.rb +187 -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 +116 -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 +483 -478
  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 +86 -60
  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 +148 -129
  63. data/lib/action_controller.rb +5 -1
  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 +40 -38
  69. data/lib/action_dispatch/http/filter_parameters.rb +9 -5
  70. data/lib/action_dispatch/http/filter_redirect.rb +22 -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 +25 -21
  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 +19 -36
  77. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  78. data/lib/action_dispatch/http/request.rb +70 -71
  79. data/lib/action_dispatch/http/response.rb +73 -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 +2 -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 +76 -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 +16 -16
  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/routes/_table.html.erb +1 -1
  125. data/lib/action_dispatch/railtie.rb +2 -3
  126. data/lib/action_dispatch/request/session.rb +23 -21
  127. data/lib/action_dispatch/request/utils.rb +2 -0
  128. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  129. data/lib/action_dispatch/routing/inspector.rb +5 -3
  130. data/lib/action_dispatch/routing/mapper.rb +622 -623
  131. data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
  132. data/lib/action_dispatch/routing/redirection.rb +37 -32
  133. data/lib/action_dispatch/routing/route_set.rb +59 -45
  134. data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
  135. data/lib/action_dispatch/routing/url_for.rb +130 -125
  136. data/lib/action_dispatch/routing.rb +150 -148
  137. data/lib/action_dispatch/system_test_case.rb +91 -81
  138. data/lib/action_dispatch/system_testing/browser.rb +4 -2
  139. data/lib/action_dispatch/system_testing/driver.rb +2 -0
  140. data/lib/action_dispatch/system_testing/server.rb +2 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  143. data/lib/action_dispatch/testing/assertion_response.rb +8 -6
  144. data/lib/action_dispatch/testing/assertions/response.rb +26 -23
  145. data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
  146. data/lib/action_dispatch/testing/assertions.rb +2 -0
  147. data/lib/action_dispatch/testing/integration.rb +223 -222
  148. data/lib/action_dispatch/testing/request_encoder.rb +2 -0
  149. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  150. data/lib/action_dispatch/testing/test_process.rb +12 -8
  151. data/lib/action_dispatch/testing/test_request.rb +3 -1
  152. data/lib/action_dispatch/testing/test_response.rb +27 -26
  153. data/lib/action_dispatch.rb +22 -28
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. data/lib/action_pack.rb +17 -16
  157. metadata +34 -11
@@ -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,47 @@ 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
+ # `:event`
81
+ # : If specified, an event with this name will be dispatched on the browser.
82
+ #
83
+ # `:retry`
84
+ # : The reconnection time in milliseconds used when attempting to send the event.
84
85
  #
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.
86
+ # `:id`
87
+ # : If the connection dies while sending an SSE to the browser, then the
88
+ # server will receive a `Last-Event-ID` header with value equal to `id`.
89
+ #
90
+ # After setting an option in the constructor of the SSE object, all future SSEs
91
+ # sent across the stream will use those options unless overridden.
87
92
  #
88
93
  # Example Usage:
89
94
  #
90
- # class MyController < ActionController::Base
91
- # include ActionController::Live
95
+ # class MyController < ActionController::Base
96
+ # include ActionController::Live
92
97
  #
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
98
+ # def index
99
+ # response.headers['Content-Type'] = 'text/event-stream'
100
+ # sse = SSE.new(response.stream, retry: 300, event: "event-name")
101
+ # sse.write({ name: 'John'})
102
+ # sse.write({ name: 'John'}, id: 10)
103
+ # sse.write({ name: 'John'}, id: 10, event: "other-event")
104
+ # sse.write({ name: 'John'}, id: 10, event: "other-event", retry: 500)
105
+ # ensure
106
+ # sse.close
107
+ # end
102
108
  # end
103
- # end
104
109
  #
105
- # Note: SSEs are not currently supported by IE. However, they are supported
106
- # by Chrome, Firefox, Opera, and Safari.
110
+ # Note: SSEs are not currently supported by IE. However, they are supported by
111
+ # Chrome, Firefox, Opera, and Safari.
107
112
  class SSE
108
113
  PERMITTED_OPTIONS = %w( retry event id )
109
114
 
@@ -153,10 +158,9 @@ module ActionController
153
158
 
154
159
  # Ignore that the client has disconnected.
155
160
  #
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.
161
+ # If this value is `true`, calling `write` after the client disconnects will
162
+ # result in the written content being silently discarded. If this value is
163
+ # `false` (the default), a ClientDisconnected exception will be raised.
160
164
  attr_accessor :ignore_disconnect
161
165
 
162
166
  def initialize(response)
@@ -167,9 +171,10 @@ module ActionController
167
171
  @ignore_disconnect = false
168
172
  end
169
173
 
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.
174
+ # ActionDispatch::Response delegates #to_ary to the internal
175
+ # ActionDispatch::Response::Buffer, defining #to_ary is an indicator that the
176
+ # response body can be buffered and/or cached by Rack middlewares, this is not
177
+ # the case for Live responses so we undefine it for this Buffer subclass.
173
178
  undef_method :to_ary
174
179
 
175
180
  def write(string)
@@ -184,21 +189,20 @@ module ActionController
184
189
  @buf.clear
185
190
 
186
191
  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.
192
+ # Raise ClientDisconnected, which is a RuntimeError (not an IOError), because
193
+ # that's more appropriate for something beyond the developer's control.
190
194
  raise ClientDisconnected, "client disconnected"
191
195
  end
192
196
  end
193
197
  end
194
198
 
195
- # Same as +write+ but automatically include a newline at the end of the string.
199
+ # Same as `write` but automatically include a newline at the end of the string.
196
200
  def writeln(string)
197
201
  write string.end_with?("\n") ? string : "#{string}\n"
198
202
  end
199
203
 
200
- # Write a 'close' event to the buffer; the producer/writing thread
201
- # uses this to notify us that it's finished supplying content.
204
+ # Write a 'close' event to the buffer; the producer/writing thread uses this to
205
+ # notify us that it's finished supplying content.
202
206
  #
203
207
  # See also #abort.
204
208
  def close
@@ -209,9 +213,8 @@ module ActionController
209
213
  end
210
214
  end
211
215
 
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.
216
+ # Inform the producer/writing thread that the client has disconnected; the
217
+ # reading thread is no longer interested in anything that's being written.
215
218
  #
216
219
  # See also #close.
217
220
  def abort
@@ -223,8 +226,8 @@ module ActionController
223
226
 
224
227
  # Is the client still connected and waiting for content?
225
228
  #
226
- # The result of calling `write` when this is `false` is determined
227
- # by `ignore_disconnect`.
229
+ # The result of calling `write` when this is `false` is determined by
230
+ # `ignore_disconnect`.
228
231
  def connected?
229
232
  !@aborted
230
233
  end
@@ -275,15 +278,15 @@ module ActionController
275
278
  locals = t1.keys.map { |key| [key, t1[key]] }
276
279
 
277
280
  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.
281
+ # This processes the action in a child thread. It lets us return the response
282
+ # code and headers back up the Rack stack, and still process the body in
283
+ # parallel with sending data to the client.
281
284
  new_controller_thread {
282
285
  ActiveSupport::Dependencies.interlock.running do
283
286
  t2 = Thread.current
284
287
 
285
- # Since we're processing the view in a different thread, copy the
286
- # thread locals from the main thread to the child thread. :'(
288
+ # Since we're processing the view in a different thread, copy the thread locals
289
+ # from the main thread to the child thread. :'(
287
290
  locals.each { |k, v| t2[k] = v }
288
291
  ActiveSupport::IsolatedExecutionState.share_with(t1)
289
292
 
@@ -321,46 +324,52 @@ module ActionController
321
324
  response.close if response
322
325
  end
323
326
 
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.
327
+ # Sends a stream to the browser, which is helpful when you're generating exports
328
+ # or other running data where you don't want the entire file buffered in memory
329
+ # first. Similar to send_data, but where the data is generated live.
326
330
  #
327
331
  # 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).
332
+ # * `:filename` - suggests a filename for the browser to use.
333
+ # * `:type` - specifies an HTTP content type. You can specify either a string
334
+ # or a symbol for a registered type with `Mime::Type.register`, for example
335
+ # :json. If omitted, type will be inferred from the file extension specified
336
+ # in `:filename`. If no content type is registered for the extension, the
337
+ # default type 'application/octet-stream' will be used.
338
+ # * `:disposition` - specifies whether the file will be shown inline or
339
+ # downloaded. Valid values are 'inline' and 'attachment' (default).
340
+ #
335
341
  #
336
342
  # Example of generating a csv export:
337
343
  #
338
- # send_stream(filename: "subscribers.csv") do |stream|
339
- # stream.write "email_address,updated_at\n"
344
+ # send_stream(filename: "subscribers.csv") do |stream|
345
+ # stream.write "email_address,updated_at\n"
340
346
  #
341
- # @subscribers.find_each do |subscriber|
342
- # stream.write "#{subscriber.email_address},#{subscriber.updated_at}\n"
343
- # end
344
- # end
347
+ # @subscribers.find_each do |subscriber|
348
+ # stream.write "#{subscriber.email_address},#{subscriber.updated_at}\n"
349
+ # end
350
+ # end
345
351
  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"
352
+ payload = { filename: filename, disposition: disposition, type: type }
353
+ ActiveSupport::Notifications.instrument("send_stream.action_controller", payload) do
354
+ response.headers["Content-Type"] =
355
+ (type.is_a?(Symbol) ? Mime[type].to_s : type) ||
356
+ Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete("."))&.to_s ||
357
+ "application/octet-stream"
350
358
 
351
- response.headers["Content-Disposition"] =
352
- ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename)
359
+ response.headers["Content-Disposition"] =
360
+ ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename)
353
361
 
354
- yield response.stream
362
+ yield response.stream
363
+ end
355
364
  ensure
356
365
  response.stream.close
357
366
  end
358
367
 
359
368
  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!
369
+ # Spawn a new thread to serve up the controller in. This is to get around the
370
+ # fact that Rack isn't based around IOs and we need to use a thread to stream
371
+ # data from the response bodies. Nobody should call this method except in Rails
372
+ # internals. Seriously!
364
373
  def new_controller_thread # :nodoc:
365
374
  Thread.new {
366
375
  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