actionpack 7.0.8.7 → 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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -537
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +119 -106
  7. data/lib/abstract_controller/caching/fragments.rb +51 -52
  8. data/lib/abstract_controller/caching.rb +2 -0
  9. data/lib/abstract_controller/callbacks.rb +94 -67
  10. data/lib/abstract_controller/collector.rb +6 -6
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +2 -0
  13. data/lib/abstract_controller/helpers.rb +121 -91
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
  16. data/lib/abstract_controller/rendering.rb +14 -13
  17. data/lib/abstract_controller/translation.rb +12 -30
  18. data/lib/abstract_controller/url_for.rb +9 -5
  19. data/lib/abstract_controller.rb +8 -0
  20. data/lib/action_controller/api/api_rendering.rb +2 -0
  21. data/lib/action_controller/api.rb +78 -73
  22. data/lib/action_controller/base.rb +199 -141
  23. data/lib/action_controller/caching.rb +16 -11
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +21 -16
  26. data/lib/action_controller/log_subscriber.rb +19 -5
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  29. data/lib/action_controller/metal/conditional_get.rb +187 -174
  30. data/lib/action_controller/metal/content_security_policy.rb +26 -25
  31. data/lib/action_controller/metal/cookies.rb +4 -2
  32. data/lib/action_controller/metal/data_streaming.rb +65 -54
  33. data/lib/action_controller/metal/default_headers.rb +6 -2
  34. data/lib/action_controller/metal/etag_with_flash.rb +4 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +18 -14
  36. data/lib/action_controller/metal/exceptions.rb +19 -9
  37. data/lib/action_controller/metal/flash.rb +12 -10
  38. data/lib/action_controller/metal/head.rb +20 -16
  39. data/lib/action_controller/metal/helpers.rb +64 -67
  40. data/lib/action_controller/metal/http_authentication.rb +212 -199
  41. data/lib/action_controller/metal/implicit_render.rb +21 -17
  42. data/lib/action_controller/metal/instrumentation.rb +22 -12
  43. data/lib/action_controller/metal/live.rb +125 -92
  44. data/lib/action_controller/metal/logging.rb +6 -4
  45. data/lib/action_controller/metal/mime_responds.rb +151 -142
  46. data/lib/action_controller/metal/parameter_encoding.rb +34 -32
  47. data/lib/action_controller/metal/params_wrapper.rb +58 -58
  48. data/lib/action_controller/metal/permissions_policy.rb +14 -13
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +110 -84
  51. data/lib/action_controller/metal/renderers.rb +50 -49
  52. data/lib/action_controller/metal/rendering.rb +103 -82
  53. data/lib/action_controller/metal/request_forgery_protection.rb +279 -161
  54. data/lib/action_controller/metal/rescue.rb +12 -8
  55. data/lib/action_controller/metal/streaming.rb +174 -132
  56. data/lib/action_controller/metal/strong_parameters.rb +598 -473
  57. data/lib/action_controller/metal/testing.rb +2 -0
  58. data/lib/action_controller/metal/url_for.rb +23 -14
  59. data/lib/action_controller/metal.rb +145 -61
  60. data/lib/action_controller/railtie.rb +25 -9
  61. data/lib/action_controller/railties/helpers.rb +2 -0
  62. data/lib/action_controller/renderer.rb +105 -66
  63. data/lib/action_controller/template_assertions.rb +4 -2
  64. data/lib/action_controller/test_case.rb +157 -128
  65. data/lib/action_controller.rb +17 -3
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +28 -29
  69. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +48 -45
  71. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  72. data/lib/action_dispatch/http/filter_redirect.rb +22 -1
  73. data/lib/action_dispatch/http/headers.rb +23 -21
  74. data/lib/action_dispatch/http/mime_negotiation.rb +37 -48
  75. data/lib/action_dispatch/http/mime_type.rb +60 -30
  76. data/lib/action_dispatch/http/mime_types.rb +5 -1
  77. data/lib/action_dispatch/http/parameters.rb +12 -10
  78. data/lib/action_dispatch/http/permissions_policy.rb +32 -27
  79. data/lib/action_dispatch/http/rack_cache.rb +4 -0
  80. data/lib/action_dispatch/http/request.rb +132 -79
  81. data/lib/action_dispatch/http/response.rb +136 -103
  82. data/lib/action_dispatch/http/upload.rb +19 -15
  83. data/lib/action_dispatch/http/url.rb +75 -73
  84. data/lib/action_dispatch/journey/formatter.rb +19 -6
  85. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
  88. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +6 -5
  90. data/lib/action_dispatch/journey/parser.rb +4 -3
  91. data/lib/action_dispatch/journey/parser_extras.rb +2 -0
  92. data/lib/action_dispatch/journey/path/pattern.rb +18 -15
  93. data/lib/action_dispatch/journey/route.rb +12 -9
  94. data/lib/action_dispatch/journey/router/utils.rb +16 -15
  95. data/lib/action_dispatch/journey/router.rb +13 -10
  96. data/lib/action_dispatch/journey/routes.rb +6 -4
  97. data/lib/action_dispatch/journey/scanner.rb +4 -2
  98. data/lib/action_dispatch/journey/visitors.rb +2 -0
  99. data/lib/action_dispatch/journey.rb +2 -0
  100. data/lib/action_dispatch/log_subscriber.rb +25 -0
  101. data/lib/action_dispatch/middleware/actionable_exceptions.rb +7 -6
  102. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  103. data/lib/action_dispatch/middleware/callbacks.rb +4 -0
  104. data/lib/action_dispatch/middleware/cookies.rb +192 -194
  105. data/lib/action_dispatch/middleware/debug_exceptions.rb +36 -27
  106. data/lib/action_dispatch/middleware/debug_locks.rb +18 -13
  107. data/lib/action_dispatch/middleware/debug_view.rb +9 -2
  108. data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -27
  109. data/lib/action_dispatch/middleware/executor.rb +9 -1
  110. data/lib/action_dispatch/middleware/flash.rb +65 -46
  111. data/lib/action_dispatch/middleware/host_authorization.rb +22 -17
  112. data/lib/action_dispatch/middleware/public_exceptions.rb +12 -8
  113. data/lib/action_dispatch/middleware/reloader.rb +9 -5
  114. data/lib/action_dispatch/middleware/remote_ip.rb +88 -83
  115. data/lib/action_dispatch/middleware/request_id.rb +15 -8
  116. data/lib/action_dispatch/middleware/server_timing.rb +8 -6
  117. data/lib/action_dispatch/middleware/session/abstract_store.rb +7 -0
  118. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -7
  119. data/lib/action_dispatch/middleware/session/cookie_store.rb +32 -25
  120. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +9 -3
  121. data/lib/action_dispatch/middleware/show_exceptions.rb +42 -28
  122. data/lib/action_dispatch/middleware/ssl.rb +60 -45
  123. data/lib/action_dispatch/middleware/stack.rb +15 -9
  124. data/lib/action_dispatch/middleware/static.rb +40 -34
  125. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  126. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
  127. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
  129. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  130. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
  133. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  134. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  137. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  138. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +47 -38
  139. data/lib/action_dispatch/railtie.rb +12 -4
  140. data/lib/action_dispatch/request/session.rb +39 -27
  141. data/lib/action_dispatch/request/utils.rb +10 -3
  142. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  143. data/lib/action_dispatch/routing/inspector.rb +59 -9
  144. data/lib/action_dispatch/routing/mapper.rb +686 -639
  145. data/lib/action_dispatch/routing/polymorphic_routes.rb +70 -61
  146. data/lib/action_dispatch/routing/redirection.rb +52 -38
  147. data/lib/action_dispatch/routing/route_set.rb +106 -62
  148. data/lib/action_dispatch/routing/routes_proxy.rb +16 -19
  149. data/lib/action_dispatch/routing/url_for.rb +131 -122
  150. data/lib/action_dispatch/routing.rb +152 -150
  151. data/lib/action_dispatch/system_test_case.rb +91 -81
  152. data/lib/action_dispatch/system_testing/browser.rb +27 -19
  153. data/lib/action_dispatch/system_testing/driver.rb +16 -22
  154. data/lib/action_dispatch/system_testing/server.rb +2 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +53 -31
  156. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +9 -7
  158. data/lib/action_dispatch/testing/assertions/response.rb +36 -26
  159. data/lib/action_dispatch/testing/assertions/routing.rb +203 -95
  160. data/lib/action_dispatch/testing/assertions.rb +5 -1
  161. data/lib/action_dispatch/testing/integration.rb +240 -229
  162. data/lib/action_dispatch/testing/request_encoder.rb +6 -1
  163. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  164. data/lib/action_dispatch/testing/test_process.rb +14 -9
  165. data/lib/action_dispatch/testing/test_request.rb +4 -2
  166. data/lib/action_dispatch/testing/test_response.rb +34 -19
  167. data/lib/action_dispatch.rb +52 -21
  168. data/lib/action_pack/gem_version.rb +6 -4
  169. data/lib/action_pack/version.rb +3 -1
  170. data/lib/action_pack.rb +18 -17
  171. metadata +86 -27
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "active_support/core_ext/hash/indifferent_access"
4
6
  require "active_support/core_ext/array/wrap"
5
7
  require "active_support/core_ext/string/filters"
6
8
  require "active_support/core_ext/object/to_query"
9
+ require "active_support/deep_mergeable"
7
10
  require "action_dispatch/http/upload"
8
11
  require "rack/test"
9
12
  require "stringio"
