actionpack 7.0.8 → 7.1.0.rc1

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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +324 -383
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/abstract_controller/base.rb +19 -10
  6. data/lib/abstract_controller/caching/fragments.rb +2 -0
  7. data/lib/abstract_controller/callbacks.rb +31 -6
  8. data/lib/abstract_controller/deprecator.rb +7 -0
  9. data/lib/abstract_controller/helpers.rb +61 -18
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
  11. data/lib/abstract_controller/rendering.rb +3 -3
  12. data/lib/abstract_controller/translation.rb +1 -5
  13. data/lib/abstract_controller/url_for.rb +2 -0
  14. data/lib/abstract_controller.rb +6 -0
  15. data/lib/action_controller/api.rb +5 -3
  16. data/lib/action_controller/base.rb +3 -17
  17. data/lib/action_controller/caching.rb +2 -0
  18. data/lib/action_controller/deprecator.rb +7 -0
  19. data/lib/action_controller/form_builder.rb +2 -0
  20. data/lib/action_controller/log_subscriber.rb +16 -4
  21. data/lib/action_controller/metal/content_security_policy.rb +1 -1
  22. data/lib/action_controller/metal/data_streaming.rb +2 -0
  23. data/lib/action_controller/metal/default_headers.rb +2 -0
  24. data/lib/action_controller/metal/etag_with_flash.rb +2 -0
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  26. data/lib/action_controller/metal/exceptions.rb +8 -0
  27. data/lib/action_controller/metal/head.rb +8 -6
  28. data/lib/action_controller/metal/helpers.rb +3 -14
  29. data/lib/action_controller/metal/http_authentication.rb +11 -4
  30. data/lib/action_controller/metal/implicit_render.rb +5 -3
  31. data/lib/action_controller/metal/instrumentation.rb +8 -1
  32. data/lib/action_controller/metal/live.rb +24 -0
  33. data/lib/action_controller/metal/mime_responds.rb +2 -2
  34. data/lib/action_controller/metal/params_wrapper.rb +3 -1
  35. data/lib/action_controller/metal/permissions_policy.rb +1 -1
  36. data/lib/action_controller/metal/redirecting.rb +6 -6
  37. data/lib/action_controller/metal/renderers.rb +2 -2
  38. data/lib/action_controller/metal/rendering.rb +0 -7
  39. data/lib/action_controller/metal/request_forgery_protection.rb +138 -50
  40. data/lib/action_controller/metal/rescue.rb +2 -0
  41. data/lib/action_controller/metal/streaming.rb +70 -30
  42. data/lib/action_controller/metal/strong_parameters.rb +122 -52
  43. data/lib/action_controller/metal/url_for.rb +7 -0
  44. data/lib/action_controller/metal.rb +79 -21
  45. data/lib/action_controller/railtie.rb +22 -9
  46. data/lib/action_controller/renderer.rb +98 -65
  47. data/lib/action_controller/test_case.rb +15 -5
  48. data/lib/action_controller.rb +8 -1
  49. data/lib/action_dispatch/constants.rb +32 -0
  50. data/lib/action_dispatch/deprecator.rb +7 -0
  51. data/lib/action_dispatch/http/cache.rb +1 -3
  52. data/lib/action_dispatch/http/content_security_policy.rb +9 -8
  53. data/lib/action_dispatch/http/filter_parameters.rb +11 -5
  54. data/lib/action_dispatch/http/headers.rb +2 -0
  55. data/lib/action_dispatch/http/mime_negotiation.rb +21 -21
  56. data/lib/action_dispatch/http/mime_type.rb +35 -12
  57. data/lib/action_dispatch/http/mime_types.rb +3 -1
  58. data/lib/action_dispatch/http/parameters.rb +1 -1
  59. data/lib/action_dispatch/http/permissions_policy.rb +39 -17
  60. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  61. data/lib/action_dispatch/http/request.rb +48 -14
  62. data/lib/action_dispatch/http/response.rb +78 -59
  63. data/lib/action_dispatch/http/upload.rb +2 -0
  64. data/lib/action_dispatch/journey/formatter.rb +8 -2
  65. data/lib/action_dispatch/journey/path/pattern.rb +14 -14
  66. data/lib/action_dispatch/journey/route.rb +3 -2
  67. data/lib/action_dispatch/journey/router.rb +9 -8
  68. data/lib/action_dispatch/journey/routes.rb +2 -2
  69. data/lib/action_dispatch/log_subscriber.rb +23 -0
  70. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
  71. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  72. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  73. data/lib/action_dispatch/middleware/cookies.rb +81 -98
  74. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
  75. data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
  76. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  77. data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -27
  78. data/lib/action_dispatch/middleware/executor.rb +1 -1
  79. data/lib/action_dispatch/middleware/flash.rb +7 -0
  80. data/lib/action_dispatch/middleware/host_authorization.rb +6 -3
  81. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  82. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  83. data/lib/action_dispatch/middleware/remote_ip.rb +17 -16
  84. data/lib/action_dispatch/middleware/request_id.rb +2 -0
  85. data/lib/action_dispatch/middleware/server_timing.rb +4 -4
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
  89. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  90. data/lib/action_dispatch/middleware/show_exceptions.rb +19 -15
  91. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  92. data/lib/action_dispatch/middleware/stack.rb +7 -2
  93. data/lib/action_dispatch/middleware/static.rb +12 -8
  94. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  95. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
  96. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  97. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
  98. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  99. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  103. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
  104. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  105. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  106. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  107. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +46 -37
  108. data/lib/action_dispatch/railtie.rb +14 -4
  109. data/lib/action_dispatch/request/session.rb +16 -6
  110. data/lib/action_dispatch/request/utils.rb +8 -3
  111. data/lib/action_dispatch/routing/inspector.rb +54 -6
  112. data/lib/action_dispatch/routing/mapper.rb +26 -14
  113. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  114. data/lib/action_dispatch/routing/redirection.rb +15 -6
  115. data/lib/action_dispatch/routing/route_set.rb +52 -22
  116. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  117. data/lib/action_dispatch/routing/url_for.rb +5 -1
  118. data/lib/action_dispatch/routing.rb +4 -4
  119. data/lib/action_dispatch/system_test_case.rb +3 -3
  120. data/lib/action_dispatch/system_testing/browser.rb +5 -6
  121. data/lib/action_dispatch/system_testing/driver.rb +13 -21
  122. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
  123. data/lib/action_dispatch/testing/assertions/response.rb +13 -6
  124. data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
  125. data/lib/action_dispatch/testing/assertions.rb +3 -1
  126. data/lib/action_dispatch/testing/integration.rb +27 -17
  127. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  128. data/lib/action_dispatch/testing/test_process.rb +4 -3
  129. data/lib/action_dispatch/testing/test_request.rb +1 -1
  130. data/lib/action_dispatch/testing/test_response.rb +23 -9
  131. data/lib/action_dispatch.rb +37 -4
  132. data/lib/action_pack/gem_version.rb +4 -4
  133. data/lib/action_pack/version.rb +1 -1
  134. data/lib/action_pack.rb +1 -1
  135. metadata +51 -29
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionController # :nodoc:
4
+ # = Action Controller \Rescue
5
+ #
4
6
  # This module is responsible for providing
