actionpack 6.0.5 → 6.1.0.rc1
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 +235 -342
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/abstract_controller/base.rb +35 -2
- data/lib/abstract_controller/callbacks.rb +2 -2
- data/lib/abstract_controller/helpers.rb +105 -90
- data/lib/abstract_controller/rendering.rb +9 -9
- data/lib/abstract_controller/translation.rb +8 -2
- data/lib/abstract_controller.rb +1 -0
- data/lib/action_controller/api.rb +2 -2
- data/lib/action_controller/base.rb +4 -2
- data/lib/action_controller/caching.rb +0 -1
- data/lib/action_controller/log_subscriber.rb +3 -3
- data/lib/action_controller/metal/conditional_get.rb +10 -2
- data/lib/action_controller/metal/content_security_policy.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +1 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -4
- data/lib/action_controller/metal/exceptions.rb +33 -0
- data/lib/action_controller/metal/feature_policy.rb +46 -0
- data/lib/action_controller/metal/head.rb +7 -4
- data/lib/action_controller/metal/helpers.rb +11 -1
- data/lib/action_controller/metal/http_authentication.rb +5 -3
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +11 -9
- data/lib/action_controller/metal/live.rb +1 -1
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +6 -2
- data/lib/action_controller/metal/parameter_encoding.rb +35 -4
- data/lib/action_controller/metal/params_wrapper.rb +16 -11
- data/lib/action_controller/metal/redirecting.rb +1 -1
- data/lib/action_controller/metal/rendering.rb +6 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +1 -1
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +103 -15
- data/lib/action_controller/metal.rb +2 -2
- data/lib/action_controller/renderer.rb +23 -13
- data/lib/action_controller/test_case.rb +62 -56
- data/lib/action_controller.rb +2 -3
- data/lib/action_dispatch/http/cache.rb +12 -10
- data/lib/action_dispatch/http/content_security_policy.rb +11 -0
- data/lib/action_dispatch/http/feature_policy.rb +168 -0
- data/lib/action_dispatch/http/filter_parameters.rb +1 -1
- data/lib/action_dispatch/http/filter_redirect.rb +1 -1
- data/lib/action_dispatch/http/headers.rb +3 -2
- data/lib/action_dispatch/http/mime_negotiation.rb +14 -8
- data/lib/action_dispatch/http/mime_type.rb +29 -16
- data/lib/action_dispatch/http/parameters.rb +1 -19
- data/lib/action_dispatch/http/request.rb +24 -8
- data/lib/action_dispatch/http/response.rb +17 -16
- data/lib/action_dispatch/http/url.rb +3 -2
- data/lib/action_dispatch/journey/formatter.rb +53 -28
- data/lib/action_dispatch/journey/gtg/builder.rb +22 -36
- data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -4
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
- data/lib/action_dispatch/journey/nodes/node.rb +4 -3
- data/lib/action_dispatch/journey/parser.rb +13 -13
- data/lib/action_dispatch/journey/parser.y +1 -1
- data/lib/action_dispatch/journey/path/pattern.rb +13 -18
- data/lib/action_dispatch/journey/route.rb +7 -18
- data/lib/action_dispatch/journey/router/utils.rb +6 -4
- data/lib/action_dispatch/journey/router.rb +26 -30
- data/lib/action_dispatch/journey.rb +0 -2
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +1 -1
- data/lib/action_dispatch/middleware/cookies.rb +67 -32
- data/lib/action_dispatch/middleware/debug_exceptions.rb +8 -15
- data/lib/action_dispatch/middleware/debug_view.rb +1 -1
- data/lib/action_dispatch/middleware/exception_wrapper.rb +28 -16
- data/lib/action_dispatch/middleware/executor.rb +1 -1
- data/lib/action_dispatch/middleware/host_authorization.rb +35 -35
- data/lib/action_dispatch/middleware/remote_ip.rb +5 -4
- data/lib/action_dispatch/middleware/request_id.rb +4 -5
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -2
- data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
- data/lib/action_dispatch/middleware/ssl.rb +9 -6
- data/lib/action_dispatch/middleware/stack.rb +18 -0
- data/lib/action_dispatch/middleware/static.rb +154 -93
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +18 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +2 -5
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -3
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +88 -8
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +12 -1
- data/lib/action_dispatch/railtie.rb +3 -2
- data/lib/action_dispatch/request/session.rb +2 -8
- data/lib/action_dispatch/request/utils.rb +26 -2
- data/lib/action_dispatch/routing/inspector.rb +8 -7
- data/lib/action_dispatch/routing/mapper.rb +102 -71
- data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -19
- data/lib/action_dispatch/routing/redirection.rb +3 -3
- data/lib/action_dispatch/routing/route_set.rb +49 -41
- data/lib/action_dispatch/system_test_case.rb +29 -24
- data/lib/action_dispatch/system_testing/browser.rb +33 -27
- data/lib/action_dispatch/system_testing/driver.rb +6 -7
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +47 -6
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +4 -7
- data/lib/action_dispatch/testing/assertions/response.rb +2 -4
- data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +38 -27
- data/lib/action_dispatch/testing/test_process.rb +29 -4
- data/lib/action_dispatch/testing/test_request.rb +3 -3
- data/lib/action_dispatch.rb +3 -2
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +23 -25
- data/lib/action_controller/metal/force_ssl.rb +0 -58
- data/lib/action_dispatch/http/parameter_filter.rb +0 -12
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -119
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController #:nodoc:
|
4
|
+
# HTTP Feature Policy is a web standard for defining a mechanism to
|
5
|
+
# allow and deny the use of browser features in its own context, and
|
6
|
+
# in content within any <iframe> elements in the document.
|
7
|
+
#
|
8
|
+
# Full details of HTTP Feature Policy specification and guidelines can
|
9
|
+
# be found at MDN:
|
10
|
+
#
|
11
|
+
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
|
12
|
+
#
|
13
|
+
# Examples of usage:
|
14
|
+
#
|
15
|
+
# # Global policy
|
16
|
+
# Rails.application.config.feature_policy do |f|
|
17
|
+
# f.camera :none
|
18
|
+
# f.gyroscope :none
|
19
|
+
# f.microphone :none
|
20
|
+
# f.usb :none
|
21
|
+
# f.fullscreen :self
|
22
|
+
# f.payment :self, "https://secure.example.com"
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # Controller level policy
|
26
|
+
# class PagesController < ApplicationController
|
27
|
+
# feature_policy do |p|
|
28
|
+
# p.geolocation "https://example.com"
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
module FeaturePolicy
|
32
|
+
extend ActiveSupport::Concern
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
def feature_policy(**options, &block)
|
36
|
+
before_action(options) do
|
37
|
+
if block_given?
|
38
|
+
policy = request.feature_policy.clone
|
39
|
+
yield policy
|
40
|
+
request.feature_policy = policy
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -29,19 +29,22 @@ module ActionController
|
|
29
29
|
content_type = options.delete(:content_type)
|
30
30
|
|
31
31
|
options.each do |key, value|
|
32
|
-
headers[key.to_s.
|
32
|
+
headers[key.to_s.split(/[-_]/).each { |v| v[0] = v[0].upcase }.join("-")] = value.to_s
|
33
33
|
end
|
34
34
|
|
35
35
|
self.status = status
|
36
36
|
self.location = url_for(location) if location
|
37
37
|
|
38
|
-
self.response_body = ""
|
39
|
-
|
40
38
|
if include_content?(response_code)
|
41
|
-
self.
|
39
|
+
unless self.media_type
|
40
|
+
self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
|
41
|
+
end
|
42
|
+
|
42
43
|
response.charset = false
|
43
44
|
end
|
44
45
|
|
46
|
+
self.response_body = ""
|
47
|
+
|
45
48
|
true
|
46
49
|
end
|
47
50
|
|
@@ -11,7 +11,12 @@ module ActionController
|
|
11
11
|
#
|
12
12
|
# In previous versions of \Rails the controller will include a helper which
|
13
13
|
# matches the name of the controller, e.g., <tt>MyController</tt> will automatically
|
14
|
-
# include <tt>MyHelper</tt>.
|
14
|
+
# include <tt>MyHelper</tt>. You can revert to the old behavior with the following:
|
15
|
+
#
|
16
|
+
# # config/application.rb
|
17
|
+
# class Application < Rails::Application
|
18
|
+
# config.action_controller.include_all_helpers = false
|
19
|
+
# end
|
15
20
|
#
|
16
21
|
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
|
17
22
|
# controller which inherits from it.
|
@@ -73,6 +78,11 @@ module ActionController
|
|
73
78
|
end
|
74
79
|
|
75
80
|
# Provides a proxy to access helper methods from outside the view.
|
81
|
+
#
|
82
|
+
# Note that the proxy is rendered under a different view context.
|
83
|
+
# This may cause incorrect behaviour with capture methods. Consider
|
84
|
+
# using {helper}[rdoc-ref:AbstractController::Helpers::ClassMethods#helper]
|
85
|
+
# instead when using +capture+.
|
76
86
|
def helpers
|
77
87
|
@helper_proxy ||= begin
|
78
88
|
proxy = ActionView::Base.empty
|
@@ -76,6 +76,8 @@ module ActionController
|
|
76
76
|
|
77
77
|
def http_basic_authenticate_or_request_with(name:, password:, realm: nil, message: nil)
|
78
78
|
authenticate_or_request_with_http_basic(realm, message) do |given_name, given_password|
|
79
|
+
# This comparison uses & so that it doesn't short circuit and
|
80
|
+
# uses `secure_compare` so that length information isn't leaked.
|
79
81
|
ActiveSupport::SecurityUtils.secure_compare(given_name, name) &
|
80
82
|
ActiveSupport::SecurityUtils.secure_compare(given_password, password)
|
81
83
|
end
|
@@ -136,7 +138,7 @@ module ActionController
|
|
136
138
|
#
|
137
139
|
# === Simple \Digest example
|
138
140
|
#
|
139
|
-
# require
|
141
|
+
# require "digest/md5"
|
140
142
|
# class PostsController < ApplicationController
|
141
143
|
# REALM = "SuperSecret"
|
142
144
|
# USERS = {"dhh" => "secret", #plain text password
|
@@ -405,7 +407,7 @@ module ActionController
|
|
405
407
|
module Token
|
406
408
|
TOKEN_KEY = "token="
|
407
409
|
TOKEN_REGEX = /^(Token|Bearer)\s+/
|
408
|
-
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t)/
|
410
|
+
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
|
409
411
|
extend self
|
410
412
|
|
411
413
|
module ControllerMethods
|
@@ -482,7 +484,7 @@ module ActionController
|
|
482
484
|
def raw_params(auth)
|
483
485
|
_raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
|
484
486
|
|
485
|
-
if !
|
487
|
+
if !_raw_params.first.start_with?(TOKEN_KEY)
|
486
488
|
_raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
|
487
489
|
end
|
488
490
|
|
@@ -22,7 +22,7 @@ module ActionController
|
|
22
22
|
# Third, if we DON'T find a template AND the request is a page load in a web
|
23
23
|
# browser (technically, a non-XHR GET request for an HTML response) where
|
24
24
|
# you reasonably expect to have rendered a template, then we raise
|
25
|
-
# <tt>
|
25
|
+
# <tt>ActionController::MissingExactTemplate</tt> with an explanation.
|
26
26
|
#
|
27
27
|
# Finally, if we DON'T find a template AND the request isn't a browser page
|
28
28
|
# load, then we implicitly respond with <tt>204 No Content</tt>.
|
@@ -16,10 +16,11 @@ module ActionController
|
|
16
16
|
|
17
17
|
attr_internal :view_runtime
|
18
18
|
|
19
|
-
def process_action(*
|
19
|
+
def process_action(*)
|
20
20
|
raw_payload = {
|
21
21
|
controller: self.class.name,
|
22
22
|
action: action_name,
|
23
|
+
request: request,
|
23
24
|
params: request.filtered_parameters,
|
24
25
|
headers: request.headers,
|
25
26
|
format: request.format.ref,
|
@@ -27,18 +28,19 @@ module ActionController
|
|
27
28
|
path: request.fullpath
|
28
29
|
}
|
29
30
|
|
30
|
-
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload
|
31
|
+
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload)
|
31
32
|
|
32
33
|
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
33
|
-
super
|
34
|
-
|
35
|
-
|
34
|
+
result = super
|
35
|
+
payload[:response] = response
|
36
|
+
payload[:status] = response.status
|
37
|
+
result
|
36
38
|
ensure
|
37
39
|
append_info_to_payload(payload)
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
def render(*
|
43
|
+
def render(*)
|
42
44
|
render_output = nil
|
43
45
|
self.view_runtime = cleanup_view_runtime do
|
44
46
|
Benchmark.ms { render_output = super }
|
@@ -59,8 +61,8 @@ module ActionController
|
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
62
|
-
def redirect_to(*
|
63
|
-
ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
|
64
|
+
def redirect_to(*)
|
65
|
+
ActiveSupport::Notifications.instrument("redirect_to.action_controller", request: request) do |payload|
|
64
66
|
result = super
|
65
67
|
payload[:status] = response.status
|
66
68
|
payload[:location] = response.filtered_location
|
@@ -70,7 +72,7 @@ module ActionController
|
|
70
72
|
|
71
73
|
private
|
72
74
|
# A hook invoked every time a before callback is halted.
|
73
|
-
def halted_callback_hook(filter)
|
75
|
+
def halted_callback_hook(filter, _)
|
74
76
|
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
|
75
77
|
end
|
76
78
|
|
@@ -136,11 +136,11 @@ module ActionController
|
|
136
136
|
attr_accessor :ignore_disconnect
|
137
137
|
|
138
138
|
def initialize(response)
|
139
|
+
super(response, SizedQueue.new(10))
|
139
140
|
@error_callback = lambda { true }
|
140
141
|
@cv = new_cond
|
141
142
|
@aborted = false
|
142
143
|
@ignore_disconnect = false
|
143
|
-
super(response, SizedQueue.new(10))
|
144
144
|
end
|
145
145
|
|
146
146
|
def write(string)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
module Logging
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Set a different log level per request.
|
9
|
+
#
|
10
|
+
# # Use the debug log level if a particular cookie is set.
|
11
|
+
# class ApplicationController < ActionController::Base
|
12
|
+
# log_at :debug, if: -> { cookies[:debug] }
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
def log_at(level, **options)
|
16
|
+
around_action ->(_, action) { logger.log_at(level, &action) }, **options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -142,7 +142,7 @@ module ActionController #:nodoc:
|
|
142
142
|
#
|
143
143
|
# You can set the variant in a +before_action+:
|
144
144
|
#
|
145
|
-
# request.variant = :tablet if request.user_agent
|
145
|
+
# request.variant = :tablet if /iPad/.match?(request.user_agent)
|
146
146
|
#
|
147
147
|
# Respond to variants in the action just like you respond to formats:
|
148
148
|
#
|
@@ -209,7 +209,7 @@ module ActionController #:nodoc:
|
|
209
209
|
raise ActionController::RespondToMismatchError
|
210
210
|
end
|
211
211
|
_process_format(format)
|
212
|
-
_set_rendered_content_type
|
212
|
+
_set_rendered_content_type(format) unless collector.any_response?
|
213
213
|
response = collector.response
|
214
214
|
response.call if response
|
215
215
|
else
|
@@ -268,6 +268,10 @@ module ActionController #:nodoc:
|
|
268
268
|
end
|
269
269
|
end
|
270
270
|
|
271
|
+
def any_response?
|
272
|
+
!@responses.fetch(format, false) && @responses[Mime::ALL]
|
273
|
+
end
|
274
|
+
|
271
275
|
def response
|
272
276
|
response = @responses.fetch(format, @responses[Mime::ALL])
|
273
277
|
if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
|
@@ -12,11 +12,13 @@ module ActionController
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def setup_param_encode # :nodoc:
|
15
|
-
@_parameter_encodings = {}
|
15
|
+
@_parameter_encodings = Hash.new { |h, k| h[k] = {} }
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
@_parameter_encodings
|
18
|
+
def action_encoding_template(action) # :nodoc:
|
19
|
+
if @_parameter_encodings.has_key?(action.to_s)
|
20
|
+
@_parameter_encodings[action.to_s]
|
21
|
+
end
|
20
22
|
end
|
21
23
|
|
22
24
|
# Specify that a given action's parameters should all be encoded as
|
@@ -44,7 +46,36 @@ module ActionController
|
|
44
46
|
# encoded as ASCII-8BIT. This is useful in the case where an application
|
45
47
|
# must handle data but encoding of the data is unknown, like file system data.
|
46
48
|
def skip_parameter_encoding(action)
|
47
|
-
@_parameter_encodings[action.to_s] =
|
49
|
+
@_parameter_encodings[action.to_s] = Hash.new { Encoding::ASCII_8BIT }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Specify the encoding for a parameter on an action.
|
53
|
+
# If not specified the default is UTF-8.
|
54
|
+
#
|
55
|
+
# You can specify a binary (ASCII_8BIT) parameter with:
|
56
|
+
#
|
57
|
+
# class RepositoryController < ActionController::Base
|
58
|
+
# # This specifies that file_path is not UTF-8 and is instead ASCII_8BIT
|
59
|
+
# param_encoding :show, :file_path, Encoding::ASCII_8BIT
|
60
|
+
#
|
61
|
+
# def show
|
62
|
+
# @repo = Repository.find_by_filesystem_path params[:file_path]
|
63
|
+
#
|
64
|
+
# # params[:repo_name] remains UTF-8 encoded
|
65
|
+
# @repo_name = params[:repo_name]
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# def index
|
69
|
+
# @repositories = Repository.all
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# The file_path parameter on the show action would be encoded as ASCII-8BIT,
|
74
|
+
# but all other arguments will remain UTF-8 encoded.
|
75
|
+
# This is useful in the case where an application must handle data
|
76
|
+
# but encoding of the data is unknown, like file system data.
|
77
|
+
def param_encoding(action, param, encoding)
|
78
|
+
@_parameter_encodings[action.to_s][param.to_s] = encoding
|
48
79
|
end
|
49
80
|
end
|
50
81
|
end
|
@@ -107,10 +107,14 @@ module ActionController
|
|
107
107
|
|
108
108
|
unless super || exclude
|
109
109
|
if m.respond_to?(:attribute_names) && m.attribute_names.any?
|
110
|
+
self.include = m.attribute_names
|
111
|
+
|
110
112
|
if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
|
111
|
-
self.include
|
112
|
-
|
113
|
-
|
113
|
+
self.include += m.stored_attributes.values.flatten.map(&:to_s)
|
114
|
+
end
|
115
|
+
|
116
|
+
if m.respond_to?(:attribute_aliases) && m.attribute_aliases.any?
|
117
|
+
self.include += m.attribute_aliases.keys
|
114
118
|
end
|
115
119
|
|
116
120
|
if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
|
@@ -151,7 +155,7 @@ module ActionController
|
|
151
155
|
# try to find Foo::Bar::User, Foo::User and finally User.
|
152
156
|
def _default_wrap_model
|
153
157
|
return nil if klass.anonymous?
|
154
|
-
model_name = klass.name.
|
158
|
+
model_name = klass.name.delete_suffix("Controller").classify
|
155
159
|
|
156
160
|
begin
|
157
161
|
if model_klass = model_name.safe_constantize
|
@@ -189,7 +193,7 @@ module ActionController
|
|
189
193
|
#
|
190
194
|
# wrap_parameters Person
|
191
195
|
# # wraps parameters by determining the wrapper key from Person class
|
192
|
-
# (+person+, in this case) and the list of attribute names
|
196
|
+
# # (+person+, in this case) and the list of attribute names
|
193
197
|
#
|
194
198
|
# wrap_parameters include: [:username, :title]
|
195
199
|
# # wraps only +:username+ and +:title+ attributes from parameters.
|
@@ -240,7 +244,7 @@ module ActionController
|
|
240
244
|
|
241
245
|
# Performs parameters wrapping upon the request. Called automatically
|
242
246
|
# by the metal call stack.
|
243
|
-
def process_action(*
|
247
|
+
def process_action(*)
|
244
248
|
_perform_parameter_wrapping if _wrapper_enabled?
|
245
249
|
super
|
246
250
|
end
|
@@ -264,9 +268,11 @@ module ActionController
|
|
264
268
|
def _extract_parameters(parameters)
|
265
269
|
if include_only = _wrapper_options.include
|
266
270
|
parameters.slice(*include_only)
|
271
|
+
elsif _wrapper_options.exclude
|
272
|
+
exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
|
273
|
+
parameters.except(*exclude)
|
267
274
|
else
|
268
|
-
|
269
|
-
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
|
275
|
+
parameters.except(*EXCLUDE_PARAMETERS)
|
270
276
|
end
|
271
277
|
end
|
272
278
|
|
@@ -275,10 +281,7 @@ module ActionController
|
|
275
281
|
return false unless request.has_content_type?
|
276
282
|
|
277
283
|
ref = request.content_mime_type.ref
|
278
|
-
|
279
284
|
_wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
|
280
|
-
rescue ActionDispatch::Http::Parameters::ParseError
|
281
|
-
false
|
282
285
|
end
|
283
286
|
|
284
287
|
def _perform_parameter_wrapping
|
@@ -292,6 +295,8 @@ module ActionController
|
|
292
295
|
|
293
296
|
# This will display the wrapped hash in the log file.
|
294
297
|
request.filtered_parameters.merge! wrapped_filtered_hash
|
298
|
+
rescue ActionDispatch::Http::Parameters::ParseError
|
299
|
+
# swallow parse error exception
|
295
300
|
end
|
296
301
|
end
|
297
302
|
end
|
@@ -85,7 +85,7 @@ module ActionController
|
|
85
85
|
# * <tt>:fallback_location</tt> - The default fallback location that will be used on missing +Referer+ header.
|
86
86
|
# * <tt>:allow_other_host</tt> - Allow or disallow redirection to the host that is different to the current host, defaults to true.
|
87
87
|
#
|
88
|
-
# All other options that can be passed to
|
88
|
+
# All other options that can be passed to #redirect_to are accepted as
|
89
89
|
# options and the behavior is identical.
|
90
90
|
def redirect_back(fallback_location:, allow_other_host: true, **args)
|
91
91
|
referer = request.headers["Referer"]
|
@@ -77,6 +77,12 @@ module ActionController
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
+
def _set_vary_header
|
81
|
+
if self.headers["Vary"].blank? && request.should_apply_vary_header?
|
82
|
+
self.headers["Vary"] = "Accept"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
80
86
|
# Normalize arguments by catching blocks and setting them on :update.
|
81
87
|
def _normalize_args(action = nil, options = {}, &blk)
|
82
88
|
options = super
|
@@ -19,12 +19,36 @@ module ActionController
|
|
19
19
|
# params.require(:a)
|
20
20
|
# # => ActionController::ParameterMissing: param is missing or the value is empty: a
|
21
21
|
class ParameterMissing < KeyError
|
22
|
-
attr_reader :param # :nodoc:
|
22
|
+
attr_reader :param, :keys # :nodoc:
|
23
23
|
|
24
|
-
def initialize(param) # :nodoc:
|
24
|
+
def initialize(param, keys = nil) # :nodoc:
|
25
25
|
@param = param
|
26
|
+
@keys = keys
|
26
27
|
super("param is missing or the value is empty: #{param}")
|
27
28
|
end
|
29
|
+
|
30
|
+
class Correction
|
31
|
+
def initialize(error)
|
32
|
+
@error = error
|
33
|
+
end
|
34
|
+
|
35
|
+
def corrections
|
36
|
+
if @error.param && @error.keys
|
37
|
+
maybe_these = @error.keys
|
38
|
+
|
39
|
+
maybe_these.sort_by { |n|
|
40
|
+
DidYouMean::Jaro.distance(@error.param.to_s, n)
|
41
|
+
}.reverse.first(4)
|
42
|
+
else
|
43
|
+
[]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# We may not have DYM, and DYM might not let us register error handlers
|
49
|
+
if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
|
50
|
+
DidYouMean.correct_error(self, Correction)
|
51
|
+
end
|
28
52
|
end
|
29
53
|
|
30
54
|
# Raised when a supplied parameter is not expected and
|
@@ -180,6 +204,14 @@ module ActionController
|
|
180
204
|
#
|
181
205
|
# Returns true if the given key is present in the parameters.
|
182
206
|
|
207
|
+
##
|
208
|
+
# :method: member?
|
209
|
+
#
|
210
|
+
# :call-seq:
|
211
|
+
# member?(key)
|
212
|
+
#
|
213
|
+
# Returns true if the given key is present in the parameters.
|
214
|
+
|
183
215
|
##
|
184
216
|
# :method: keys
|
185
217
|
#
|
@@ -211,7 +243,7 @@ module ActionController
|
|
211
243
|
# values()
|
212
244
|
#
|
213
245
|
# Returns a new array of the values of the parameters.
|
214
|
-
delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
|
246
|
+
delegate :keys, :key?, :has_key?, :member?, :values, :has_value?, :value?, :empty?, :include?,
|
215
247
|
:as_json, :to_s, :each_key, to: :@parameters
|
216
248
|
|
217
249
|
# By default, never raise an UnpermittedParameters exception if these
|
@@ -220,9 +252,15 @@ module ActionController
|
|
220
252
|
# to change these is to specify `always_permitted_parameters` in your
|
221
253
|
# config. For instance:
|
222
254
|
#
|
223
|
-
# config.always_permitted_parameters = %w( controller action format )
|
255
|
+
# config.action_controller.always_permitted_parameters = %w( controller action format )
|
224
256
|
cattr_accessor :always_permitted_parameters, default: %w( controller action )
|
225
257
|
|
258
|
+
class << self
|
259
|
+
def nested_attribute?(key, value) # :nodoc:
|
260
|
+
/\A-?\d+\z/.match?(key) && (value.is_a?(Hash) || value.is_a?(Parameters))
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
226
264
|
# Returns a new instance of <tt>ActionController::Parameters</tt>.
|
227
265
|
# Also, sets the +permitted+ attribute to the default value of
|
228
266
|
# <tt>ActionController::Parameters.permit_all_parameters</tt>.
|
@@ -253,6 +291,11 @@ module ActionController
|
|
253
291
|
@parameters == other
|
254
292
|
end
|
255
293
|
end
|
294
|
+
alias eql? ==
|
295
|
+
|
296
|
+
def hash
|
297
|
+
[@parameters.hash, @permitted].hash
|
298
|
+
end
|
256
299
|
|
257
300
|
# Returns a safe <tt>ActiveSupport::HashWithIndifferentAccess</tt>
|
258
301
|
# representation of the parameters with all unpermitted keys removed.
|
@@ -341,6 +384,7 @@ module ActionController
|
|
341
384
|
# Convert all hashes in values into parameters, then yield each pair in
|
342
385
|
# the same way as <tt>Hash#each_pair</tt>.
|
343
386
|
def each_pair(&block)
|
387
|
+
return to_enum(__callee__) unless block_given?
|
344
388
|
@parameters.each_pair do |key, value|
|
345
389
|
yield [key, convert_hashes_to_parameters(key, value)]
|
346
390
|
end
|
@@ -352,6 +396,7 @@ module ActionController
|
|
352
396
|
# Convert all hashes in values into parameters, then yield each value in
|
353
397
|
# the same way as <tt>Hash#each_value</tt>.
|
354
398
|
def each_value(&block)
|
399
|
+
return to_enum(:each_value) unless block_given?
|
355
400
|
@parameters.each_pair do |key, value|
|
356
401
|
yield convert_hashes_to_parameters(key, value)
|
357
402
|
end
|
@@ -459,7 +504,7 @@ module ActionController
|
|
459
504
|
if value.present? || value == false
|
460
505
|
value
|
461
506
|
else
|
462
|
-
raise ParameterMissing.new(key)
|
507
|
+
raise ParameterMissing.new(key, @parameters.keys)
|
463
508
|
end
|
464
509
|
end
|
465
510
|
|
@@ -596,7 +641,7 @@ module ActionController
|
|
596
641
|
if block_given?
|
597
642
|
yield
|
598
643
|
else
|
599
|
-
args.fetch(0) { raise ActionController::ParameterMissing.new(key) }
|
644
|
+
args.fetch(0) { raise ActionController::ParameterMissing.new(key, @parameters.keys) }
|
600
645
|
end
|
601
646
|
}
|
602
647
|
)
|
@@ -691,6 +736,23 @@ module ActionController
|
|
691
736
|
self
|
692
737
|
end
|
693
738
|
|
739
|
+
# Returns a new <tt>ActionController::Parameters</tt> instance with the
|
740
|
+
# results of running +block+ once for every key. This includes the keys
|
741
|
+
# from the root hash and from all nested hashes and arrays. The values are unchanged.
|
742
|
+
def deep_transform_keys(&block)
|
743
|
+
new_instance_with_inherited_permitted_status(
|
744
|
+
@parameters.deep_transform_keys(&block)
|
745
|
+
)
|
746
|
+
end
|
747
|
+
|
748
|
+
# Returns the <tt>ActionController::Parameters</tt> instance changing its keys.
|
749
|
+
# This includes the keys from the root hash and from all nested hashes and arrays.
|
750
|
+
# The values are unchanged.
|
751
|
+
def deep_transform_keys!(&block)
|
752
|
+
@parameters.deep_transform_keys!(&block)
|
753
|
+
self
|
754
|
+
end
|
755
|
+
|
694
756
|
# Deletes a key-value pair from +Parameters+ and returns the value. If
|
695
757
|
# +key+ is not found, returns +nil+ (or, with optional code block, yields
|
696
758
|
# +key+ and returns the result). Cf. +#extract!+, which returns the
|
@@ -725,6 +787,28 @@ module ActionController
|
|
725
787
|
end
|
726
788
|
alias_method :delete_if, :reject!
|
727
789
|
|
790
|
+
# Returns a new instance of <tt>ActionController::Parameters</tt> with +nil+ values removed.
|
791
|
+
def compact
|
792
|
+
new_instance_with_inherited_permitted_status(@parameters.compact)
|
793
|
+
end
|
794
|
+
|
795
|
+
# Removes all +nil+ values in place and returns +self+, or +nil+ if no changes were made.
|
796
|
+
def compact!
|
797
|
+
self if @parameters.compact!
|
798
|
+
end
|
799
|
+
|
800
|
+
# Returns a new instance of <tt>ActionController::Parameters</tt> without the blank values.
|
801
|
+
# Uses Object#blank? for determining if a value is blank.
|
802
|
+
def compact_blank
|
803
|
+
reject { |_k, v| v.blank? }
|
804
|
+
end
|
805
|
+
|
806
|
+
# Removes all blank values in place and returns self.
|
807
|
+
# Uses Object#blank? for determining if a value is blank.
|
808
|
+
def compact_blank!
|
809
|
+
reject! { |_k, v| v.blank? }
|
810
|
+
end
|
811
|
+
|
728
812
|
# Returns values that were assigned to the given +keys+. Note that all the
|
729
813
|
# +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
|
730
814
|
def values_at(*keys)
|
@@ -771,7 +855,7 @@ module ActionController
|
|
771
855
|
end
|
772
856
|
|
773
857
|
def inspect
|
774
|
-
"
|
858
|
+
"#<#{self.class} #{@parameters} permitted: #{@permitted}>"
|
775
859
|
end
|
776
860
|
|
777
861
|
def self.hook_into_yaml_loading # :nodoc:
|
@@ -813,8 +897,14 @@ module ActionController
|
|
813
897
|
|
814
898
|
attr_writer :permitted
|
815
899
|
|
816
|
-
def
|
817
|
-
@parameters.
|
900
|
+
def nested_attributes?
|
901
|
+
@parameters.any? { |k, v| Parameters.nested_attribute?(k, v) }
|
902
|
+
end
|
903
|
+
|
904
|
+
def each_nested_attribute
|
905
|
+
hash = self.class.new
|
906
|
+
self.each { |k, v| hash[k] = yield v if Parameters.nested_attribute?(k, v) }
|
907
|
+
hash
|
818
908
|
end
|
819
909
|
|
820
910
|
private
|
@@ -859,15 +949,13 @@ module ActionController
|
|
859
949
|
end
|
860
950
|
end
|
861
951
|
|
862
|
-
def each_element(object)
|
952
|
+
def each_element(object, &block)
|
863
953
|
case object
|
864
954
|
when Array
|
865
955
|
object.grep(Parameters).map { |el| yield el }.compact
|
866
956
|
when Parameters
|
867
|
-
if object.
|
868
|
-
|
869
|
-
object.each { |k, v| hash[k] = yield v }
|
870
|
-
hash
|
957
|
+
if object.nested_attributes?
|
958
|
+
object.each_nested_attribute(&block)
|
871
959
|
else
|
872
960
|
yield object
|
873
961
|
end
|
@@ -895,7 +983,7 @@ module ActionController
|
|
895
983
|
# --- Filtering ----------------------------------------------------------
|
896
984
|
#
|
897
985
|
|
898
|
-
# This is a
|
986
|
+
# This is a list of permitted scalar types that includes the ones
|
899
987
|
# supported in XML and JSON requests.
|
900
988
|
#
|
901
989
|
# This list is in particular used to filter ordinary requests, String goes
|