actionpack 7.1.5.1 → 8.1.2

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