omg-actionpack 8.0.0.alpha1

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