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