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