5
7
  # {rescue_from}[rdoc-ref:ActiveSupport::Rescuable::ClassMethods#rescue_from]
6
8
  # to controllers, wrapping actions to handle configured errors, and
@@ -1,30 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack/chunked"
4
-
5
3
  module ActionController # :nodoc:
4
+ # = Action Controller \Streaming
5
+ #
6
6
  # Allows views to be streamed back to the client as they are rendered.
7
7
  #
8
- # By default, Rails renders views by first rendering the template
8
+ # By default, \Rails renders views by first rendering the template
9
9
  # and then the layout. The response is sent to the client after the whole
10
10
  # template is rendered, all queries are made, and the layout is processed.
11
11
  #
12
- # Streaming inverts the rendering flow by rendering the layout first and
13
- # streaming each part of the layout as they are processed. This allows the
12
+ # \Streaming inverts the rendering flow by rendering the layout first and
13
+ # subsequently each part of the layout as they are processed. This allows the
14
14
  # header of the HTML (which is usually in the layout) to be streamed back
15
- # to client very quickly, allowing JavaScripts and stylesheets to be loaded
15
+ # to client very quickly, enabling JavaScripts and stylesheets to be loaded
16
16
  # earlier than usual.
17
17
  #
18
- # This approach was introduced in Rails 3.1 and is still improving. Several
19
- # Rack middlewares may not work and you need to be careful when streaming.
20
- # Those points are going to be addressed soon.
21
- #
22
- # In order to use streaming, you will need to use a Ruby version that
23
- # supports fibers (fibers are supported since version 1.9.2 of the main
24
- # Ruby implementation).
18
+ # Several Rack middlewares may not work and you need to be careful when streaming.
19
+ # This is covered in more detail below, see the Streaming@Middlewares section.
25
20
  #
