omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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