actionpack 7.1.3 → 7.2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -501
  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 +15 -7
  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 +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 +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 +210 -205
  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 +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 +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 +18 -9
  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 +31 -24
  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 +20 -44
  77. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  78. data/lib/action_dispatch/http/request.rb +94 -75
  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 +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 +671 -636
  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 +39 -16
@@ -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