@@ -13,11 +16,11 @@ require "yaml"
13
16
  module ActionController
14
17
  # Raised when a required parameter is missing.
15
18
  #
16
- # params = ActionController::Parameters.new(a: {})
17
- # params.fetch(:b)
18
- # # => ActionController::ParameterMissing: param is missing or the value is empty: b
19
- # params.require(:a)
20
- # # => ActionController::ParameterMissing: param is missing or the value is empty: a
19
+ # params = ActionController::Parameters.new(a: {})
20
+ # params.fetch(:b)
21
+ # # => ActionController::ParameterMissing: param is missing or the value is empty: b
22
+ # params.require(:a)
23
+ # # => ActionController::ParameterMissing: param is missing or the value is empty: a
21
24
  class ParameterMissing < KeyError
22
25
  attr_reader :param, :keys # :nodoc:
23
26
 
@@ -37,12 +40,12 @@ module ActionController
37
40
  end
38
41
 
39
42
  # Raised when a supplied parameter is not expected and
40
- # ActionController::Parameters.action_on_unpermitted_parameters
41
- # is set to <tt>:raise</tt>.
43
+ # ActionController::Parameters.action_on_unpermitted_parameters is set to
44
+ # `:raise`.
42
45
  #
43
- # params = ActionController::Parameters.new(a: "123", b: "456")
44
- # params.permit(:c)
45
- # # => ActionController::UnpermittedParameters: found unpermitted parameters: :a, :b
46
+ # params = ActionController::Parameters.new(a: "123", b: "456")
47
+ # params.permit(:c)
48
+ # # => ActionController::UnpermittedParameters: found unpermitted parameters: :a, :b
46
49
  class UnpermittedParameters < IndexError
47
50
  attr_reader :params # :nodoc:
48
51
 
@@ -52,19 +55,26 @@ module ActionController
52
55
  end
53
56
  end
54
57
 
55
- # Raised when a Parameters instance is not marked as permitted and
56
- # an operation to transform it to hash is called.
58
+ # Raised when a Parameters instance is not marked as permitted and an operation
59
+ # to transform it to hash is called.
57
60
  #
58
- # params = ActionController::Parameters.new(a: "123", b: "456")
59
- # params.to_h
60
- # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
61
+ # params = ActionController::Parameters.new(a: "123", b: "456")
62
+ # params.to_h
63
+ # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
61
64
  class UnfilteredParameters < ArgumentError
62
65
  def initialize # :nodoc:
63
66
  super("unable to convert unpermitted parameters to hash")
64
67
  end
65
68
  end
66
69
 
67
- # == Action Controller \Parameters
70
+ # Raised when initializing Parameters with keys that aren't strings or symbols.
71
+ #
72
+ # ActionController::Parameters.new(123 => 456)
73
+ # # => ActionController::InvalidParameterKey: all keys must be Strings or Symbols, got: Integer
74
+ class InvalidParameterKey < ArgumentError
75
+ end
76
+
77
+ # # Action Controller Parameters
68
78
  #
69
79
  # Allows you to choose which attributes should be permitted for mass updating
70
80
  # and thus prevent accidentally exposing that which shouldn't be exposed.
@@ -72,73 +82,106 @@ module ActionController
72
82
  # used to mark parameters as required. The latter is used to set the parameter
73
83
  # as permitted and limit which attributes should be allowed for mass updating.
74
84
  #
75
- # params = ActionController::Parameters.new({
76
- # person: {
77
- # name: "Francesco",
78
- # age: 22,
79
- # role: "admin"
80
- # }
81
- # })
85
+ # params = ActionController::Parameters.new({
86
+ # person: {
87
+ # name: "Francesco",
88
+ # age: 22,
89
+ # role: "admin"
90
+ # }
91
+ # })
82
92
  #
83
- # permitted = params.require(:person).permit(:name, :age)
84
- # permitted # => #<ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true>
85
- # permitted.permitted? # => true
93
+ # permitted = params.require(:person).permit(:name, :age)
94
+ # permitted # => #<ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true>
95
+ # permitted.permitted? # => true
86
96
  #
87
- # Person.first.update!(permitted)
88
- # # => #<Person id: 1, name: "Francesco", age: 22, role: "user">
97
+ # Person.first.update!(permitted)
98
+ # # => #<Person id: 1, name: "Francesco", age: 22, role: "user">
89
99
  #
90
100
  # It provides two options that controls the top-level behavior of new instances:
91
101
  #
92
- # * +permit_all_parameters+ - If it's +true+, all the parameters will be
93
- # permitted by default. The default is +false+.
94
- # * +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:
97
- # * +false+ to take no action.
98
- # * <tt>:log</tt> to emit an <tt>ActiveSupport::Notifications.instrument</tt> event on the
99
- # <tt>unpermitted_parameters.action_controller</tt> topic and log at the DEBUG level.
100
- # * <tt>:raise</tt> to raise an ActionController::UnpermittedParameters exception.
102
+ # * `permit_all_parameters` - If it's `true`, all the parameters will be
103
+ # permitted by default. The default is `false`.
104
+ # * `action_on_unpermitted_parameters` - Controls behavior when parameters
105
+ # that are not explicitly permitted are found. The default value is `:log`
106
+ # in test and development environments, `false` otherwise. The values can
107
+ # be:
108
+ # * `false` to take no action.
109
+ # * `:log` to emit an `ActiveSupport::Notifications.instrument` event on
110
+ # the `unpermitted_parameters.action_controller` topic and log at the
111
+ # DEBUG level.
112
+ # * `:raise` to raise an ActionController::UnpermittedParameters
113
+ # exception.
114
+ #
115
+ #
101
116
  #
102
117
  # Examples:
103
118
  #
104
- # params = ActionController::Parameters.new
105
- # params.permitted? # => false
119
+ # params = ActionController::Parameters.new
120
+ # params.permitted? # => false
106
121
  #
107
- # ActionController::Parameters.permit_all_parameters = true
122
+ # ActionController::Parameters.permit_all_parameters = true
108
123
  #
109
- # params = ActionController::Parameters.new
110
- # params.permitted? # => true
124
+ # params = ActionController::Parameters.new
125
+ # params.permitted? # => true
111
126
  #
112
- # params = ActionController::Parameters.new(a: "123", b: "456")
113
- # params.permit(:c)
114
- # # => #<ActionController::Parameters {} permitted: true>
127
+ # params = ActionController::Parameters.new(a: "123", b: "456")
128
+ # params.permit(:c)
129
+ # # => #<ActionController::Parameters {} permitted: true>
115
130
  #
116
- # ActionController::Parameters.action_on_unpermitted_parameters = :raise
131
+ # ActionController::Parameters.action_on_unpermitted_parameters = :raise
117
132
  #
118
- # params = ActionController::Parameters.new(a: "123", b: "456")
119
- # params.permit(:c)
120
- # # => ActionController::UnpermittedParameters: found unpermitted keys: a, b
133
+ # params = ActionController::Parameters.new(a: "123", b: "456")
134
+ # params.permit(:c)
135
+ # # => ActionController::UnpermittedParameters: found unpermitted keys: a, b
121
136
  #
122
137
  # Please note that these options *are not thread-safe*. In a multi-threaded
123
138
  # environment they should only be set once at boot-time and never mutated at
124
139
  # runtime.
125
140
  #
126
- # You can fetch values of <tt>ActionController::Parameters</tt> using either
127
- # <tt>:key</tt> or <tt>"key"</tt>.
141
+ # You can fetch values of `ActionController::Parameters` using either `:key` or
142
+ # `"key"`.
128
143
  #
129
- # params = ActionController::Parameters.new(key: "value")
130
- # params[:key] # => "value"
131
- # params["key"] # => "value"
144
+ # params = ActionController::Parameters.new(key: "value")
145
+ # params[:key] # => "value"
146
+ # params["key"] # => "value"
132
147
  class Parameters
148
+ include ActiveSupport::DeepMergeable
149
+
133
150
  cattr_accessor :permit_all_parameters, instance_accessor: false, default: false
134
151
 
135
152
  cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
136
153
 
154
+ ##
155
+ # :method: deep_merge
156
+ #
157
+ # :call-seq:
158
+ # deep_merge(other_hash, &block)
159
+ #
160
+ # Returns a new `ActionController::Parameters` instance with `self` and
161
+ # `other_hash` merged recursively.
162
+ #
163
+ # Like with `Hash#merge` in the standard library, a block can be provided to
164
+ # merge values.
165
+ #
166
+ #--
167
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge.
168
+
169
+ ##
170
+ # :method: deep_merge!
171
+ #
172
+ # :call-seq:
173
+ # deep_merge!(other_hash, &block)
174
+ #
175
+ # Same as `#deep_merge`, but modifies `self`.
176
+ #
177
+ #--
178
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge!.
179
+
137
180
  ##
138
181
  # :method: as_json
139
182
  #
140
183
  # :call-seq:
141
- # as_json(options=nil)
184
+ # as_json(options=nil)
142
185
  #
143
186
  # Returns a hash that can be used as the JSON representation for the parameters.
144
187
 
@@ -146,32 +189,32 @@ module ActionController
146
189
  # :method: each_key
147
190
  #
148
191
  # :call-seq:
149
- # each_key(&block)
192
+ # each_key(&block)
150
193
  #
151
- # Calls block once for each key in the parameters, passing the key.
152
- # If no block is given, an enumerator is returned instead.
194
+ # Calls block once for each key in the parameters, passing the key. If no block
195
+ # is given, an enumerator is returned instead.
153
196
 