26
- # Streaming can be added to a given template easily, all you need to do is
27
- # to pass the +:stream+ option.
21
+ # \Streaming can be added to a given template easily, all you need to do is
22
+ # to pass the +:stream+ option to +render+.
28
23
  #
29
24
  # class PostsController
30
25
  # def index
@@ -35,7 +30,7 @@ module ActionController # :nodoc:
35
30
  #
36
31
  # == When to use streaming
37
32
  #
38
- # Streaming may be considered to be overkill for lightweight actions like
33
+ # \Streaming may be considered to be overkill for lightweight actions like
39
34
  # +new+ or +edit+. The real benefit of streaming is on expensive actions
40
35
  # that, for example, do a lot of queries on the database.
41
36
  #
@@ -59,13 +54,13 @@ module ActionController # :nodoc:
59
54
  # render stream: true
60
55
  # end
61
56
  #
62
- # Notice that +:stream+ only works with templates. Rendering +:json+
57
+ # Notice that +:stream+ only works with templates. \Rendering +:json+
63
58
  # or +:xml+ with +:stream+ won't work.
64
59
  #
65
60
  # == Communication between layout and template
66
61
  #
67
62
  # When streaming, rendering happens top-down instead of inside-out.
68
- # Rails starts with the layout, and the template is rendered later,
63
+ # \Rails starts with the layout, and the template is rendered later,
69
64
  # when its +yield+ is reached.
70
65
  #
71
66
  # This means that, if your application currently relies on instance
@@ -112,7 +107,7 @@ module ActionController # :nodoc:
112
107
  # This means that, if you have <code>yield :title</code> in your layout
113
108
  # and you want to use streaming, you would have to render the whole template
114
109
  # (and eventually trigger all queries) before streaming the title and all
115
- # assets, which kills the purpose of streaming. For this purpose, you can use
110
+ # assets, which defeats the purpose of streaming. Alternatively, you can use
116
111
  # a helper called +provide+ that does the same as +content_for+ but tells the
117
112
  # layout to stop searching for other entries and continue rendering.
118
113
  #
@@ -122,7 +117,7 @@ module ActionController # :nodoc:
122
117
  # Hello
123
118
  # <%= content_for :title, " page" %>
124
119
  #
125
- # Giving:
120
+ # Resulting in:
126
121
  #
127
122
  # <html>
128
123
  # <head><title>Main</title></head>
@@ -132,6 +127,8 @@ module ActionController # :nodoc:
132
127
  # That said, when streaming, you need to properly check your templates
133
128
  # and choose when to use +provide+ and +content_for+.
134
129
  #
130
+ # See also ActionView::Helpers::CaptureHelper for more information.
131
+ #
135
132
  # == Headers, cookies, session, and flash
136
133
  #
137
134
  # When streaming, the HTTP headers are sent to the client right before
@@ -143,10 +140,10 @@ module ActionController # :nodoc:
143
140
  #
144
141
  # Middlewares that need to manipulate the body won't work with streaming.
145
142
  # You should disable those middlewares whenever streaming in development
146
- # or production. For instance, <tt>Rack::Bug</tt> won't work when streaming as it
143
+ # or production. For instance, +Rack::Bug+ won't work when streaming as it
147
144
  # needs to inject contents in the HTML body.
148
145
  #
149
- # Also <tt>Rack::Cache</tt> won't work with streaming as it does not support
146
+ # Also +Rack::Cache+ won't work with streaming as it does not support
150
147
  # streaming bodies yet. Whenever streaming +Cache-Control+ is automatically
151
148
  # set to "no-cache".
152
149
  #
@@ -156,14 +153,14 @@ module ActionController # :nodoc:
156
153
  # happens because part of the template was already rendered and streamed to
157
154
  # the client, making it impossible to render a whole exception page.
158
155
  #
159
- # Currently, when an exception happens in development or production, Rails
156
+ # Currently, when an exception happens in development or production, \Rails
160
157
  # will automatically stream to the client:
161
158
  #
162
159
  # "><script>window.location = "/500.html"</script></html>
163
160
  #
164
- # The first two characters (">) are required in case the exception happens
165
- # while rendering attributes for a given tag. You can check the real cause
166
- # for the exception in your logger.
161
+ # The first two characters (<tt>"></tt>) are required in case the exception
162
+ # happens while rendering attributes for a given tag. You can check the real
163
+ # cause for the exception in your logger.
167
164
  #
