actionpack 4.2.8 → 5.2.4.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 +285 -444
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller.rb +12 -5
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +45 -49
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
- 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/action_controller.rb +29 -21
- data/lib/action_controller/api.rb +149 -0
- data/lib/action_controller/api/api_rendering.rb +16 -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.rb +98 -83
- 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 +64 -57
- 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 +203 -92
- 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 +582 -165
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- 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_dispatch.rb +27 -19
- 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 +60 -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.rb +7 -5
- 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.rb +35 -23
- data/lib/action_dispatch/journey/router/utils.rb +20 -11
- 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/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.rb +17 -18
- 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 +734 -447
- data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
- 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/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.rb +6 -4
- 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/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_pack.rb +4 -2
- data/lib/action_pack/gem_version.rb +5 -3
- data/lib/action_pack/version.rb +3 -1
- metadata +56 -39
- 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,25 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionController #:nodoc:
|
2
|
-
# This module is responsible
|
3
|
-
# to controllers and
|
4
|
+
# This module is responsible for providing +rescue_from+ helpers
|
5
|
+
# to controllers and configuring when detailed exceptions must be
|
4
6
|
# shown.
|
5
7
|
module Rescue
|
6
8
|
extend ActiveSupport::Concern
|
7
9
|
include ActiveSupport::Rescuable
|
8
10
|
|
9
|
-
def rescue_with_handler(exception)
|
10
|
-
if (exception.respond_to?(:original_exception) &&
|
11
|
-
(orig_exception = exception.original_exception) &&
|
12
|
-
handler_for_rescue(orig_exception))
|
13
|
-
exception = orig_exception
|
14
|
-
end
|
15
|
-
super(exception)
|
16
|
-
end
|
17
|
-
|
18
11
|
# Override this method if you want to customize when detailed
|
19
12
|
# exceptions must be shown. This method is only called when
|
20
|
-
# consider_all_requests_local is false
|
21
|
-
# false
|
22
|
-
# requests in production still
|
13
|
+
# +consider_all_requests_local+ is +false+. By default, it returns
|
14
|
+
# +false+, but someone may set it to <tt>request.local?</tt> so local
|
15
|
+
# requests in production still show the detailed exception pages.
|
23
16
|
def show_detailed_exceptions?
|
24
17
|
false
|
25
18
|
end
|
@@ -28,8 +21,8 @@ module ActionController #:nodoc:
|
|
28
21
|
def process_action(*args)
|
29
22
|
super
|
30
23
|
rescue Exception => exception
|
31
|
-
request.env[
|
32
|
-
rescue_with_handler(exception) || raise
|
24
|
+
request.env["action_dispatch.show_detailed_exceptions"] ||= show_detailed_exceptions?
|
25
|
+
rescue_with_handler(exception) || raise
|
33
26
|
end
|
34
27
|
end
|
35
28
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack/chunked"
|
2
4
|
|
3
5
|
module ActionController #:nodoc:
|
4
6
|
# Allows views to be streamed back to the client as they are rendered.
|
5
7
|
#
|
6
|
-
#
|
8
|
+
# By default, Rails renders views by first rendering the template
|
7
9
|
# and then the layout. The response is sent to the client after the whole
|
8
10
|
# template is rendered, all queries are made, and the layout is processed.
|
9
11
|
#
|
@@ -110,9 +112,9 @@ module ActionController #:nodoc:
|
|
110
112
|
# This means that, if you have <code>yield :title</code> in your layout
|
111
113
|
# and you want to use streaming, you would have to render the whole template
|
112
114
|
# (and eventually trigger all queries) before streaming the title and all
|
113
|
-
# assets, which kills the purpose of streaming. For this
|
114
|
-
#
|
115
|
-
#
|
115
|
+
# assets, which kills the purpose of streaming. For this purpose, you can use
|
116
|
+
# a helper called +provide+ that does the same as +content_for+ but tells the
|
117
|
+
# layout to stop searching for other entries and continue rendering.
|
116
118
|
#
|
117
119
|
# For instance, the template above using +provide+ would be:
|
118
120
|
#
|
@@ -181,7 +183,7 @@ module ActionController #:nodoc:
|
|
181
183
|
# unicorn_rails --config-file unicorn.config.rb
|
182
184
|
#
|
183
185
|
# You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
|
184
|
-
# Please check its documentation for more information:
|
186
|
+
# Please check its documentation for more information: https://bogomips.org/unicorn/Unicorn/Configurator.html#method-i-listen
|
185
187
|
#
|
186
188
|
# If you are using Unicorn with NGINX, you may need to tweak NGINX.
|
187
189
|
# Streaming should work out of the box on Rainbows.
|
@@ -193,13 +195,13 @@ module ActionController #:nodoc:
|
|
193
195
|
module Streaming
|
194
196
|
extend ActiveSupport::Concern
|
195
197
|
|
196
|
-
|
198
|
+
private
|
197
199
|
|
198
200
|
# Set proper cache control and transfer encoding when streaming
|
199
|
-
def _process_options(options)
|
201
|
+
def _process_options(options)
|
200
202
|
super
|
201
203
|
if options[:stream]
|
202
|
-
if
|
204
|
+
if request.version == "HTTP/1.0"
|
203
205
|
options.delete(:stream)
|
204
206
|
else
|
205
207
|
headers["Cache-Control"] ||= "no-cache"
|
@@ -210,7 +212,7 @@ module ActionController #:nodoc:
|
|
210
212
|
end
|
211
213
|
|
212
214
|
# Call render_body if we are streaming instead of usual +render+.
|
213
|
-
def _render_template(options)
|
215
|
+
def _render_template(options)
|
214
216
|
if options.delete(:stream)
|
215
217
|
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
|
216
218
|
else
|
@@ -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,121 @@ 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
|
-
|
185
|
-
super
|
186
340
|
end
|
187
|
-
|
188
341
|
alias_method :each, :each_pair
|
189
342
|
|
190
343
|
# Attribute that keeps track of converted arrays, if any, to avoid double
|
@@ -214,7 +367,7 @@ module ActionController
|
|
214
367
|
# class Person < ActiveRecord::Base
|
215
368
|
# end
|
216
369
|
#
|
217
|
-
# params = ActionController::Parameters.new(name:
|
370
|
+
# params = ActionController::Parameters.new(name: "Francesco")
|
218
371
|
# params.permitted? # => false
|
219
372
|
# Person.new(params) # => ActiveModel::ForbiddenAttributesError
|
220
373
|
# params.permit!
|
@@ -222,7 +375,7 @@ module ActionController
|
|
222
375
|
# Person.new(params) # => #<Person id: nil, name: "Francesco">
|
223
376
|
def permit!
|
224
377
|
each_pair do |key, value|
|
225
|
-
Array.wrap(value).each do |v|
|
378
|
+
Array.wrap(value).flatten.each do |v|
|
226
379
|
v.permit! if v.respond_to? :permit!
|
227
380
|
end
|
228
381
|
end
|
@@ -231,19 +384,58 @@ module ActionController
|
|
231
384
|
self
|
232
385
|
end
|
233
386
|
|
234
|
-
#
|
235
|
-
#
|
236
|
-
#
|
387
|
+
# This method accepts both a single key and an array of keys.
|
388
|
+
#
|
389
|
+
# When passed a single key, if it exists and its associated value is
|
390
|
+
# either present or the singleton +false+, returns said value:
|
391
|
+
#
|
392
|
+
# ActionController::Parameters.new(person: { name: "Francesco" }).require(:person)
|
393
|
+
# # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
|
237
394
|
#
|
238
|
-
#
|
239
|
-
#
|
395
|
+
# Otherwise raises <tt>ActionController::ParameterMissing</tt>:
|
396
|
+
#
|
397
|
+
# ActionController::Parameters.new.require(:person)
|
398
|
+
# # ActionController::ParameterMissing: param is missing or the value is empty: person
|
240
399
|
#
|
241
400
|
# ActionController::Parameters.new(person: nil).require(:person)
|
242
|
-
# #
|
401
|
+
# # ActionController::ParameterMissing: param is missing or the value is empty: person
|
402
|
+
#
|
403
|
+
# ActionController::Parameters.new(person: "\t").require(:person)
|
404
|
+
# # ActionController::ParameterMissing: param is missing or the value is empty: person
|
243
405
|
#
|
244
406
|
# ActionController::Parameters.new(person: {}).require(:person)
|
245
|
-
# #
|
407
|
+
# # ActionController::ParameterMissing: param is missing or the value is empty: person
|
408
|
+
#
|
409
|
+
# When given an array of keys, the method tries to require each one of them
|
410
|
+
# in order. If it succeeds, an array with the respective return values is
|
411
|
+
# returned:
|
412
|
+
#
|
413
|
+
# params = ActionController::Parameters.new(user: { ... }, profile: { ... })
|
414
|
+
# user_params, profile_params = params.require([:user, :profile])
|
415
|
+
#
|
416
|
+
# Otherwise, the method re-raises the first exception found:
|
417
|
+
#
|
418
|
+
# params = ActionController::Parameters.new(user: {}, profile: {})
|
419
|
+
# user_params, profile_params = params.require([:user, :profile])
|
420
|
+
# # ActionController::ParameterMissing: param is missing or the value is empty: user
|
421
|
+
#
|
422
|
+
# Technically this method can be used to fetch terminal values:
|
423
|
+
#
|
424
|
+
# # CAREFUL
|
425
|
+
# params = ActionController::Parameters.new(person: { name: "Finn" })
|
426
|
+
# name = params.require(:person).require(:name) # CAREFUL
|
427
|
+
#
|
428
|
+
# but take into account that at some point those ones have to be permitted:
|
429
|
+
#
|
430
|
+
# def person_params
|
431
|
+
# params.require(:person).permit(:name).tap do |person_params|
|
432
|
+
# person_params.require(:name) # SAFER
|
433
|
+
# end
|
434
|
+
# end
|
435
|
+
#
|
436
|
+
# for example.
|
246
437
|
def require(key)
|
438
|
+
return key.map { |k| require(k) } if key.is_a?(Array)
|
247
439
|
value = self[key]
|
248
440
|
if value.present? || value == false
|
249
441
|
value
|
@@ -260,7 +452,7 @@ module ActionController
|
|
260
452
|
# for the object to +true+. This is useful for limiting which attributes
|
261
453
|
# should be allowed for mass updating.
|
262
454
|
#
|
263
|
-
# params = ActionController::Parameters.new(user: { name:
|
455
|
+
# params = ActionController::Parameters.new(user: { name: "Francesco", age: 22, role: "admin" })
|
264
456
|
# permitted = params.require(:user).permit(:name, :age)
|
265
457
|
# permitted.permitted? # => true
|
266
458
|
# permitted.has_key?(:name) # => true
|
@@ -271,7 +463,7 @@ module ActionController
|
|
271
463
|
#
|
272
464
|
# params.permit(:name)
|
273
465
|
#
|
274
|
-
# +:name+ passes it is a key of +params+ whose associated value is of type
|
466
|
+
# +:name+ passes if it is a key of +params+ whose associated value is of type
|
275
467
|
# +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+,
|
276
468
|
# +Date+, +Time+, +DateTime+, +StringIO+, +IO+,
|
277
469
|
# +ActionDispatch::Http::UploadedFile+ or +Rack::Test::UploadedFile+.
|
@@ -280,18 +472,27 @@ module ActionController
|
|
280
472
|
# You may declare that the parameter should be an array of permitted scalars
|
281
473
|
# by mapping it to an empty array:
|
282
474
|
#
|
283
|
-
# params = ActionController::Parameters.new(tags: [
|
475
|
+
# params = ActionController::Parameters.new(tags: ["rails", "parameters"])
|
284
476
|
# params.permit(tags: [])
|
285
477
|
#
|
478
|
+
# Sometimes it is not possible or convenient to declare the valid keys of
|
479
|
+
# a hash parameter or its internal structure. Just map to an empty hash:
|
480
|
+
#
|
481
|
+
# params.permit(preferences: {})
|
482
|
+
#
|
483
|
+
# Be careful because this opens the door to arbitrary input. In this
|
484
|
+
# case, +permit+ ensures values in the returned structure are permitted
|
485
|
+
# scalars and filters out anything else.
|
486
|
+
#
|
286
487
|
# You can also use +permit+ on nested parameters, like:
|
287
488
|
#
|
288
489
|
# params = ActionController::Parameters.new({
|
289
490
|
# person: {
|
290
|
-
# name:
|
491
|
+
# name: "Francesco",
|
291
492
|
# age: 22,
|
292
493
|
# pets: [{
|
293
|
-
# name:
|
294
|
-
# category:
|
494
|
+
# name: "Purplish",
|
495
|
+
# category: "dogs"
|
295
496
|
# }]
|
296
497
|
# }
|
297
498
|
# })
|
@@ -310,20 +511,20 @@ module ActionController
|
|
310
511
|
# params = ActionController::Parameters.new({
|
311
512
|
# person: {
|
312
513
|
# contact: {
|
313
|
-
# email:
|
314
|
-
# phone:
|
514
|
+
# email: "none@test.com",
|
515
|
+
# phone: "555-1234"
|
315
516
|
# }
|
316
517
|
# }
|
317
518
|
# })
|
318
519
|
#
|
319
520
|
# params.require(:person).permit(:contact)
|
320
|
-
# # => {}
|
521
|
+
# # => <ActionController::Parameters {} permitted: true>
|
321
522
|
#
|
322
523
|
# params.require(:person).permit(contact: :phone)
|
323
|
-
# # => {"contact"
|
524
|
+
# # => <ActionController::Parameters {"contact"=><ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true>
|
324
525
|
#
|
325
526
|
# params.require(:person).permit(contact: [ :email, :phone ])
|
326
|
-
# # => {"contact"
|
527
|
+
# # => <ActionController::Parameters {"contact"=><ActionController::Parameters {"email"=>"none@test.com", "phone"=>"555-1234"} permitted: true>} permitted: true>
|
327
528
|
def permit(*filters)
|
328
529
|
params = self.class.new
|
329
530
|
|
@@ -331,7 +532,7 @@ module ActionController
|
|
331
532
|
case filter
|
332
533
|
when Symbol, String
|
333
534
|
permitted_scalar_filter(params, filter)
|
334
|
-
when Hash
|
535
|
+
when Hash
|
335
536
|
hash_filter(params, filter)
|
336
537
|
end
|
337
538
|
end
|
@@ -344,28 +545,58 @@ module ActionController
|
|
344
545
|
# Returns a parameter for the given +key+. If not found,
|
345
546
|
# returns +nil+.
|
346
547
|
#
|
347
|
-
# params = ActionController::Parameters.new(person: { name:
|
348
|
-
# params[:person] # => {"name"=>"Francesco"}
|
548
|
+
# params = ActionController::Parameters.new(person: { name: "Francesco" })
|
549
|
+
# params[:person] # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
|
349
550
|
# params[:none] # => nil
|
350
551
|
def [](key)
|
351
|
-
convert_hashes_to_parameters(key,
|
552
|
+
convert_hashes_to_parameters(key, @parameters[key])
|
553
|
+
end
|
554
|
+
|
555
|
+
# Assigns a value to a given +key+. The given key may still get filtered out
|
556
|
+
# when +permit+ is called.
|
557
|
+
def []=(key, value)
|
558
|
+
@parameters[key] = value
|
352
559
|
end
|
353
560
|
|
354
561
|
# Returns a parameter for the given +key+. If the +key+
|
355
562
|
# can't be found, there are several options: With no other arguments,
|
356
563
|
# it will raise an <tt>ActionController::ParameterMissing</tt> error;
|
357
|
-
# if
|
564
|
+
# if a second argument is given, then that is returned (converted to an
|
565
|
+
# instance of ActionController::Parameters if possible); if a block
|
358
566
|
# is given, then that will be run and its result returned.
|
359
567
|
#
|
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
|
568
|
+
# params = ActionController::Parameters.new(person: { name: "Francesco" })
|
569
|
+
# params.fetch(:person) # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
|
570
|
+
# params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
|
571
|
+
# params.fetch(:none, {}) # => <ActionController::Parameters {} permitted: false>
|
572
|
+
# params.fetch(:none, "Francesco") # => "Francesco"
|
573
|
+
# params.fetch(:none) { "Francesco" } # => "Francesco"
|
365
574
|
def fetch(key, *args)
|
366
|
-
|
367
|
-
|
368
|
-
|
575
|
+
convert_value_to_parameters(
|
576
|
+
@parameters.fetch(key) {
|
577
|
+
if block_given?
|
578
|
+
yield
|
579
|
+
else
|
580
|
+
args.fetch(0) { raise ActionController::ParameterMissing.new(key) }
|
581
|
+
end
|
582
|
+
}
|
583
|
+
)
|
584
|
+
end
|
585
|
+
|
586
|
+
if Hash.method_defined?(:dig)
|
587
|
+
# Extracts the nested parameter from the given +keys+ by calling +dig+
|
588
|
+
# at each step. Returns +nil+ if any intermediate step is +nil+.
|
589
|
+
#
|
590
|
+
# params = ActionController::Parameters.new(foo: { bar: { baz: 1 } })
|
591
|
+
# params.dig(:foo, :bar, :baz) # => 1
|
592
|
+
# params.dig(:foo, :zot, :xyz) # => nil
|
593
|
+
#
|
594
|
+
# params2 = ActionController::Parameters.new(foo: [10, 11, 12])
|
595
|
+
# params2.dig(:foo, 1) # => 11
|
596
|
+
def dig(*keys)
|
597
|
+
convert_hashes_to_parameters(keys.first, @parameters[keys.first])
|
598
|
+
@parameters.dig(*keys)
|
599
|
+
end
|
369
600
|
end
|
370
601
|
|
371
602
|
# Returns a new <tt>ActionController::Parameters</tt> instance that
|
@@ -373,19 +604,36 @@ module ActionController
|
|
373
604
|
# don't exist, returns an empty hash.
|
374
605
|
#
|
375
606
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
376
|
-
# params.slice(:a, :b) # => {"a"=>1, "b"=>2}
|
377
|
-
# params.slice(:d) # => {}
|
607
|
+
# params.slice(:a, :b) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
|
608
|
+
# params.slice(:d) # => <ActionController::Parameters {} permitted: false>
|
378
609
|
def slice(*keys)
|
379
|
-
new_instance_with_inherited_permitted_status(
|
610
|
+
new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
|
611
|
+
end
|
612
|
+
|
613
|
+
# Returns current <tt>ActionController::Parameters</tt> instance which
|
614
|
+
# contains only the given +keys+.
|
615
|
+
def slice!(*keys)
|
616
|
+
@parameters.slice!(*keys)
|
617
|
+
self
|
618
|
+
end
|
619
|
+
|
620
|
+
# Returns a new <tt>ActionController::Parameters</tt> instance that
|
621
|
+
# filters out the given +keys+.
|
622
|
+
#
|
623
|
+
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
624
|
+
# params.except(:a, :b) # => <ActionController::Parameters {"c"=>3} permitted: false>
|
625
|
+
# params.except(:d) # => <ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false>
|
626
|
+
def except(*keys)
|
627
|
+
new_instance_with_inherited_permitted_status(@parameters.except(*keys))
|
380
628
|
end
|
381
629
|
|
382
630
|
# Removes and returns the key/value pairs matching the given keys.
|
383
631
|
#
|
384
632
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
385
|
-
# params.extract!(:a, :b) # => {"a"=>1, "b"=>2}
|
386
|
-
# params # => {"c"=>3}
|
633
|
+
# params.extract!(:a, :b) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
|
634
|
+
# params # => <ActionController::Parameters {"c"=>3} permitted: false>
|
387
635
|
def extract!(*keys)
|
388
|
-
new_instance_with_inherited_permitted_status(
|
636
|
+
new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
|
389
637
|
end
|
390
638
|
|
391
639
|
# Returns a new <tt>ActionController::Parameters</tt> with the results of
|
@@ -393,58 +641,169 @@ module ActionController
|
|
393
641
|
#
|
394
642
|
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
395
643
|
# params.transform_values { |x| x * 2 }
|
396
|
-
# # => {"a"=>2, "b"=>4, "c"=>6}
|
644
|
+
# # => <ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
|
397
645
|
def transform_values
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
end
|
646
|
+
return to_enum(:transform_values) unless block_given?
|
647
|
+
new_instance_with_inherited_permitted_status(
|
648
|
+
@parameters.transform_values { |v| yield convert_value_to_parameters(v) }
|
649
|
+
)
|
403
650
|
end
|
404
651
|
|
405
|
-
#
|
406
|
-
#
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
652
|
+
# Performs values transformation and returns the altered
|
653
|
+
# <tt>ActionController::Parameters</tt> instance.
|
654
|
+
def transform_values!
|
655
|
+
return to_enum(:transform_values!) unless block_given?
|
656
|
+
@parameters.transform_values! { |v| yield convert_value_to_parameters(v) }
|
657
|
+
self
|
658
|
+
end
|
659
|
+
|
660
|
+
# Returns a new <tt>ActionController::Parameters</tt> instance with the
|
661
|
+
# results of running +block+ once for every key. The values are unchanged.
|
662
|
+
def transform_keys(&block)
|
663
|
+
if block
|
664
|
+
new_instance_with_inherited_permitted_status(
|
665
|
+
@parameters.transform_keys(&block)
|
666
|
+
)
|
411
667
|
else
|
412
|
-
|
668
|
+
@parameters.transform_keys
|
413
669
|
end
|
414
670
|
end
|
415
671
|
|
416
|
-
#
|
417
|
-
#
|
418
|
-
|
419
|
-
|
672
|
+
# Performs keys transformation and returns the altered
|
673
|
+
# <tt>ActionController::Parameters</tt> instance.
|
674
|
+
def transform_keys!(&block)
|
675
|
+
@parameters.transform_keys!(&block)
|
676
|
+
self
|
677
|
+
end
|
678
|
+
|
679
|
+
# Deletes a key-value pair from +Parameters+ and returns the value. If
|
680
|
+
# +key+ is not found, returns +nil+ (or, with optional code block, yields
|
681
|
+
# +key+ and returns the result). Cf. +#extract!+, which returns the
|
682
|
+
# corresponding +ActionController::Parameters+ object.
|
420
683
|
def delete(key, &block)
|
421
|
-
|
684
|
+
convert_value_to_parameters(@parameters.delete(key, &block))
|
422
685
|
end
|
423
686
|
|
424
|
-
#
|
687
|
+
# Returns a new instance of <tt>ActionController::Parameters</tt> with only
|
688
|
+
# items that the block evaluates to true.
|
689
|
+
def select(&block)
|
690
|
+
new_instance_with_inherited_permitted_status(@parameters.select(&block))
|
691
|
+
end
|
692
|
+
|
693
|
+
# Equivalent to Hash#keep_if, but returns +nil+ if no changes were made.
|
425
694
|
def select!(&block)
|
426
|
-
|
695
|
+
@parameters.select!(&block)
|
696
|
+
self
|
427
697
|
end
|
698
|
+
alias_method :keep_if, :select!
|
428
699
|
|
429
|
-
# Returns
|
430
|
-
#
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
#
|
436
|
-
|
437
|
-
|
438
|
-
|
700
|
+
# Returns a new instance of <tt>ActionController::Parameters</tt> with items
|
701
|
+
# that the block evaluates to true removed.
|
702
|
+
def reject(&block)
|
703
|
+
new_instance_with_inherited_permitted_status(@parameters.reject(&block))
|
704
|
+
end
|
705
|
+
|
706
|
+
# Removes items that the block evaluates to true and returns self.
|
707
|
+
def reject!(&block)
|
708
|
+
@parameters.reject!(&block)
|
709
|
+
self
|
710
|
+
end
|
711
|
+
alias_method :delete_if, :reject!
|
712
|
+
|
713
|
+
# Returns values that were assigned to the given +keys+. Note that all the
|
714
|
+
# +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
|
715
|
+
def values_at(*keys)
|
716
|
+
convert_value_to_parameters(@parameters.values_at(*keys))
|
717
|
+
end
|
718
|
+
|
719
|
+
# Returns a new <tt>ActionController::Parameters</tt> with all keys from
|
720
|
+
# +other_hash+ merged into current hash.
|
721
|
+
def merge(other_hash)
|
722
|
+
new_instance_with_inherited_permitted_status(
|
723
|
+
@parameters.merge(other_hash.to_h)
|
724
|
+
)
|
725
|
+
end
|
726
|
+
|
727
|
+
# Returns current <tt>ActionController::Parameters</tt> instance with
|
728
|
+
# +other_hash+ merged into current hash.
|
729
|
+
def merge!(other_hash)
|
730
|
+
@parameters.merge!(other_hash.to_h)
|
731
|
+
self
|
732
|
+
end
|
733
|
+
|
734
|
+
# Returns a new <tt>ActionController::Parameters</tt> with all keys from
|
735
|
+
# current hash merged into +other_hash+.
|
736
|
+
def reverse_merge(other_hash)
|
737
|
+
new_instance_with_inherited_permitted_status(
|
738
|
+
other_hash.to_h.merge(@parameters)
|
739
|
+
)
|
740
|
+
end
|
741
|
+
alias_method :with_defaults, :reverse_merge
|
742
|
+
|
743
|
+
# Returns current <tt>ActionController::Parameters</tt> instance with
|
744
|
+
# current hash merged into +other_hash+.
|
745
|
+
def reverse_merge!(other_hash)
|
746
|
+
@parameters.merge!(other_hash.to_h) { |key, left, right| left }
|
747
|
+
self
|
748
|
+
end
|
749
|
+
alias_method :with_defaults!, :reverse_merge!
|
750
|
+
|
751
|
+
# This is required by ActiveModel attribute assignment, so that user can
|
752
|
+
# pass +Parameters+ to a mass assignment methods in a model. It should not
|
753
|
+
# matter as we are using +HashWithIndifferentAccess+ internally.
|
754
|
+
def stringify_keys # :nodoc:
|
755
|
+
dup
|
756
|
+
end
|
757
|
+
|
758
|
+
def inspect
|
759
|
+
"<#{self.class} #{@parameters} permitted: #{@permitted}>"
|
760
|
+
end
|
761
|
+
|
762
|
+
def self.hook_into_yaml_loading # :nodoc:
|
763
|
+
# Wire up YAML format compatibility with Rails 4.2 and Psych 2.0.8 and 2.0.9+.
|
764
|
+
# Makes the YAML parser call `init_with` when it encounters the keys below
|
765
|
+
# instead of trying its own parsing routines.
|
766
|
+
YAML.load_tags["!ruby/hash-with-ivars:ActionController::Parameters"] = name
|
767
|
+
YAML.load_tags["!ruby/hash:ActionController::Parameters"] = name
|
768
|
+
end
|
769
|
+
hook_into_yaml_loading
|
770
|
+
|
771
|
+
def init_with(coder) # :nodoc:
|
772
|
+
case coder.tag
|
773
|
+
when "!ruby/hash:ActionController::Parameters"
|
774
|
+
# YAML 2.0.8's format where hash instance variables weren't stored.
|
775
|
+
@parameters = coder.map.with_indifferent_access
|
776
|
+
@permitted = false
|
777
|
+
when "!ruby/hash-with-ivars:ActionController::Parameters"
|
778
|
+
# YAML 2.0.9's Hash subclass format where keys and values
|
779
|
+
# were stored under an elements hash and `permitted` within an ivars hash.
|
780
|
+
@parameters = coder.map["elements"].with_indifferent_access
|
781
|
+
@permitted = coder.map["ivars"][:@permitted]
|
782
|
+
when "!ruby/object:ActionController::Parameters"
|
783
|
+
# YAML's Object format. Only needed because of the format
|
784
|
+
# backwardscompability above, otherwise equivalent to YAML's initialization.
|
785
|
+
@parameters, @permitted = coder.map["parameters"], coder.map["permitted"]
|
786
|
+
end
|
787
|
+
end
|
788
|
+
|
789
|
+
# Returns duplicate of object including all parameters.
|
790
|
+
def deep_dup
|
791
|
+
self.class.new(@parameters.deep_dup).tap do |duplicate|
|
439
792
|
duplicate.permitted = @permitted
|
440
793
|
end
|
441
794
|
end
|
442
795
|
|
443
796
|
protected
|
797
|
+
attr_reader :parameters
|
798
|
+
|
444
799
|
def permitted=(new_permitted)
|
445
800
|
@permitted = new_permitted
|
446
801
|
end
|
447
802
|
|
803
|
+
def fields_for_style?
|
804
|
+
@parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
|
805
|
+
end
|
806
|
+
|
448
807
|
private
|
449
808
|
def new_instance_with_inherited_permitted_status(hash)
|
450
809
|
self.class.new(hash).tap do |new_instance|
|
@@ -452,40 +811,56 @@ module ActionController
|
|
452
811
|
end
|
453
812
|
end
|
454
813
|
|
455
|
-
def
|
814
|
+
def convert_parameters_to_hashes(value, using)
|
815
|
+
case value
|
816
|
+
when Array
|
817
|
+
value.map { |v| convert_parameters_to_hashes(v, using) }
|
818
|
+
when Hash
|
819
|
+
value.transform_values do |v|
|
820
|
+
convert_parameters_to_hashes(v, using)
|
821
|
+
end.with_indifferent_access
|
822
|
+
when Parameters
|
823
|
+
value.send(using)
|
824
|
+
else
|
825
|
+
value
|
826
|
+
end
|
827
|
+
end
|
828
|
+
|
829
|
+
def convert_hashes_to_parameters(key, value)
|
456
830
|
converted = convert_value_to_parameters(value)
|
457
|
-
|
831
|
+
@parameters[key] = converted unless converted.equal?(value)
|
458
832
|
converted
|
459
833
|
end
|
460
834
|
|
461
835
|
def convert_value_to_parameters(value)
|
462
|
-
|
836
|
+
case value
|
837
|
+
when Array
|
838
|
+
return value if converted_arrays.member?(value)
|
463
839
|
converted = value.map { |_| convert_value_to_parameters(_) }
|
464
840
|
converted_arrays << converted
|
465
841
|
converted
|
466
|
-
|
467
|
-
value
|
468
|
-
else
|
842
|
+
when Hash
|
469
843
|
self.class.new(value)
|
844
|
+
else
|
845
|
+
value
|
470
846
|
end
|
471
847
|
end
|
472
848
|
|
473
849
|
def each_element(object)
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
object.
|
479
|
-
|
480
|
-
|
481
|
-
|
850
|
+
case object
|
851
|
+
when Array
|
852
|
+
object.grep(Parameters).map { |el| yield el }.compact
|
853
|
+
when Parameters
|
854
|
+
if object.fields_for_style?
|
855
|
+
hash = object.class.new
|
856
|
+
object.each { |k, v| hash[k] = yield v }
|
857
|
+
hash
|
858
|
+
else
|
859
|
+
yield object
|
860
|
+
end
|
482
861
|
end
|
483
862
|
end
|
484
863
|
|
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
864
|
def unpermitted_parameters!(params)
|
490
865
|
unpermitted_keys = unpermitted_keys(params)
|
491
866
|
if unpermitted_keys.any?
|
@@ -500,7 +875,7 @@ module ActionController
|
|
500
875
|
end
|
501
876
|
|
502
877
|
def unpermitted_keys(params)
|
503
|
-
|
878
|
+
keys - params.keys - always_permitted_parameters
|
504
879
|
end
|
505
880
|
|
506
881
|
#
|
@@ -531,7 +906,7 @@ module ActionController
|
|
531
906
|
]
|
532
907
|
|
533
908
|
def permitted_scalar?(value)
|
534
|
-
PERMITTED_SCALAR_TYPES.any? {|type| value.is_a?(type)}
|
909
|
+
PERMITTED_SCALAR_TYPES.any? { |type| value.is_a?(type) }
|
535
910
|
end
|
536
911
|
|
537
912
|
def permitted_scalar_filter(params, key)
|
@@ -547,39 +922,80 @@ module ActionController
|
|
547
922
|
end
|
548
923
|
|
549
924
|
def array_of_permitted_scalars?(value)
|
550
|
-
if value.is_a?(Array)
|
551
|
-
value
|
925
|
+
if value.is_a?(Array) && value.all? { |element| permitted_scalar?(element) }
|
926
|
+
yield value
|
552
927
|
end
|
553
928
|
end
|
554
929
|
|
555
|
-
def
|
556
|
-
|
557
|
-
params[key] = self[key]
|
558
|
-
end
|
930
|
+
def non_scalar?(value)
|
931
|
+
value.is_a?(Array) || value.is_a?(Parameters)
|
559
932
|
end
|
560
933
|
|
561
934
|
EMPTY_ARRAY = []
|
935
|
+
EMPTY_HASH = {}
|
562
936
|
def hash_filter(params, filter)
|
563
937
|
filter = filter.with_indifferent_access
|
564
938
|
|
565
939
|
# Slicing filters out non-declared keys.
|
566
940
|
slice(*filter.keys).each do |key, value|
|
567
941
|
next unless value
|
942
|
+
next unless has_key? key
|
568
943
|
|
569
944
|
if filter[key] == EMPTY_ARRAY
|
570
945
|
# Declaration { comment_ids: [] }.
|
571
|
-
|
572
|
-
|
946
|
+
array_of_permitted_scalars?(self[key]) do |val|
|
947
|
+
params[key] = val
|
948
|
+
end
|
949
|
+
elsif filter[key] == EMPTY_HASH
|
950
|
+
# Declaration { preferences: {} }.
|
951
|
+
if value.is_a?(Parameters)
|
952
|
+
params[key] = permit_any_in_parameters(value)
|
953
|
+
end
|
954
|
+
elsif non_scalar?(value)
|
573
955
|
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
|
574
956
|
params[key] = each_element(value) do |element|
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
957
|
+
element.permit(*Array.wrap(filter[key]))
|
958
|
+
end
|
959
|
+
end
|
960
|
+
end
|
961
|
+
end
|
962
|
+
|
963
|
+
def permit_any_in_parameters(params)
|
964
|
+
self.class.new.tap do |sanitized|
|
965
|
+
params.each do |key, value|
|
966
|
+
case value
|
967
|
+
when ->(v) { permitted_scalar?(v) }
|
968
|
+
sanitized[key] = value
|
969
|
+
when Array
|
970
|
+
sanitized[key] = permit_any_in_array(value)
|
971
|
+
when Parameters
|
972
|
+
sanitized[key] = permit_any_in_parameters(value)
|
973
|
+
else
|
974
|
+
# Filter this one out.
|
579
975
|
end
|
580
976
|
end
|
581
977
|
end
|
582
978
|
end
|
979
|
+
|
980
|
+
def permit_any_in_array(array)
|
981
|
+
[].tap do |sanitized|
|
982
|
+
array.each do |element|
|
983
|
+
case element
|
984
|
+
when ->(e) { permitted_scalar?(e) }
|
985
|
+
sanitized << element
|
986
|
+
when Parameters
|
987
|
+
sanitized << permit_any_in_parameters(element)
|
988
|
+
else
|
989
|
+
# Filter this one out.
|
990
|
+
end
|
991
|
+
end
|
992
|
+
end
|
993
|
+
end
|
994
|
+
|
995
|
+
def initialize_copy(source)
|
996
|
+
super
|
997
|
+
@parameters = @parameters.dup
|
998
|
+
end
|
583
999
|
end
|
584
1000
|
|
585
1001
|
# == Strong \Parameters
|
@@ -590,12 +1006,12 @@ module ActionController
|
|
590
1006
|
# whitelisted.
|
591
1007
|
#
|
592
1008
|
# 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
|
1009
|
+
# predefined raise/rescue flow to end up as a <tt>400 Bad Request</tt> with no
|
594
1010
|
# effort.
|
595
1011
|
#
|
596
1012
|
# class PeopleController < ActionController::Base
|
597
1013
|
# # Using "Person.create(params[:person])" would raise an
|
598
|
-
# # ActiveModel::
|
1014
|
+
# # ActiveModel::ForbiddenAttributesError exception because it'd
|
599
1015
|
# # be using mass assignment without an explicit permit step.
|
600
1016
|
# # This is the recommended form:
|
601
1017
|
# def create
|
@@ -603,7 +1019,7 @@ module ActionController
|
|
603
1019
|
# end
|
604
1020
|
#
|
605
1021
|
# # This will pass with flying colors as long as there's a person key in the
|
606
|
-
# # parameters, otherwise it'll raise an ActionController::
|
1022
|
+
# # parameters, otherwise it'll raise an ActionController::ParameterMissing
|
607
1023
|
# # exception, which will get caught by ActionController::Base and turned
|
608
1024
|
# # into a 400 Bad Request reply.
|
609
1025
|
# def update
|
@@ -614,7 +1030,7 @@ module ActionController
|
|
614
1030
|
#
|
615
1031
|
# private
|
616
1032
|
# # Using a private method to encapsulate the permissible parameters is
|
617
|
-
# #
|
1033
|
+
# # a good pattern since you'll be able to reuse the same permit
|
618
1034
|
# # list between create and update. Also, you can specialize this method
|
619
1035
|
# # with per-user checking of permissible attributes.
|
620
1036
|
# def person_params
|
@@ -623,7 +1039,8 @@ module ActionController
|
|
623
1039
|
# end
|
624
1040
|
#
|
625
1041
|
# 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.
|
1042
|
+
# will need to specify which nested attributes should be whitelisted. You might want
|
1043
|
+
# to allow +:id+ and +:_destroy+, see ActiveRecord::NestedAttributes for more information.
|
627
1044
|
#
|
628
1045
|
# class Person
|
629
1046
|
# has_many :pets
|
@@ -643,7 +1060,7 @@ module ActionController
|
|
643
1060
|
# # It's mandatory to specify the nested attributes that should be whitelisted.
|
644
1061
|
# # If you use `permit` with just the key that points to the nested attributes hash,
|
645
1062
|
# # it will return an empty hash.
|
646
|
-
# params.require(:person).permit(:name, :age, pets_attributes: [ :name, :category ])
|
1063
|
+
# params.require(:person).permit(:name, :age, pets_attributes: [ :id, :name, :category ])
|
647
1064
|
# end
|
648
1065
|
# end
|
649
1066
|
#
|