154
197
  ##
155
198
  # :method: empty?
156
199
  #
157
200
  # :call-seq:
158
- # empty?()
201
+ # empty?()
159
202
  #
160
203
  # Returns true if the parameters have no key/value pairs.
161
204
 
162
205
  ##
163
- # :method: has_value?
206
+ # :method: exclude?
164
207
  #
165
208
  # :call-seq:
166
- # has_value?(value)
209
+ # exclude?(key)
167
210
  #
168
- # Returns true if the given value is present for some key in the parameters.
211
+ # Returns true if the given key is not present in the parameters.
169
212
 
170
213
  ##
171
214
  # :method: include?
172
215
  #
173
216
  # :call-seq:
174
- # include?(key)
217
+ # include?(key)
175
218
  #
176
219
  # Returns true if the given key is present in the parameters.
177
220
 
@@ -179,7 +222,7 @@ module ActionController
179
222
  # :method: keys
180
223
  #
181
224
  # :call-seq:
182
- # keys()
225
+ # keys()
183
226
  #
184
227
  # Returns a new array of the keys of the parameters.
185
228
 
@@ -187,76 +230,80 @@ module ActionController
187
230
  # :method: to_s
188
231
  #
189
232
  # :call-seq:
190
- # to_s()
233
+ # to_s()
191
234
  #
192
235
  # Returns the content of the parameters as a string.
193
236
 
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?,
237
+ delegate :keys, :empty?, :exclude?, :include?,
210
238
  :as_json, :to_s, :each_key, to: :@parameters
211
239
 
212
240
  alias_method :has_key?, :include?
213
241
  alias_method :key?, :include?
214
242
  alias_method :member?, :include?
215
243
 
216
- # By default, never raise an UnpermittedParameters exception if these
217
- # params are present. The default includes both 'controller' and 'action'
218
- # because they are added by Rails and should be of no concern. One way
219
- # to change these is to specify +always_permitted_parameters+ in your
220
- # config. For instance:
244
+ # By default, never raise an UnpermittedParameters exception if these params are
245
+ # present. The default includes both 'controller' and 'action' because they are
246
+ # added by Rails and should be of no concern. One way to change these is to
247
+ # specify `always_permitted_parameters` in your config. For instance:
221
248
  #
222
- # config.action_controller.always_permitted_parameters = %w( controller action format )
249
+ # config.action_controller.always_permitted_parameters = %w( controller action format )
223
250
  cattr_accessor :always_permitted_parameters, default: %w( controller action )
224
251
 
225
252
  class << self
253
+ def allow_deprecated_parameters_hash_equality
254
+ ActionController.deprecator.warn <<-WARNING.squish
255
+ `Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality` is
256
+ deprecated and will be removed in Rails 8.0.
257
+ WARNING
258
+ end
259
+
260
+ def allow_deprecated_parameters_hash_equality=(value)
261
+ ActionController.deprecator.warn <<-WARNING.squish
262
+ `Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality`
263
+ is deprecated and will be removed in Rails 8.0.
264
+ WARNING
265
+ end
266
+
226
267
  def nested_attribute?(key, value) # :nodoc:
227
268
  /\A-?\d+\z/.match?(key) && (value.is_a?(Hash) || value.is_a?(Parameters))
228
269
  end
229
270
  end
230
271
 
231
- # Returns a new <tt>ActionController::Parameters</tt> instance.
232
- # Also, sets the +permitted+ attribute to the default value of
233
- # <tt>ActionController::Parameters.permit_all_parameters</tt>.
272
+ # Returns a new `ActionController::Parameters` instance. Also, sets the
273
+ # `permitted` attribute to the default value of
274
+ # `ActionController::Parameters.permit_all_parameters`.
234
275
  #
235
- # class Person < ActiveRecord::Base
236
- # end
276
+ # class Person < ActiveRecord::Base
277
+ # end
237
278
  #
238
- # params = ActionController::Parameters.new(name: "Francesco")
239
- # params.permitted? # => false
240
- # Person.new(params) # => ActiveModel::ForbiddenAttributesError
279
+ # params = ActionController::Parameters.new(name: "Francesco")
280
+ # params.permitted? # => false
281
+ # Person.new(params) # => ActiveModel::ForbiddenAttributesError
241
282
  #
242
- # ActionController::Parameters.permit_all_parameters = true
283
+ # ActionController::Parameters.permit_all_parameters = true
243
284
  #
244
- # params = ActionController::Parameters.new(name: "Francesco")
245
- # params.permitted? # => true
246
- # Person.new(params) # => #<Person id: nil, name: "Francesco">
285
+ # params = ActionController::Parameters.new(name: "Francesco")
286
+ # params.permitted? # => true
287
+ # Person.new(params) # => #<Person id: nil, name: "Francesco">
247
288
  def initialize(parameters = {}, logging_context = {})
289
+ parameters.each_key do |key|
290
+ unless key.is_a?(String) || key.is_a?(Symbol)
291
+ raise InvalidParameterKey, "all keys must be Strings or Symbols, got: #{key.class}"
292
+ end
293
+ end
294
+
248
295
  @parameters = parameters.with_indifferent_access
249
296
  @logging_context = logging_context
250
297
  @permitted = self.class.permit_all_parameters
251
298
  end
252
299
 
253
- # Returns true if another +Parameters+ object contains the same content and
300
+ # Returns true if another `Parameters` object contains the same content and
254
301
  # permitted flag.
255
302
  def ==(other)
256
303
  if other.respond_to?(:permitted?)
257
304
  permitted? == other.permitted? && parameters == other.parameters
258
305
  else
259
- @parameters == other
306
+ super
260
307
  end
261
308
  end
262
309
 
@@ -270,38 +317,38 @@ module ActionController
270
317
  [self.class, @parameters, @permitted].hash
271
318
  end
272
319
 
273
- # Returns a safe ActiveSupport::HashWithIndifferentAccess
274
- # representation of the parameters with all unpermitted keys removed.
320
+ # Returns a safe ActiveSupport::HashWithIndifferentAccess representation of the
321
+ # parameters with all unpermitted keys removed.
275
322
  #
276
- # params = ActionController::Parameters.new({
277
- # name: "Senjougahara Hitagi",
278
- # oddity: "Heavy stone crab"
279
- # })
280
- # params.to_h
281
- # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
323
+ # params = ActionController::Parameters.new({
324
+ # name: "Senjougahara Hitagi",
325
+ # oddity: "Heavy stone crab"
326
+ # })
327
+ # params.to_h
328
+ # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
282
329
  #
283
- # safe_params = params.permit(:name)
284
- # safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
285
- def to_h
330
+ # safe_params = params.permit(:name)
331
+ # safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
332
+ def to_h(&block)
286
333
  if permitted?
287
- convert_parameters_to_hashes(@parameters, :to_h)
334
+ convert_parameters_to_hashes(@parameters, :to_h, &block)
288
335
  else
289
336
  raise UnfilteredParameters
290
337
  end
291
338
  end
292
339
 
293
- # Returns a safe <tt>Hash</tt> representation of the parameters
294
- # with all unpermitted keys removed.
340
+ # Returns a safe `Hash` representation of the parameters with all unpermitted
341
+ # keys removed.
295
342
  #
296
- # params = ActionController::Parameters.new({
297
- # name: "Senjougahara Hitagi",
298
- # oddity: "Heavy stone crab"
299
- # })
300
- # params.to_hash
301
- # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
343
+ # params = ActionController::Parameters.new({
344
+ # name: "Senjougahara Hitagi",
345
+ # oddity: "Heavy stone crab"
346
+ # })
347
+ # params.to_hash
348
+ # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
302
349
  #
303
- # safe_params = params.permit(:name)
304
- # safe_params.to_hash # => {"name"=>"Senjougahara Hitagi"}
350
+ # safe_params = params.permit(:name)
351
+ # safe_params.to_hash # => {"name"=>"Senjougahara Hitagi"}
305
352
  def to_hash
306
353
  to_h.to_hash
307
354
  end
@@ -309,29 +356,29 @@ module ActionController
309
356
  # Returns a string representation of the receiver suitable for use as a URL
310
357
  # query string:
311
358
  #
312
- # params = ActionController::Parameters.new({
313
- # name: "David",
314
- # nationality: "Danish"
315
- # })
316
- # params.to_query
317
- # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
359
+ # params = ActionController::Parameters.new({
360
+ # name: "David",
361
+ # nationality: "Danish"
362
+ # })
363
+ # params.to_query
364
+ # # => ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
318
365
  #
319
- # safe_params = params.permit(:name, :nationality)
320
- # safe_params.to_query
321
- # # => "name=David&nationality=Danish"
366
+ # safe_params = params.permit(:name, :nationality)
367
+ # safe_params.to_query
368
+ # # => "name=David&nationality=Danish"
322
369
  #
323
370
  # An optional namespace can be passed to enclose key names:
324
371
  #
325
- # params = ActionController::Parameters.new({
326
- # name: "David",
327
- # nationality: "Danish"
328
- # })
329
- # safe_params = params.permit(:name, :nationality)
330
- # safe_params.to_query("user")
331
- # # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
372
+ # params = ActionController::Parameters.new({
373
+ # name: "David",
374
+ # nationality: "Danish"
375
+ # })
376
+ # safe_params = params.permit(:name, :nationality)
377
+ # safe_params.to_query("user")
378
+ # # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
332
379
  #