168
165
  # == Web server support
169
166
  #
@@ -183,16 +180,59 @@ module ActionController # :nodoc:
183
180
  # unicorn_rails --config-file unicorn.config.rb
184
181
  #
185
182
  # You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
186
- # Please check its documentation for more information: https://bogomips.org/unicorn/Unicorn/Configurator.html#method-i-listen
183
+ #
184
+ # For more information, please check the
185
+ # {documentation}[https://bogomips.org/unicorn/Unicorn/Configurator.html#method-i-listen].
187
186
  #
188
187
  # If you are using Unicorn with NGINX, you may need to tweak NGINX.
189
- # Streaming should work out of the box on Rainbows.
188
+ # \Streaming should work out of the box on Rainbows.
190
189
  #
191
190
  # ==== Passenger
192
191
  #
193
- # To be described.
192
+ # Phusion Passenger with NGINX, offers two streaming mechanisms out of the box.
193
+ #
194
+ # 1. NGINX response buffering mechanism which is dependent on the value of
195
+ # +passenger_buffer_response+ option (default is "off").
196
+ # 2. Passenger buffering system which is always 'on' irrespective of the value
197
+ # of +passenger_buffer_response+.
198
+ #
199
+ # When +passenger_buffer_response+ is turned "on", then streaming would be
200
+ # done at the NGINX level which waits until the application is done sending
201
+ # the response back to the client.
202
+ #
203
+ # For more information, please check the
204
+ # {documentation}[https://www.phusionpassenger.com/docs/references/config_reference/nginx/#passenger_buffer_response].
194
205
  #
195
206
  module Streaming
207
+ class Body # :nodoc:
208
+ TERM = "\r\n"
209
+ TAIL = "0#{TERM}"
210
+
211
+ # Store the response body to be chunked.
212
+ def initialize(body)
213
+ @body = body
214
+ end
215
+
216
+ # For each element yielded by the response body, yield
217
+ # the element in chunked encoding.
218
+ def each(&block)
219
+ term = TERM
220
+ @body.each do |chunk|
221
+ size = chunk.bytesize
222
+ next if size == 0
223
+
224
+ yield [size.to_s(16), term, chunk.b, term].join
225
+ end
226
+ yield TAIL
227
+ yield term
228
+ end
229
+
230
+ # Close the response body if the response body supports it.
231
+ def close
232
+ @body.close if @body.respond_to?(:close)
233
+ end
234
+ end
235
+
196
236
  private
197
237
  # Set proper cache control and transfer encoding when streaming
198
238
  def _process_options(options)
@@ -211,7 +251,7 @@ module ActionController # :nodoc:
211
251
  # Call render_body if we are streaming instead of usual +render+.
212
252
  def _render_template(options)
213
253
  if options.delete(:stream)
214
- Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
254
+ Body.new view_renderer.render_body(view_context, options)
215
255
  else
216
256
  super
217
257
  end
@@ -4,6 +4,7 @@ require "active_support/core_ext/hash/indifferent_access"
4
4
  require "active_support/core_ext/array/wrap"
5
5
  require "active_support/core_ext/string/filters"
6
6
  require "active_support/core_ext/object/to_query"
7
+ require "active_support/deep_mergeable"
7
8
  require "action_dispatch/http/upload"
8
9
  require "rack/test"
9
10
  require "stringio"
@@ -64,7 +65,14 @@ module ActionController
64
65
  end
65
66
  end
66
67
 
67
- # == Action Controller \Parameters
68
+ # Raised when initializing Parameters with keys that aren't strings or symbols.
69
+ #
70
+ # ActionController::Parameters.new(123 => 456)
71
+ # # => ActionController::InvalidParameterKey: all keys must be Strings or Symbols, got: Integer
72
+ class InvalidParameterKey < ArgumentError
73
+ end
74
+
75
+ # = Action Controller \Parameters
68
76
  #
69
77
  # Allows you to choose which attributes should be permitted for mass updating
70
78
  # and thus prevent accidentally exposing that which shouldn't be exposed.
@@ -92,8 +100,8 @@ module ActionController
92
100
  # * +permit_all_parameters+ - If it's +true+, all the parameters will be
93
101
  # permitted by default. The default is +false+.
94
102
  # * +action_on_unpermitted_parameters+ - Controls behavior when parameters that are not explicitly