333
- # The string pairs <tt>"key=value"</tt> that conform the query string
334
- # are sorted lexicographically in ascending order.
380
+ # The string pairs `"key=value"` that conform the query string are sorted
381
+ # lexicographically in ascending order.
335
382
  def to_query(*args)
336
383
  to_h.to_query(*args)
337
384
  end
@@ -340,19 +387,19 @@ module ActionController
340
387
  # Returns an unsafe, unfiltered ActiveSupport::HashWithIndifferentAccess
341
388
  # representation of the parameters.
342
389
  #
343
- # params = ActionController::Parameters.new({
344
- # name: "Senjougahara Hitagi",
345
- # oddity: "Heavy stone crab"
346
- # })
347
- # params.to_unsafe_h
348
- # # => {"name"=>"Senjougahara Hitagi", "oddity" => "Heavy stone crab"}
390
+ # params = ActionController::Parameters.new({
391
+ # name: "Senjougahara Hitagi",
392
+ # oddity: "Heavy stone crab"
393
+ # })
394
+ # params.to_unsafe_h
395
+ # # => {"name"=>"Senjougahara Hitagi", "oddity" => "Heavy stone crab"}
349
396
  def to_unsafe_h
350
397
  convert_parameters_to_hashes(@parameters, :to_unsafe_h)
351
398
  end
352
399
  alias_method :to_unsafe_hash, :to_unsafe_h
353
400
 
354
- # Convert all hashes in values into parameters, then yield each pair in
355
- # the same way as <tt>Hash#each_pair</tt>.
401
+ # Convert all hashes in values into parameters, then yield each pair in the same
402
+ # way as `Hash#each_pair`.
356
403
  def each_pair(&block)
357
404
  return to_enum(__callee__) unless block_given?
358
405
  @parameters.each_pair do |key, value|
@@ -363,8 +410,8 @@ module ActionController
363
410
  end
364
411
  alias_method :each, :each_pair
365
412
 
366
- # Convert all hashes in values into parameters, then yield each value in
367
- # the same way as <tt>Hash#each_value</tt>.
413
+ # Convert all hashes in values into parameters, then yield each value in the
414
+ # same way as `Hash#each_value`.
368
415
  def each_value(&block)
369
416
  return to_enum(:each_value) unless block_given?
370
417
  @parameters.each_pair do |key, value|
@@ -374,39 +421,44 @@ module ActionController
374
421
  self
375
422
  end
376
423
 
424
+ # Returns a new array of the values of the parameters.
425
+ def values
426
+ to_enum(:each_value).to_a
427
+ end
428
+
377
429
  # Attribute that keeps track of converted arrays, if any, to avoid double
378
- # looping in the common use case permit + mass-assignment. Defined in a
379
- # method to instantiate it only if needed.
430
+ # looping in the common use case permit + mass-assignment. Defined in a method
431
+ # to instantiate it only if needed.
380
432
  #
381
- # \Testing membership still loops, but it's going to be faster than our own
382
- # loop that converts values. Also, we are not going to build a new array
383
- # object per fetch.
433
+ # Testing membership still loops, but it's going to be faster than our own loop
434
+ # that converts values. Also, we are not going to build a new array object per
435
+ # fetch.
384
436
  def converted_arrays
385
437
  @converted_arrays ||= Set.new
386
438
  end
387
439
 
388
- # Returns +true+ if the parameter is permitted, +false+ otherwise.
440
+ # Returns `true` if the parameter is permitted, `false` otherwise.
389
441
  #
390
- # params = ActionController::Parameters.new
391
- # params.permitted? # => false
392
- # params.permit!
393
- # params.permitted? # => true
442
+ # params = ActionController::Parameters.new
443
+ # params.permitted? # => false
444
+ # params.permit!
445
+ # params.permitted? # => true
394
446
  def permitted?
395
447
  @permitted
396
448
  end
397
449
 
398
- # Sets the +permitted+ attribute to +true+. This can be used to pass
399
- # mass assignment. Returns +self+.
450
+ # Sets the `permitted` attribute to `true`. This can be used to pass mass
451
+ # assignment. Returns `self`.
400
452
  #
401
- # class Person < ActiveRecord::Base
402
- # end
453
+ # class Person < ActiveRecord::Base
454
+ # end
403
455
  #
404
- # params = ActionController::Parameters.new(name: "Francesco")
405
- # params.permitted? # => false
406
- # Person.new(params) # => ActiveModel::ForbiddenAttributesError
407
- # params.permit!
408
- # params.permitted? # => true
409
- # Person.new(params) # => #<Person id: nil, name: "Francesco">
456
+ # params = ActionController::Parameters.new(name: "Francesco")
457
+ # params.permitted? # => false
458
+ # Person.new(params) # => ActiveModel::ForbiddenAttributesError
459
+ # params.permit!
460
+ # params.permitted? # => true
461
+ # Person.new(params) # => #<Person id: nil, name: "Francesco">
410
462
  def permit!
411
463
  each_pair do |key, value|
412
464
  Array.wrap(value).flatten.each do |v|
@@ -420,52 +472,51 @@ module ActionController
420
472
 
421
473
  # This method accepts both a single key and an array of keys.
422
474
  #
423
- # When passed a single key, if it exists and its associated value is
424
- # either present or the singleton +false+, returns said value:
475
+ # When passed a single key, if it exists and its associated value is either
476
+ # present or the singleton `false`, returns said value:
425
477
  #
426
- # ActionController::Parameters.new(person: { name: "Francesco" }).require(:person)
427
- # # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
478
+ # ActionController::Parameters.new(person: { name: "Francesco" }).require(:person)
479
+ # # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
428
480
  #
429
481
  # Otherwise raises ActionController::ParameterMissing:
430
482
  #
431
- # ActionController::Parameters.new.require(:person)
432
- # # ActionController::ParameterMissing: param is missing or the value is empty: person
483
+ # ActionController::Parameters.new.require(:person)
484
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
433
485
  #
434
- # ActionController::Parameters.new(person: nil).require(:person)
435
- # # ActionController::ParameterMissing: param is missing or the value is empty: person
486
+ # ActionController::Parameters.new(person: nil).require(:person)
487
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
436
488
  #
437
- # ActionController::Parameters.new(person: "\t").require(:person)
438
- # # ActionController::ParameterMissing: param is missing or the value is empty: person
489
+ # ActionController::Parameters.new(person: "\t").require(:person)
490
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
439
491
  #
440
- # ActionController::Parameters.new(person: {}).require(:person)
441
- # # ActionController::ParameterMissing: param is missing or the value is empty: person
492
+ # ActionController::Parameters.new(person: {}).require(:person)
493
+ # # ActionController::ParameterMissing: param is missing or the value is empty: person
442
494
  #
443
- # When given an array of keys, the method tries to require each one of them
444
- # in order. If it succeeds, an array with the respective return values is
445
- # returned:
495
+ # When given an array of keys, the method tries to require each one of them in
496
+ # order. If it succeeds, an array with the respective return values is returned:
446
497
  #
447
- # params = ActionController::Parameters.new(user: { ... }, profile: { ... })
448
- # user_params, profile_params = params.require([:user, :profile])
498
+ # params = ActionController::Parameters.new(user: { ... }, profile: { ... })
499
+ # user_params, profile_params = params.require([:user, :profile])
449
500
  #
450
501
  # Otherwise, the method re-raises the first exception found:
451
502
  #
452
- # params = ActionController::Parameters.new(user: {}, profile: {})
453
- # user_params, profile_params = params.require([:user, :profile])
454
- # # ActionController::ParameterMissing: param is missing or the value is empty: user
503
+ # params = ActionController::Parameters.new(user: {}, profile: {})
504
+ # user_params, profile_params = params.require([:user, :profile])
505
+ # # ActionController::ParameterMissing: param is missing or the value is empty: user
455
506
  #
456
507
  # Technically this method can be used to fetch terminal values:
457
508
  #
458
- # # CAREFUL
459
- # params = ActionController::Parameters.new(person: { name: "Finn" })
460
- # name = params.require(:person).require(:name) # CAREFUL
509
+ # # CAREFUL
510
+ # params = ActionController::Parameters.new(person: { name: "Finn" })
511
+ # name = params.require(:person).require(:name) # CAREFUL
461
512
  #
462
513
  # but take into account that at some point those ones have to be permitted:
463
514
  #
464
- # def person_params
465
- # params.require(:person).permit(:name).tap do |person_params|
466
- # person_params.require(:name) # SAFER
515
+ # def person_params
516
+ # params.require(:person).permit(:name).tap do |person_params|
517
+ # person_params.require(:name) # SAFER
518
+ # end
467
519
  # end
468
- # end
469
520
  #
470
521
  # for example.
471
522
  def require(key)
@@ -480,119 +531,120 @@ module ActionController
480
531
 
481
532
  alias :required :require
482
533
 
483
- # Returns a new <tt>ActionController::Parameters</tt> instance that
484
- # includes only the given +filters+ and sets the +permitted+ attribute
485
- # for the object to +true+. This is useful for limiting which attributes
486
- # should be allowed for mass updating.
534
+ # Returns a new `ActionController::Parameters` instance that includes only the
535
+ # given `filters` and sets the `permitted` attribute for the object to `true`.
536
+ # This is useful for limiting which attributes should be allowed for mass
537
+ # updating.
487
538
  #