95
- # permitted are found. The default value is <tt>:log</tt> in test and development environments,
96
- # +false+ otherwise. The values can be:
103
+ # permitted are found. The default value is <tt>:log</tt> in test and development environments,
104
+ # +false+ otherwise. The values can be:
97
105
  # * +false+ to take no action.
98
106
  # * <tt>:log</tt> to emit an <tt>ActiveSupport::Notifications.instrument</tt> event on the
99
107
  # <tt>unpermitted_parameters.action_controller</tt> topic and log at the DEBUG level.
@@ -123,13 +131,15 @@ module ActionController
123
131
  # environment they should only be set once at boot-time and never mutated at
124
132
  # runtime.
125
133
  #
126
- # You can fetch values of <tt>ActionController::Parameters</tt> using either
134
+ # You can fetch values of +ActionController::Parameters+ using either
127
135
  # <tt>:key</tt> or <tt>"key"</tt>.
128
136
  #
129
137
  # params = ActionController::Parameters.new(key: "value")
130
138
  # params[:key] # => "value"
131
139
  # params["key"] # => "value"
132
140
  class Parameters
141
+ include ActiveSupport::DeepMergeable
142
+
133
143
  cattr_accessor :permit_all_parameters, instance_accessor: false, default: false
134
144
 
135
145
  cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
@@ -160,12 +170,12 @@ module ActionController
160
170
  # Returns true if the parameters have no key/value pairs.
161
171
 
162
172
  ##
163
- # :method: has_value?
173
+ # :method: exclude?
164
174
  #
165
175
  # :call-seq:
166
- # has_value?(value)
176
+ # exclude?(key)
167
177
  #
168
- # Returns true if the given value is present for some key in the parameters.
178
+ # Returns true if the given key is not present in the parameters.
169
179
 
170
180
  ##
171
181
  # :method: include?
@@ -191,22 +201,7 @@ module ActionController
191
201
  #
192
202
  # Returns the content of the parameters as a string.
193
203
 
194
- ##
195
- # :method: value?
196
- #
197
- # :call-seq:
198
- # value?(value)
199
- #
200
- # Returns true if the given value is present for some key in the parameters.
201
-
202
- ##
203
- # :method: values
204
- #
205
- # :call-seq:
206
- # values()
207
- #
208
- # Returns a new array of the values of the parameters.
209
- delegate :keys, :values, :has_value?, :value?, :empty?, :include?,
204
+ delegate :keys, :empty?, :exclude?, :include?,
210
205
  :as_json, :to_s, :each_key, to: :@parameters
211
206
 
212
207
  alias_method :has_key?, :include?
@@ -222,13 +217,15 @@ module ActionController
222
217
  # config.action_controller.always_permitted_parameters = %w( controller action format )
223
218
  cattr_accessor :always_permitted_parameters, default: %w( controller action )
224
219
 
220
+ cattr_accessor :allow_deprecated_parameters_hash_equality, default: true, instance_accessor: false
221
+
225
222
  class << self
226
223
  def nested_attribute?(key, value) # :nodoc:
227
224
  /\A-?\d+\z/.match?(key) && (value.is_a?(Hash) || value.is_a?(Parameters))
228
225
  end
229
226
  end
230
227
 
231
- # Returns a new <tt>ActionController::Parameters</tt> instance.
228
+ # Returns a new +ActionController::Parameters+ instance.
232
229
  # Also, sets the +permitted+ attribute to the default value of
233
230
  # <tt>ActionController::Parameters.permit_all_parameters</tt>.
234
231
  #
@@ -245,6 +242,12 @@ module ActionController
245
242
  # params.permitted? # => true
246
243
  # Person.new(params) # => #<Person id: nil, name: "Francesco">
247
244
  def initialize(parameters = {}, logging_context = {})
245
+ parameters.each_key do |key|
246
+ unless key.is_a?(String) || key.is_a?(Symbol)
247
+ raise InvalidParameterKey, "all keys must be Strings or Symbols, got: #{key.class}"
248
+ end
249
+ end
250
+
248
251
  @parameters = parameters.with_indifferent_access
249
252
  @logging_context = logging_context
250
253
  @permitted = self.class.permit_all_parameters
@@ -256,7 +259,20 @@ module ActionController
256
259
  if other.respond_to?(:permitted?)
257
260
  permitted? == other.permitted? && parameters == other.parameters
258
261
  else