488
- # params = ActionController::Parameters.new(user: { name: "Francesco", age: 22, role: "admin" })
489
- # permitted = params.require(:user).permit(:name, :age)
490
- # permitted.permitted? # => true
491
- # permitted.has_key?(:name) # => true
492
- # permitted.has_key?(:age) # => true
493
- # permitted.has_key?(:role) # => false
539
+ # params = ActionController::Parameters.new(user: { name: "Francesco", age: 22, role: "admin" })
540
+ # permitted = params.require(:user).permit(:name, :age)
541
+ # permitted.permitted? # => true
542
+ # permitted.has_key?(:name) # => true
543
+ # permitted.has_key?(:age) # => true
544
+ # permitted.has_key?(:role) # => false
494
545
  #
495
546
  # Only permitted scalars pass the filter. For example, given
496
547
  #
497
- # params.permit(:name)
498
- #
499
- # +:name+ passes if it is a key of +params+ whose associated value is of type
500
- # +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+,
501
- # +Date+, +Time+, +DateTime+, +StringIO+, +IO+,
502
- # ActionDispatch::Http::UploadedFile or +Rack::Test::UploadedFile+.
503
- # Otherwise, the key +:name+ is filtered out.
504
- #
505
- # You may declare that the parameter should be an array of permitted scalars
506
- # by mapping it to an empty array:
507
- #
508
- # params = ActionController::Parameters.new(tags: ["rails", "parameters"])
509
- # params.permit(tags: [])
510
- #
511
- # Sometimes it is not possible or convenient to declare the valid keys of
512
- # a hash parameter or its internal structure. Just map to an empty hash:
513
- #
514
- # params.permit(preferences: {})
515
- #
516
- # Be careful because this opens the door to arbitrary input. In this
517
- # case, +permit+ ensures values in the returned structure are permitted
518
- # scalars and filters out anything else.
519
- #
520
- # You can also use +permit+ on nested parameters, like:
521
- #
522
- # params = ActionController::Parameters.new({
523
- # person: {
524
- # name: "Francesco",
525
- # age: 22,
526
- # pets: [{
527
- # name: "Purplish",
528
- # category: "dogs"
529
- # }]
530
- # }
531
- # })
532
- #
533
- # permitted = params.permit(person: [ :name, { pets: :name } ])
534
- # permitted.permitted? # => true
535
- # permitted[:person][:name] # => "Francesco"
536
- # permitted[:person][:age] # => nil
537
- # permitted[:person][:pets][0][:name] # => "Purplish"
538
- # permitted[:person][:pets][0][:category] # => nil
539
- #
540
- # Note that if you use +permit+ in a key that points to a hash,
541
- # it won't allow all the hash. You also need to specify which
542
- # attributes inside the hash should be permitted.
543
- #
544
- # params = ActionController::Parameters.new({
545
- # person: {
546
- # contact: {
547
- # email: "none@test.com",
548
- # phone: "555-1234"
548
+ # params.permit(:name)
549
+ #
550
+ # `:name` passes if it is a key of `params` whose associated value is of type
551
+ # `String`, `Symbol`, `NilClass`, `Numeric`, `TrueClass`, `FalseClass`, `Date`,
552
+ # `Time`, `DateTime`, `StringIO`, `IO`, ActionDispatch::Http::UploadedFile or
553
+ # `Rack::Test::UploadedFile`. Otherwise, the key `:name` is filtered out.
554
+ #
555
+ # You may declare that the parameter should be an array of permitted scalars by
556
+ # mapping it to an empty array:
557
+ #
558
+ # params = ActionController::Parameters.new(tags: ["rails", "parameters"])
559
+ # params.permit(tags: [])
560
+ #
561
+ # Sometimes it is not possible or convenient to declare the valid keys of a hash
562
+ # parameter or its internal structure. Just map to an empty hash:
563
+ #
564
+ # params.permit(preferences: {})
565
+ #
566
+ # Be careful because this opens the door to arbitrary input. In this case,
567
+ # `permit` ensures values in the returned structure are permitted scalars and
568
+ # filters out anything else.
569
+ #
570
+ # You can also use `permit` on nested parameters, like:
571
+ #
572
+ # params = ActionController::Parameters.new({
573
+ # person: {
574
+ # name: "Francesco",
575
+ # age: 22,
576
+ # pets: [{
577
+ # name: "Purplish",
578
+ # category: "dogs"
579
+ # }]
580
+ # }
581
+ # })
582
+ #
583
+ # permitted = params.permit(person: [ :name, { pets: :name } ])
584
+ # permitted.permitted? # => true
585
+ # permitted[:person][:name] # => "Francesco"
586
+ # permitted[:person][:age] # => nil
587
+ # permitted[:person][:pets][0][:name] # => "Purplish"
588
+ # permitted[:person][:pets][0][:category] # => nil
589
+ #
590
+ # Note that if you use `permit` in a key that points to a hash, it won't allow
591
+ # all the hash. You also need to specify which attributes inside the hash should
592
+ # be permitted.
593
+ #
594
+ # params = ActionController::Parameters.new({
595
+ # person: {
596
+ # contact: {
597
+ # email: "none@test.com",
598
+ # phone: "555-1234"
599
+ # }
600
+ # }
601
+ # })
602
+ #
603
+ # params.require(:person).permit(:contact)
604
+ # # => #<ActionController::Parameters {} permitted: true>
605
+ #
606
+ # params.require(:person).permit(contact: :phone)
607
+ # # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true>
608
+ #
609
+ # params.require(:person).permit(contact: [ :email, :phone ])
610
+ # # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"email"=>"none@test.com", "phone"=>"555-1234"} permitted: true>} permitted: true>
611
+ #
612
+ # If your parameters specify multiple parameters indexed by a number, you can
613
+ # permit each set of parameters under the numeric key to be the same using the
614
+ # same syntax as permitting a single item.
615
+ #
616
+ # params = ActionController::Parameters.new({
617
+ # person: {
618
+ # '0': {
619
+ # email: "none@test.com",
620
+ # phone: "555-1234"
621
+ # },
622
+ # '1': {
623
+ # email: "nothing@test.com",
624
+ # phone: "555-6789"
625
+ # },
626
+ # }
627
+ # })
628
+ # params.permit(person: [:email]).to_h
629
+ # # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"email"=>"nothing@test.com"}}}
630
+ #
631
+ # If you want to specify what keys you want from each numeric key, you can
632
+ # instead specify each one individually
633
+ #
634
+ # params = ActionController::Parameters.new({
635
+ # person: {
636
+ # '0': {
637
+ # email: "none@test.com",
638
+ # phone: "555-1234"
639
+ # },
640
+ # '1': {
641
+ # email: "nothing@test.com",
642
+ # phone: "555-6789"
643
+ # },
549
644
  # }
550
- # }
551
- # })
552
- #
553
- # params.require(:person).permit(:contact)
554
- # # => #<ActionController::Parameters {} permitted: true>
555
- #
556
- # params.require(:person).permit(contact: :phone)
557
- # # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true>
558
- #
559
- # params.require(:person).permit(contact: [ :email, :phone ])
560
- # # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"email"=>"none@test.com", "phone"=>"555-1234"} permitted: true>} permitted: true>
561
- #
562
- # If your parameters specify multiple parameters indexed by a number,
563
- # you can permit each set of parameters under the numeric key to be the same using the same syntax as permitting a single item.
564
- #
565
- # params = ActionController::Parameters.new({
566
- # person: {
567
- # '0': {
568
- # email: "none@test.com",
569
- # phone: "555-1234"
570
- # },
571
- # '1': {
572
- # email: "nothing@test.com",
573
- # phone: "555-6789"
574
- # },
575
- # }
576
- # })
577
- # params.permit(person: [:email]).to_h
578
- # # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"email"=>"nothing@test.com"}}}
579
- #
580
- # If you want to specify what keys you want from each numeric key, you can instead specify each one individually
581
- #
582
- # params = ActionController::Parameters.new({
583
- # person: {
584
- # '0': {
585
- # email: "none@test.com",
586
- # phone: "555-1234"
587
- # },
588
- # '1': {
589
- # email: "nothing@test.com",
590
- # phone: "555-6789"
591
- # },
592
- # }
593
- # })
594
- # params.permit(person: { '0': [:email], '1': [:phone]}).to_h
595
- # # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"phone"=>"555-6789"}}}
645
+ # })
646
+ # params.permit(person: { '0': [:email], '1': [:phone]}).to_h
647
+ # # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"phone"=>"555-6789"}}}
596
648
  def permit(*filters)
597
649
  params = self.class.new
598
650
 
@@ -610,35 +662,34 @@ module ActionController
610
662
  params.permit!
611
663
  end
612
664
 
613
- # Returns a parameter for the given +key+. If not found,
614
- # returns +nil+.
665
+ # Returns a parameter for the given `key`. If not found, returns `nil`.
615
666
  #
616
- # params = ActionController::Parameters.new(person: { name: "Francesco" })
617
- # params[:person] # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
618
- # params[:none] # => nil
667
+ # params = ActionController::Parameters.new(person: { name: "Francesco" })
668
+ # params[:person] # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
669
+ # params[:none] # => nil
619
670
  def [](key)
620
671
  convert_hashes_to_parameters(key, @parameters[key])
621
672
  end
622
673
 
623
- # Assigns a value to a given +key+. The given key may still get filtered out
674
+ # Assigns a value to a given `key`. The given key may still get filtered out
624
675
  # when #permit is called.
625
676
  def []=(key, value)
626
677
  @parameters[key] = value
627
678
  end
628
679
 
629
- # Returns a parameter for the given +key+. If the +key+
630
- # can't be found, there are several options: With no other arguments,
631
- # it will raise an ActionController::ParameterMissing error;
632
- # if a second argument is given, then that is returned (converted to an
633
- # instance of +ActionController::Parameters+ if possible); if a block
634
- # is given, then that will be run and its result returned.
635
- #
636
- # params = ActionController::Parameters.new(person: { name: "Francesco" })
637
- # params.fetch(:person) # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
638
- # params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
639
- # params.fetch(:none, {}) # => #<ActionController::Parameters {} permitted: false>
640
- # params.fetch(:none, "Francesco") # => "Francesco"
641
- # params.fetch(:none) { "Francesco" } # => "Francesco"
680
+ # Returns a parameter for the given `key`. If the `key` can't be found, there
681
+ # are several options: With no other arguments, it will raise an
682
+ # ActionController::ParameterMissing error; if a second argument is given, then
683
+ # that is returned (converted to an instance of `ActionController::Parameters`
684
+ # if possible); if a block is given, then that will be run and its result
685
+ # returned.
686
+ #
687
+ # params = ActionController::Parameters.new(person: { name: "Francesco" })
688
+ # params.fetch(:person) # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
689
+ # params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
690
+ # params.fetch(:none, {}) # => #<ActionController::Parameters {} permitted: false>
691
+ # params.fetch(:none, "Francesco") # => "Francesco"
692
+ # params.fetch(:none) { "Francesco" } # => "Francesco"
642
693
  def fetch(key, *args)