259
- @parameters == other
262
+ if self.class.allow_deprecated_parameters_hash_equality && Hash === other
263
+ ActionController.deprecator.warn <<-WARNING.squish
264
+ Comparing equality between `ActionController::Parameters` and a
265
+ `Hash` is deprecated and will be removed in Rails 7.2. Please only do
266
+ comparisons between instances of `ActionController::Parameters`. If
267
+ you need to compare to a hash, first convert it using
268
+ `ActionController::Parameters#new`.
269
+ To disable the deprecated behavior set
270
+ `Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality = false`.
271
+ WARNING
272
+ @parameters == other
273
+ else
274
+ super
275
+ end
260
276
  end
261
277
  end
262
278
 
@@ -282,9 +298,9 @@ module ActionController
282
298
  #
283
299
  # safe_params = params.permit(:name)
284
300
  # safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
285
- def to_h
301
+ def to_h(&block)
286
302
  if permitted?
287
- convert_parameters_to_hashes(@parameters, :to_h)
303
+ convert_parameters_to_hashes(@parameters, :to_h, &block)
288
304
  else
289
305
  raise UnfilteredParameters
290
306
  end
@@ -374,6 +390,11 @@ module ActionController
374
390
  self
375
391
  end
376
392
 
393
+ # Returns a new array of the values of the parameters.
394
+ def values
395
+ to_enum(:each_value).to_a
396
+ end
397
+
377
398
  # Attribute that keeps track of converted arrays, if any, to avoid double
378
399
  # looping in the common use case permit + mass-assignment. Defined in a
379
400
  # method to instantiate it only if needed.
@@ -480,7 +501,7 @@ module ActionController
480
501
 
481
502
  alias :required :require
482
503
 
483
- # Returns a new <tt>ActionController::Parameters</tt> instance that
504
+ # Returns a new +ActionController::Parameters+ instance that
484
505
  # includes only the given +filters+ and sets the +permitted+ attribute
485
506
  # for the object to +true+. This is useful for limiting which attributes
486
507
  # should be allowed for mass updating.
@@ -665,7 +686,7 @@ module ActionController
665
686
  @parameters.dig(*keys)
666
687
  end
667
688
 
668
- # Returns a new <tt>ActionController::Parameters</tt> instance that
689
+ # Returns a new +ActionController::Parameters+ instance that
669
690
  # includes only the given +keys+. If the given +keys+
670
691
  # don't exist, returns an empty hash.
671
692
  #
@@ -676,14 +697,14 @@ module ActionController
676
697
  new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
677
698
  end
678
699
 
679
- # Returns the current <tt>ActionController::Parameters</tt> instance which
700
+ # Returns the current +ActionController::Parameters+ instance which
680
701
  # contains only the given +keys+.
681
702
  def slice!(*keys)
682
703
  @parameters.slice!(*keys)
683
704
  self
684
705
  end
685
706
 
686
- # Returns a new <tt>ActionController::Parameters</tt> instance that
707
+ # Returns a new +ActionController::Parameters+ instance that
687
708
  # filters out the given +keys+.
688
709
  #
689
710
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
@@ -692,6 +713,7 @@ module ActionController
692
713
  def except(*keys)
693
714
  new_instance_with_inherited_permitted_status(@parameters.except(*keys))
694
715
  end
716
+ alias_method :without, :except
695
717
 
696
718
  # Removes and returns the key/value pairs matching the given keys.
697
719
  #
@@ -702,7 +724,7 @@ module ActionController
702
724
  new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
703
725
  end
704
726
 
705
- # Returns a new <tt>ActionController::Parameters</tt> instance with the results of
727
+ # Returns a new +ActionController::Parameters+ instance with the results of
706
728
  # running +block+ once for every value. The keys are unchanged.
707
729
  #
708
730
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
@@ -716,14 +738,14 @@ module ActionController
716
738
  end
717
739
 
718
740
  # Performs values transformation and returns the altered
719
- # <tt>ActionController::Parameters</tt> instance.
741
+ # +ActionController::Parameters+ instance.
720
742
  def transform_values!
721
743
  return to_enum(:transform_values!) unless block_given?
722
744
  @parameters.transform_values! { |v| yield convert_value_to_parameters(v) }
723
745
  self
724
746
  end
725
747
 
726
- # Returns a new <tt>ActionController::Parameters</tt> instance with the
748
+ # Returns a new +ActionController::Parameters+ instance with the
727
749
  # results of running +block+ once for every key. The values are unchanged.
728
750
  def transform_keys(&block)
729
751
  return to_enum(:transform_keys) unless block_given?
@@ -733,14 +755,14 @@ module ActionController
733
755
  end
734
756
 
735
757
  # Performs keys transformation and returns the altered
736
- # <tt>ActionController::Parameters</tt> instance.
758
+ # +ActionController::Parameters+ instance.
737
759
  def transform_keys!(&block)
738
760
  return to_enum(:transform_keys!) unless block_given?
739
761
  @parameters.transform_keys!(&block)
740
762
  self
741
763
  end
742
764
 
743
- # Returns a new <tt>ActionController::Parameters</tt> instance with the
765
+ # Returns a new +ActionController::Parameters+ instance with the
744
766
  # results of running +block+ once for every key. This includes the keys
745
767
  # from the root hash and from all nested hashes and arrays. The values are unchanged.
746
768
  def deep_transform_keys(&block)
@@ -749,7 +771,7 @@ module ActionController
749
771
  )
750
772
  end
751
773
 
752
- # Returns the same <tt>ActionController::Parameters</tt> instance with
774
+ # Returns the same +ActionController::Parameters+ instance with
753
775
  # changed keys. This includes the keys from the root hash and from all
754
776
  # nested hashes and arrays. The values are unchanged.
755
777
  def deep_transform_keys!(&block)
@@ -765,7 +787,7 @@ module ActionController
765
787
  convert_value_to_parameters(@parameters.delete(key, &block))
766
788
  end
767
789
 
768
- # Returns a new <tt>ActionController::Parameters</tt> instance with only
790
+ # Returns a new +ActionController::Parameters+ instance with only
769
791
  # items that the block evaluates to true.
770
792
  def select(&block)
771
793
  new_instance_with_inherited_permitted_status(@parameters.select(&block))
@@ -778,7 +800,7 @@ module ActionController
778
800
  end
779
801
  alias_method :keep_if, :select!
780
802
 
781
- # Returns a new <tt>ActionController::Parameters</tt> instance with items
803
+ # Returns a new +ActionController::Parameters+ instance with items
782
804
  # that the block evaluates to true removed.
783
805
  def reject(&block)
784
806
  new_instance_with_inherited_permitted_status(@parameters.reject(&block))
@@ -791,7 +813,7 @@ module ActionController
791
813
  end
792
814
  alias_method :delete_if, :reject!
793
815
 
794
- # Returns a new <tt>ActionController::Parameters</tt> instance with +nil+ values removed.
816
+ # Returns a new +ActionController::Parameters+ instance with +nil+ values removed.
795
817
  def compact
796
818
  new_instance_with_inherited_permitted_status(@parameters.compact)
797
819
  end
@@ -801,7 +823,7 @@ module ActionController
801
823
  self if @parameters.compact!
802
824
  end
803
825
 
804
- # Returns a new <tt>ActionController::Parameters</tt> instance without the blank values.
826
+ # Returns a new +ActionController::Parameters+ instance without the blank values.
805
827
  # Uses Object#blank? for determining if a value is blank.
806
828
  def compact_blank
807
829
  reject { |_k, v| v.blank? }
@@ -813,13 +835,20 @@ module ActionController
813
835
  reject! { |_k, v| v.blank? }
814
836
  end
815
837
 
838
+ # Returns true if the given value is present for some key in the parameters.
839
+ def has_value?(value)
840
+ each_value.include?(convert_value_to_parameters(value))
841
+ end
842
+
843
+ alias value? has_value?
844
+
816
845
  # Returns values that were assigned to the given +keys+. Note that all the
817
- # +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
846
+ # +Hash+ objects will be converted to +ActionController::Parameters+.
818
847
  def values_at(*keys)
819
848
  convert_value_to_parameters(@parameters.values_at(*keys))
820
849
  end
821
850
 
822
- # Returns a new <tt>ActionController::Parameters</tt> instance with all keys from
851
+ # Returns a new +ActionController::Parameters+ instance with all keys from
823
852
  # +other_hash+ merged into current hash.
824
853
  def merge(other_hash)
825
854
  new_instance_with_inherited_permitted_status(
@@ -827,14 +856,42 @@ module ActionController
827
856
  )
828
857
  end
829
858
 
830
- # Returns the current <tt>ActionController::Parameters</tt> instance with
859
+ ##
860
+ # :call-seq: merge!(other_hash)
861
+ #
862
+ # Returns the current +ActionController::Parameters+ instance with
831
863
  # +other_hash+ merged into current hash.
832
- def merge!(other_hash)
833
- @parameters.merge!(other_hash.to_h)
864
+ def merge!(other_hash, &block)
865
+ @parameters.merge!(other_hash.to_h, &block)
834
866
  self