643
694
  convert_value_to_parameters(
644
695
  @parameters.fetch(key) {
@@ -651,63 +702,63 @@ module ActionController
651
702
  )
652
703
  end
653
704
 
654
- # Extracts the nested parameter from the given +keys+ by calling +dig+
655
- # at each step. Returns +nil+ if any intermediate step is +nil+.
705
+ # Extracts the nested parameter from the given `keys` by calling `dig` at each
706
+ # step. Returns `nil` if any intermediate step is `nil`.
656
707
  #
657
- # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
658
- # params.dig(:foo, :bar, :baz) # => 1
659
- # params.dig(:foo, :zot, :xyz) # => nil
708
+ # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
709
+ # params.dig(:foo, :bar, :baz) # => 1
710
+ # params.dig(:foo, :zot, :xyz) # => nil
660
711
  #
661
- # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
662
- # params2.dig(:foo, 1) # => 11
712
+ # params2 = ActionController::Parameters.new(foo: [10, 11, 12])
713
+ # params2.dig(:foo, 1) # => 11
663
714
  def dig(*keys)
664
715
  convert_hashes_to_parameters(keys.first, @parameters[keys.first])
665
716
  @parameters.dig(*keys)
666
717
  end
667
718
 
668
- # Returns a new <tt>ActionController::Parameters</tt> instance that
669
- # includes only the given +keys+. If the given +keys+
670
- # don't exist, returns an empty hash.
719
+ # Returns a new `ActionController::Parameters` instance that includes only the
720
+ # given `keys`. If the given `keys` don't exist, returns an empty hash.
671
721
  #
672
- # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
673
- # params.slice(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
674
- # params.slice(:d) # => #<ActionController::Parameters {} permitted: false>
722
+ # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
723
+ # params.slice(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
724
+ # params.slice(:d) # => #<ActionController::Parameters {} permitted: false>
675
725
  def slice(*keys)
676
726
  new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
677
727
  end
678
728
 
679
- # Returns the current <tt>ActionController::Parameters</tt> instance which
680
- # contains only the given +keys+.
729
+ # Returns the current `ActionController::Parameters` instance which contains
730
+ # only the given `keys`.
681
731
  def slice!(*keys)
682
732
  @parameters.slice!(*keys)
683
733
  self
684
734
  end
685
735
 
686
- # Returns a new <tt>ActionController::Parameters</tt> instance that
687
- # filters out the given +keys+.
736
+ # Returns a new `ActionController::Parameters` instance that filters out the
737
+ # given `keys`.
688
738
  #
689
- # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
690
- # params.except(:a, :b) # => #<ActionController::Parameters {"c"=>3} permitted: false>
691
- # params.except(:d) # => #<ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false>
739
+ # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
740
+ # params.except(:a, :b) # => #<ActionController::Parameters {"c"=>3} permitted: false>
741
+ # params.except(:d) # => #<ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false>
692
742
  def except(*keys)
693
743
  new_instance_with_inherited_permitted_status(@parameters.except(*keys))
694
744
  end
745
+ alias_method :without, :except
695
746
 
696
747
  # Removes and returns the key/value pairs matching the given keys.
697
748
  #
698
- # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
699
- # params.extract!(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
700
- # params # => #<ActionController::Parameters {"c"=>3} permitted: false>
749
+ # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
750
+ # params.extract!(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
751
+ # params # => #<ActionController::Parameters {"c"=>3} permitted: false>
701
752
  def extract!(*keys)
702
753
  new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
703
754
  end
704
755
 
705
- # Returns a new <tt>ActionController::Parameters</tt> instance with the results of
706
- # running +block+ once for every value. The keys are unchanged.
756
+ # Returns a new `ActionController::Parameters` instance with the results of
757
+ # running `block` once for every value. The keys are unchanged.
707
758
  #
708
- # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
709
- # params.transform_values { |x| x * 2 }
710
- # # => #<ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
759
+ # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
760
+ # params.transform_values { |x| x * 2 }
761
+ # # => #<ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
711
762
  def transform_values
712
763
  return to_enum(:transform_values) unless block_given?
713
764
  new_instance_with_inherited_permitted_status(
@@ -716,15 +767,15 @@ module ActionController
716
767
  end
717
768
 
718
769
  # Performs values transformation and returns the altered
719
- # <tt>ActionController::Parameters</tt> instance.
770
+ # `ActionController::Parameters` instance.
720
771
  def transform_values!
721
772
  return to_enum(:transform_values!) unless block_given?
722
773
  @parameters.transform_values! { |v| yield convert_value_to_parameters(v) }
723
774
  self
724
775
  end
725
776
 
726
- # Returns a new <tt>ActionController::Parameters</tt> instance with the
727
- # results of running +block+ once for every key. The values are unchanged.
777
+ # Returns a new `ActionController::Parameters` instance with the results of
778
+ # running `block` once for every key. The values are unchanged.
728
779
  def transform_keys(&block)
729
780
  return to_enum(:transform_keys) unless block_given?
730
781
  new_instance_with_inherited_permitted_status(
@@ -733,53 +784,53 @@ module ActionController
733
784
  end
734
785
 
735
786
  # Performs keys transformation and returns the altered
736
- # <tt>ActionController::Parameters</tt> instance.
787
+ # `ActionController::Parameters` instance.
737
788
  def transform_keys!(&block)
738
789
  return to_enum(:transform_keys!) unless block_given?
739
790
  @parameters.transform_keys!(&block)
740
791
  self
741
792
  end
742
793
 
743
- # Returns a new <tt>ActionController::Parameters</tt> instance with the
744
- # results of running +block+ once for every key. This includes the keys
745
- # from the root hash and from all nested hashes and arrays. The values are unchanged.
794
+ # Returns a new `ActionController::Parameters` instance with the results of
795
+ # running `block` once for every key. This includes the keys from the root hash
796
+ # and from all nested hashes and arrays. The values are unchanged.
746
797
  def deep_transform_keys(&block)
747
798
  new_instance_with_inherited_permitted_status(
748
- @parameters.deep_transform_keys(&block)
799
+ _deep_transform_keys_in_object(@parameters, &block).to_unsafe_h
749
800
  )
750
801
  end
751
802
 
752
- # Returns the same <tt>ActionController::Parameters</tt> instance with
753
- # changed keys. This includes the keys from the root hash and from all
754
- # nested hashes and arrays. The values are unchanged.
803
+ # Returns the same `ActionController::Parameters` instance with changed keys.
804
+ # This includes the keys from the root hash and from all nested hashes and
805
+ # arrays. The values are unchanged.
755
806
  def deep_transform_keys!(&block)
756
- @parameters.deep_transform_keys!(&block)
807
+ @parameters = _deep_transform_keys_in_object(@parameters, &block).to_unsafe_h
757
808
  self
758
809
  end
759
810
 
760
- # Deletes a key-value pair from +Parameters+ and returns the value. If
761
- # +key+ is not found, returns +nil+ (or, with optional code block, yields
762
- # +key+ and returns the result). This method is similar to #extract!, which
763
- # returns the corresponding +ActionController::Parameters+ object.
811
+ # Deletes a key-value pair from `Parameters` and returns the value. If `key` is
812
+ # not found, returns `nil` (or, with optional code block, yields `key` and
813
+ # returns the result). This method is similar to #extract!, which returns the
814
+ # corresponding `ActionController::Parameters` object.
764
815
  def delete(key, &block)
765
816
  convert_value_to_parameters(@parameters.delete(key, &block))
766
817
  end
767
818
 
768
- # Returns a new <tt>ActionController::Parameters</tt> instance with only
769
- # items that the block evaluates to true.
819
+ # Returns a new `ActionController::Parameters` instance with only items that the
820
+ # block evaluates to true.
770
821
  def select(&block)
771
822
  new_instance_with_inherited_permitted_status(@parameters.select(&block))
772
823
  end
773
824
 
774
- # Equivalent to Hash#keep_if, but returns +nil+ if no changes were made.
825
+ # Equivalent to Hash#keep_if, but returns `nil` if no changes were made.
775
826
  def select!(&block)
776
827
  @parameters.select!(&block)
777
828
  self
778
829
  end
779
830
  alias_method :keep_if, :select!
780
831
 
781
- # Returns a new <tt>ActionController::Parameters</tt> instance with items
782
- # that the block evaluates to true removed.
832
+ # Returns a new `ActionController::Parameters` instance with items that the
833
+ # block evaluates to true removed.
783
834
  def reject(&block)
784
835
  new_instance_with_inherited_permitted_status(@parameters.reject(&block))
785
836
  end
@@ -791,51 +842,67 @@ module ActionController
791
842
  end
792
843
  alias_method :delete_if, :reject!
793
844
 
794
- # Returns a new <tt>ActionController::Parameters</tt> instance with +nil+ values removed.
845
+ # Returns a new `ActionController::Parameters` instance with `nil` values
846
+ # removed.
795
847
  def compact
796
848
  new_instance_with_inherited_permitted_status(@parameters.compact)
797
849
  end
798
850
 
799
- # Removes all +nil+ values in place and returns +self+, or +nil+ if no changes were made.
851
+ # Removes all `nil` values in place and returns `self`, or `nil` if no changes
852
+ # were made.
800
853
  def compact!
801
854
  self if @parameters.compact!
802
855
  end
803
856
 
804
- # Returns a new <tt>ActionController::Parameters</tt> instance without the blank values.
805
- # Uses Object#blank? for determining if a value is blank.
857
+ # Returns a new `ActionController::Parameters` instance without the blank
858
+ # values. Uses Object#blank? for determining if a value is blank.
806
859
  def compact_blank
807
860
  reject { |_k, v| v.blank? }
808
861
  end
809
862
 
810
- # Removes all blank values in place and returns self.
811
- # Uses Object#blank? for determining if a value is blank.
863
+ # Removes all blank values in place and returns self. Uses Object#blank? for
864
+ # determining if a value is blank.
812
865
  def compact_blank!
813
866
  reject! { |_k, v| v.blank? }
814
867
  end
815
868
 
816
- # Returns values that were assigned to the given +keys+. Note that all the
817
- # +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
869
+ # Returns true if the given value is present for some key in the parameters.
870
+ def has_value?(value)
871
+ each_value.include?(convert_value_to_parameters(value))
872
+ end
873
+
874
+ alias value? has_value?
875
+
876
+ # Returns values that were assigned to the given `keys`. Note that all the
877
+ # `Hash` objects will be converted to `ActionController::Parameters`.
818
878
  def values_at(*keys)
819
879
  convert_value_to_parameters(@parameters.values_at(*keys))
820
880
  end
821
881
 
822
- # Returns a new <tt>ActionController::Parameters</tt> instance with all keys from
823
- # +other_hash+ merged into current hash.
882
+ # Returns a new `ActionController::Parameters` instance with all keys from
883
+ # `other_hash` merged into current hash.
824
884
  def merge(other_hash)
825
885
  new_instance_with_inherited_permitted_status(
826
886
  @parameters.merge(other_hash.to_h)
827
887
  )
828
888
  end
829
889
 
830
- # Returns the current <tt>ActionController::Parameters</tt> instance with
831
- # +other_hash+ merged into current hash.
832
- def merge!(other_hash)
833
- @parameters.merge!(other_hash.to_h)
890
+ ##
891
+ # :call-seq: merge!(other_hash)
892
+ #
893
+ # Returns the current `ActionController::Parameters` instance with `other_hash`
894
+ # merged into current hash.
895
+ def merge!(other_hash, &block)
896
+ @parameters.merge!(other_hash.to_h, &block)
834
897
  self
835
898
  end
836
899
 
837
- # Returns a new <tt>ActionController::Parameters</tt> instance with all keys
838
- # from current hash merged into +other_hash+.
900
+ def deep_merge?(other_hash) # :nodoc:
901
+ other_hash.is_a?(ActiveSupport::DeepMergeable)
902
+ end
903
+
904
+ # Returns a new `ActionController::Parameters` instance with all keys from
905
+ # current hash merged into `other_hash`.
839
906
  def reverse_merge(other_hash)
840
907
  new_instance_with_inherited_permitted_status(
841
908
  other_hash.to_h.merge(@parameters)
@@ -843,17 +910,17 @@ module ActionController
843
910
  end
844
911
  alias_method :with_defaults, :reverse_merge
845
912
 
846
- # Returns the current <tt>ActionController::Parameters</tt> instance with
847
- # current hash merged into +other_hash+.
913
+ # Returns the current `ActionController::Parameters` instance with current hash
914
+ # merged into `other_hash`.
848
915
  def reverse_merge!(other_hash)
849
916
  @parameters.merge!(other_hash.to_h) { |key, left, right| left }
850
917
  self
851
918
  end
852
919
  alias_method :with_defaults!, :reverse_merge!
853
920
 
854
- # This is required by ActiveModel attribute assignment, so that user can
855
- # pass +Parameters+ to a mass assignment methods in a model. It should not
856
- # matter as we are using +HashWithIndifferentAccess+ internally.
921
+ # This is required by ActiveModel attribute assignment, so that user can pass
922
+ # `Parameters` to a mass assignment methods in a model. It should not matter as
923
+ # we are using `HashWithIndifferentAccess` internally.
857
924
  def stringify_keys # :nodoc:
858
925
  dup
859
926
  end
@@ -878,13 +945,13 @@ module ActionController
878
945
  @parameters = coder.map.with_indifferent_access
879
946
  @permitted = false
880
947
  when "!ruby/hash-with-ivars:ActionController::Parameters"
881
- # YAML 2.0.9's Hash subclass format where keys and values
882
- # were stored under an elements hash and `permitted` within an ivars hash.
948
+ # YAML 2.0.9's Hash subclass format where keys and values were stored under an
949
+ # elements hash and `permitted` within an ivars hash.
883
950
  @parameters = coder.map["elements"].with_indifferent_access
884
951
  @permitted = coder.map["ivars"][:@permitted]
885
952
  when "!ruby/object:ActionController::Parameters"
886
- # YAML's Object format. Only needed because of the format
887
- # backwards compatibility above, otherwise equivalent to YAML's initialization.
953
+ # YAML's Object format. Only needed because of the format backwards
954
+ # compatibility above, otherwise equivalent to YAML's initialization.
888
955
  @parameters, @permitted = coder.map["parameters"], coder.map["permitted"]
889
956
  end
890
957
  end
@@ -893,13 +960,30 @@ module ActionController
893
960
  coder.map = { "parameters" => @parameters, "permitted" => @permitted }
894
961
  end
895
962
 
896
- # Returns a duplicate +ActionController::Parameters+ instance with the same permitted parameters.
963
+ # Returns a duplicate `ActionController::Parameters` instance with the same
964
+ # permitted parameters.
897
965
  def deep_dup
898
966
  self.class.new(@parameters.deep_dup, @logging_context).tap do |duplicate|
899
967
  duplicate.permitted = @permitted
900
968
  end
901
969
  end
902
970
 
971
+ # Returns parameter value for the given `key` separated by `delimiter`.
972
+ #
973
+ # params = ActionController::Parameters.new(id: "1_123", tags: "ruby,rails")
974
+ # params.extract_value(:id) # => ["1", "123"]
975
+ # params.extract_value(:tags, delimiter: ",") # => ["ruby", "rails"]
976
+ # params.extract_value(:non_existent_key) # => nil
977
+ #
978
+ # Note that if the given `key`'s value contains blank elements, then the
979
+ # returned array will include empty strings.
980
+ #
981
+ # params = ActionController::Parameters.new(tags: "ruby,rails,,web")
982
+ # params.extract_value(:tags, delimiter: ",") # => ["ruby", "rails", "", "web"]
983
+ def extract_value(key, delimiter: "_")
984
+ @parameters[key]&.split(delimiter, -1)
985
+ end
986
+
903
987
  protected
904
988
  attr_reader :parameters
905
989
 
@@ -922,14 +1006,15 @@ module ActionController
922
1006
  end
923
1007
  end
924
1008
 
925
- def convert_parameters_to_hashes(value, using)
1009
+ def convert_parameters_to_hashes(value, using, &block)
926
1010
  case value
927
1011
  when Array
928
1012
  value.map { |v| convert_parameters_to_hashes(v, using) }
929
1013
  when Hash
930
- value.transform_values do |v|
1014
+ transformed = value.transform_values do |v|
931
1015
  convert_parameters_to_hashes(v, using)
932
- end.with_indifferent_access
1016
+ end
1017
+ (block_given? ? transformed.to_h(&block) : transformed).with_indifferent_access
933
1018
  when Parameters
934
1019
  value.send(using)
935
1020
  else
@@ -957,6 +1042,46 @@ module ActionController
957
1042
  end
958
1043
  end
959
1044
 
1045
+ def _deep_transform_keys_in_object(object, &block)
1046
+ case object
1047
+ when Hash
1048
+ object.each_with_object(self.class.new) do |(key, value), result|
1049
+ result[yield(key)] = _deep_transform_keys_in_object(value, &block)
1050
+ end
1051
+ when Parameters
1052
+ if object.permitted?
1053
+ object.to_h.deep_transform_keys(&block)
1054
+ else
1055
+ object.to_unsafe_h.deep_transform_keys(&block)
1056
+ end
1057
+ when Array
1058
+ object.map { |e| _deep_transform_keys_in_object(e, &block) }
1059
+ else
1060
+ object
1061
+ end
1062
+ end
1063
+
1064
+ def _deep_transform_keys_in_object!(object, &block)
1065
+ case object
1066
+ when Hash
1067
+ object.keys.each do |key|
1068
+ value = object.delete(key)
1069
+ object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
1070
+ end
1071
+ object
1072
+ when Parameters
1073
+ if object.permitted?
1074
+ object.to_h.deep_transform_keys!(&block)
1075
+ else
1076
+ object.to_unsafe_h.deep_transform_keys!(&block)
1077
+ end
1078
+ when Array
1079
+ object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
1080
+ else
1081
+ object
1082
+ end
1083
+ end
1084
+
960
1085
  def specify_numeric_keys?(filter)
961
1086
  if filter.respond_to?(:keys)
962
1087
  filter.keys.any? { |key| /\A-?\d+\z/.match?(key) }
@@ -996,15 +1121,14 @@ module ActionController
996
1121
  #
997
1122
  # --- Filtering ----------------------------------------------------------
998
1123
  #
999
-
1000
- # This is a list of permitted scalar types that includes the ones
1001
- # supported in XML and JSON requests.
1124
+ # This is a list of permitted scalar types that includes the ones supported in
1125
+ # XML and JSON requests.
1002
1126
  #
1003
- # This list is in particular used to filter ordinary requests, \String goes
1004
- # as first element to quickly short-circuit the common case.
1127
+ # This list is in particular used to filter ordinary requests, String goes as
1128
+ # first element to quickly short-circuit the common case.
1005
1129
  #
1006
- # If you modify this collection please update the one in the #permit doc
1007
- # as well.
1130
+ # If you modify this collection please update the one in the #permit doc as
1131
+ # well.
1008
1132
  PERMITTED_SCALAR_TYPES = [
1009
1133
  String,
1010
1134
  Symbol,
@@ -1029,12 +1153,12 @@ module ActionController
1029
1153
  #
1030
1154
  # For example:
1031
1155
  #
1032
- # puts self.keys #=> ["zipcode(90210i)"]
1033
- # params = {}
1156
+ # puts self.keys #=> ["zipcode(90210i)"]
1157
+ # params = {}
1034
1158
  #
1035
- # permitted_scalar_filter(params, "zipcode")
1159
+ # permitted_scalar_filter(params, "zipcode")
1036
1160
  #
1037
- # puts params.keys # => ["zipcode"]
1161
+ # puts params.keys # => ["zipcode"]
1038
1162
  def permitted_scalar_filter(params, permitted_key)
1039
1163
  permitted_key = permitted_key.to_s
1040
1164
 
@@ -1112,6 +1236,8 @@ module ActionController
1112
1236
  case element
1113
1237
  when ->(e) { permitted_scalar?(e) }
1114
1238
  sanitized << element
1239
+ when Array
1240
+ sanitized << permit_any_in_array(element)
1115
1241
  when Parameters
1116
1242
  sanitized << permit_any_in_parameters(element)
1117
1243
  else
@@ -1127,77 +1253,76 @@ module ActionController
1127
1253
  end
1128
1254
  end
1129
1255
 
1130
- # == Strong \Parameters
1256
+ # # Strong Parameters
1131
1257
  #
1132
- # It provides an interface for protecting attributes from end-user
1133
- # assignment. This makes Action Controller parameters forbidden
1134
- # to be used in Active Model mass assignment until they have been explicitly
1135
- # enumerated.
1258
+ # It provides an interface for protecting attributes from end-user assignment.
1259
+ # This makes Action Controller parameters forbidden to be used in Active Model
1260
+ # mass assignment until they have been explicitly enumerated.
1136
1261
  #
1137
1262
  # In addition, parameters can be marked as required and flow through a
1138
- # predefined raise/rescue flow to end up as a <tt>400 Bad Request</tt> with no
1139
- # effort.
1263
+ # predefined raise/rescue flow to end up as a `400 Bad Request` with no effort.
1140
1264
  #
1141
- # class PeopleController < ActionController::Base
1142
- # # Using "Person.create(params[:person])" would raise an
1143
- # # ActiveModel::ForbiddenAttributesError exception because it'd
1144
- # # be using mass assignment without an explicit permit step.
1145
- # # This is the recommended form:
1146
- # def create
1147
- # Person.create(person_params)
1148
- # end
1149
- #
1150
- # # This will pass with flying colors as long as there's a person key in the
1151
- # # parameters, otherwise it'll raise an ActionController::ParameterMissing
1152
- # # exception, which will get caught by ActionController::Base and turned
1153
- # # into a 400 Bad Request reply.
1154
- # def update
1155
- # redirect_to current_account.people.find(params[:id]).tap { |person|
1156
- # person.update!(person_params)
1157
- # }
1158
- # end
1265
+ # class PeopleController < ActionController::Base
1266
+ # # Using "Person.create(params[:person])" would raise an
1267
+ # # ActiveModel::ForbiddenAttributesError exception because it'd
1268
+ # # be using mass assignment without an explicit permit step.
1269
+ # # This is the recommended form:
1270
+ # def create
1271
+ # Person.create(person_params)
1272
+ # end
1159
1273
  #
1160
- # private
1161
- # # Using a private method to encapsulate the permissible parameters is
1162
- # # a good pattern since you'll be able to reuse the same permit
1163
- # # list between create and update. Also, you can specialize this method
1164
- # # with per-user checking of permissible attributes.
1165
- # def person_params
1166
- # params.require(:person).permit(:name, :age)
1274
+ # # This will pass with flying colors as long as there's a person key in the
1275
+ # # parameters, otherwise it'll raise an ActionController::ParameterMissing
1276
+ # # exception, which will get caught by ActionController::Base and turned
1277
+ # # into a 400 Bad Request reply.
1278
+ # def update
1279
+ # redirect_to current_account.people.find(params[:id]).tap { |person|
1280
+ # person.update!(person_params)
1281
+ # }
1167
1282
  # end
1168
- # end
1169
1283
  #
1170
- # In order to use <tt>accepts_nested_attributes_for</tt> with Strong \Parameters, you
1171
- # will need to specify which nested attributes should be permitted. You might want
1172
- # to allow +:id+ and +:_destroy+, see ActiveRecord::NestedAttributes for more information.
1284
+ # private
1285
+ # # Using a private method to encapsulate the permissible parameters is
1286
+ # # a good pattern since you'll be able to reuse the same permit
1287
+ # # list between create and update. Also, you can specialize this method
1288
+ # # with per-user checking of permissible attributes.
1289
+ # def person_params
1290
+ # params.require(:person).permit(:name, :age)
1291
+ # end
1292
+ # end
1173
1293
  #
1174
- # class Person
1175
- # has_many :pets
1176
- # accepts_nested_attributes_for :pets
1177
- # end
1294
+ # In order to use `accepts_nested_attributes_for` with Strong Parameters, you
1295
+ # will need to specify which nested attributes should be permitted. You might
1296
+ # want to allow `:id` and `:_destroy`, see ActiveRecord::NestedAttributes for
1297
+ # more information.
1178
1298
  #
1179
- # class PeopleController < ActionController::Base
1180
- # def create
1181
- # Person.create(person_params)
1299
+ # class Person
1300
+ # has_many :pets
1301
+ # accepts_nested_attributes_for :pets
1182
1302
  # end
1183
1303
  #
1184
- # ...
1304
+ # class PeopleController < ActionController::Base
1305
+ # def create
1306
+ # Person.create(person_params)
1307
+ # end
1308
+ #
1309
+ # ...
1185
1310
  #
1186
- # private
1311
+ # private
1187
1312
  #
1188
- # def person_params
1189
- # # It's mandatory to specify the nested attributes that should be permitted.
1190
- # # If you use `permit` with just the key that points to the nested attributes hash,
1191
- # # it will return an empty hash.
1192
- # params.require(:person).permit(:name, :age, pets_attributes: [ :id, :name, :category ])
1193
- # end
1194
- # end
1313
+ # def person_params
1314
+ # # It's mandatory to specify the nested attributes that should be permitted.
1315
+ # # If you use `permit` with just the key that points to the nested attributes hash,
1316
+ # # it will return an empty hash.
1317
+ # params.require(:person).permit(:name, :age, pets_attributes: [ :id, :name, :category ])
1318
+ # end
1319
+ # end
1195
1320
  #
1196
- # See ActionController::Parameters.require and ActionController::Parameters.permit
1197
- # for more information.
1321
+ # See ActionController::Parameters.require and
1322
+ # ActionController::Parameters.permit for more information.
1198
1323
  module StrongParameters
1199
- # Returns a new ActionController::Parameters object that
1200
- # has been instantiated with the <tt>request.parameters</tt>.
1324
+ # Returns a new ActionController::Parameters object that has been instantiated
1325
+ # with the `request.parameters`.
1201
1326
  def params
1202
1327
  @_params ||= begin
1203
1328
  context = {
@@ -1210,9 +1335,9 @@ module ActionController
1210
1335
  end
1211
1336
  end
1212
1337
 
1213
- # Assigns the given +value+ to the +params+ hash. If +value+
1214
- # is a Hash, this will create an ActionController::Parameters
1215
- # object that has been instantiated with the given +value+ hash.
1338
+ # Assigns the given `value` to the `params` hash. If `value` is a Hash, this
1339
+ # will create an ActionController::Parameters object that has been instantiated
1340
+ # with the given `value` hash.
1216
1341
  def params=(value)
1217
1342
  @_params = value.is_a?(Hash) ? Parameters.new(value) : value
1218
1343
  end