835
867
  end
836
868
 
837
- # Returns a new <tt>ActionController::Parameters</tt> instance with all keys
869
+ ##
870
+ # :method: deep_merge
871
+ # :call-seq: deep_merge(other_hash, &block)
872
+ #
873
+ # Returns a new +ActionController::Parameters+ instance with +self+ and +other_hash+ merged recursively.
874
+ #
875
+ # Like with +Hash#merge+ in the standard library, a block can be provided
876
+ # to merge values.
877
+ #
878
+ #--
879
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge.
880
+
881
+ ##
882
+ # :method: deep_merge!
883
+ # :call-seq: deep_merge!(other_hash, &block)
884
+ #
885
+ # Same as +#deep_merge+, but modifies +self+.
886
+ #
887
+ #--
888
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge!.
889
+
890
+ def deep_merge?(other_hash) # :nodoc
891
+ other_hash.is_a?(ActiveSupport::DeepMergeable)
892
+ end
893
+
894
+ # Returns a new +ActionController::Parameters+ instance with all keys
838
895
  # from current hash merged into +other_hash+.
839
896
  def reverse_merge(other_hash)
840
897
  new_instance_with_inherited_permitted_status(
@@ -843,7 +900,7 @@ module ActionController
843
900
  end
844
901
  alias_method :with_defaults, :reverse_merge
845
902
 
846
- # Returns the current <tt>ActionController::Parameters</tt> instance with
903
+ # Returns the current +ActionController::Parameters+ instance with
847
904
  # current hash merged into +other_hash+.
848
905
  def reverse_merge!(other_hash)
849
906
  @parameters.merge!(other_hash.to_h) { |key, left, right| left }
@@ -900,6 +957,16 @@ module ActionController
900
957
  end
901
958
  end
902
959
 
960
+ # Returns parameter value for the given +key+ separated by +delimiter+.
961
+ #
962
+ # params = ActionController::Parameters.new(id: "1_123", tags: "ruby,rails")
963
+ # params.extract_value(:id) # => ["1", "123"]
964
+ # params.extract_value(:tags, delimiter: ",") # => ["ruby", "rails"]
965
+ # params.extract_value(:non_existent_key) # => nil
966
+ def extract_value(key, delimiter: "_")
967
+ @parameters[key]&.split(delimiter)
968
+ end
969
+
903
970
  protected
904
971
  attr_reader :parameters
905
972
 
@@ -922,14 +989,15 @@ module ActionController
922
989
  end
923
990
  end
924
991
 
925
- def convert_parameters_to_hashes(value, using)
992
+ def convert_parameters_to_hashes(value, using, &block)
926
993
  case value
927
994
  when Array
928
995
  value.map { |v| convert_parameters_to_hashes(v, using) }
929
996
  when Hash
930
- value.transform_values do |v|
997
+ transformed = value.transform_values do |v|
931
998
  convert_parameters_to_hashes(v, using)
932
- end.with_indifferent_access
999
+ end
1000
+ (block_given? ? transformed.to_h(&block) : transformed).with_indifferent_access
933
1001
  when Parameters
934
1002
  value.send(using)
935
1003
  else
@@ -1112,6 +1180,8 @@ module ActionController
1112
1180
  case element
1113
1181
  when ->(e) { permitted_scalar?(e) }
1114
1182
  sanitized << element
1183
+ when Array
1184
+ sanitized << permit_any_in_array(element)
1115
1185
  when Parameters
1116
1186
  sanitized << permit_any_in_parameters(element)
1117
1187
  else
@@ -1127,7 +1197,7 @@ module ActionController
1127
1197
  end
1128
1198
  end
1129
1199
 
1130
- # == Strong \Parameters
1200
+ # = Strong \Parameters
1131
1201
  #
1132
1202
  # It provides an interface for protecting attributes from end-user
1133
1203
  # assignment. This makes Action Controller parameters forbidden
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionController
4
+ # = Action Controller \UrlFor
5
+ #
4
6
  # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
5
7
  # the <tt>_routes</tt> method. Otherwise, an exception will be raised.
6
8
  #
@@ -25,6 +27,11 @@ module ActionController
25
27
 
26
28
  include AbstractController::UrlFor
27
29
 
30
+ def initialize(...)
31
+ super
32
+ @_url_options = nil
33
+ end
34
+
28
35
  def url_options
29
36
  @_url_options ||= {
30
37
  host: request.host,