actionpack 7.2.2.1 → 8.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +228 -101
- data/README.rdoc +1 -1
- data/lib/abstract_controller/base.rb +1 -12
- data/lib/abstract_controller/collector.rb +1 -1
- data/lib/abstract_controller/helpers.rb +1 -1
- data/lib/abstract_controller/rendering.rb +0 -1
- data/lib/action_controller/base.rb +1 -1
- data/lib/action_controller/form_builder.rb +3 -3
- data/lib/action_controller/metal/allow_browser.rb +11 -1
- data/lib/action_controller/metal/conditional_get.rb +5 -1
- data/lib/action_controller/metal/data_streaming.rb +4 -2
- data/lib/action_controller/metal/instrumentation.rb +1 -2
- data/lib/action_controller/metal/live.rb +59 -11
- data/lib/action_controller/metal/params_wrapper.rb +3 -3
- data/lib/action_controller/metal/rate_limiting.rb +13 -4
- data/lib/action_controller/metal/redirecting.rb +4 -3
- data/lib/action_controller/metal/renderers.rb +2 -3
- data/lib/action_controller/metal/rendering.rb +1 -1
- data/lib/action_controller/metal/request_forgery_protection.rb +3 -1
- data/lib/action_controller/metal/streaming.rb +5 -84
- data/lib/action_controller/metal/strong_parameters.rb +277 -92
- data/lib/action_controller/railtie.rb +6 -7
- data/lib/action_controller/renderer.rb +0 -1
- data/lib/action_controller/test_case.rb +12 -2
- data/lib/action_dispatch/constants.rb +6 -0
- data/lib/action_dispatch/http/cache.rb +27 -10
- data/lib/action_dispatch/http/content_security_policy.rb +14 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +8 -3
- data/lib/action_dispatch/http/param_builder.rb +186 -0
- data/lib/action_dispatch/http/param_error.rb +26 -0
- data/lib/action_dispatch/http/permissions_policy.rb +2 -0
- data/lib/action_dispatch/http/query_parser.rb +53 -0
- data/lib/action_dispatch/http/request.rb +64 -19
- data/lib/action_dispatch/http/response.rb +49 -14
- data/lib/action_dispatch/http/url.rb +2 -2
- data/lib/action_dispatch/journey/formatter.rb +8 -3
- data/lib/action_dispatch/journey/gtg/transition_table.rb +4 -4
- data/lib/action_dispatch/journey/parser.rb +99 -196
- data/lib/action_dispatch/journey/scanner.rb +44 -42
- data/lib/action_dispatch/middleware/cookies.rb +4 -2
- data/lib/action_dispatch/middleware/debug_exceptions.rb +19 -4
- data/lib/action_dispatch/middleware/debug_view.rb +0 -5
- data/lib/action_dispatch/middleware/exception_wrapper.rb +3 -9
- data/lib/action_dispatch/middleware/executor.rb +5 -2
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -1
- data/lib/action_dispatch/middleware/request_id.rb +2 -1
- data/lib/action_dispatch/middleware/ssl.rb +13 -3
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +0 -3
- data/lib/action_dispatch/railtie.rb +8 -0
- data/lib/action_dispatch/request/session.rb +1 -0
- data/lib/action_dispatch/request/utils.rb +9 -3
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +96 -67
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -2
- data/lib/action_dispatch/routing/route_set.rb +21 -10
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -0
- data/lib/action_dispatch/system_testing/browser.rb +12 -21
- data/lib/action_dispatch/testing/assertion_response.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +12 -2
- data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
- data/lib/action_dispatch/testing/integration.rb +20 -10
- data/lib/action_dispatch/testing/request_encoder.rb +9 -9
- data/lib/action_dispatch/testing/test_process.rb +1 -2
- data/lib/action_dispatch.rb +6 -4
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +16 -38
- data/lib/action_dispatch/journey/parser.y +0 -50
- data/lib/action_dispatch/journey/parser_extras.rb +0 -33
|
@@ -29,6 +29,10 @@ module ActionDispatch
|
|
|
29
29
|
config.action_dispatch.request_id_header = ActionDispatch::Constants::X_REQUEST_ID
|
|
30
30
|
config.action_dispatch.log_rescued_responses = true
|
|
31
31
|
config.action_dispatch.debug_exception_log_level = :fatal
|
|
32
|
+
config.action_dispatch.strict_freshness = false
|
|
33
|
+
|
|
34
|
+
config.action_dispatch.ignore_leading_brackets = nil
|
|
35
|
+
config.action_dispatch.strict_query_string_separator = nil
|
|
32
36
|
|
|
33
37
|
config.action_dispatch.default_headers = {
|
|
34
38
|
"X-Frame-Options" => "SAMEORIGIN",
|
|
@@ -51,6 +55,9 @@ module ActionDispatch
|
|
|
51
55
|
ActionDispatch::Http::URL.secure_protocol = app.config.force_ssl
|
|
52
56
|
ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
|
|
53
57
|
|
|
58
|
+
ActionDispatch::ParamBuilder.ignore_leading_brackets = app.config.action_dispatch.ignore_leading_brackets
|
|
59
|
+
ActionDispatch::QueryParser.strict_query_string_separator = app.config.action_dispatch.strict_query_string_separator
|
|
60
|
+
|
|
54
61
|
ActiveSupport.on_load(:action_dispatch_request) do
|
|
55
62
|
self.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
|
|
56
63
|
ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge
|
|
@@ -69,6 +76,7 @@ module ActionDispatch
|
|
|
69
76
|
|
|
70
77
|
ActionDispatch::Routing::Mapper.route_source_locations = Rails.env.development?
|
|
71
78
|
|
|
79
|
+
ActionDispatch::Http::Cache::Request.strict_freshness = app.config.action_dispatch.strict_freshness
|
|
72
80
|
ActionDispatch.test_app = app
|
|
73
81
|
end
|
|
74
82
|
end
|
|
@@ -83,8 +83,8 @@ module ActionDispatch
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
class CustomParamEncoder # :nodoc:
|
|
86
|
-
def self.
|
|
87
|
-
return params unless
|
|
86
|
+
def self.encode_for_template(params, encoding_template)
|
|
87
|
+
return params unless encoding_template
|
|
88
88
|
params.except(:controller, :action).each do |key, value|
|
|
89
89
|
ActionDispatch::Request::Utils.each_param_value(value) do |param|
|
|
90
90
|
# If `param` is frozen, it comes from the router defaults
|
|
@@ -98,8 +98,14 @@ module ActionDispatch
|
|
|
98
98
|
params
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
def self.encode(request, params, controller, action)
|
|
102
|
+
encoding_template = action_encoding_template(request, controller, action)
|
|
103
|
+
encode_for_template(params, encoding_template)
|
|
104
|
+
end
|
|
105
|
+
|
|
101
106
|
def self.action_encoding_template(request, controller, action) # :nodoc:
|
|
102
|
-
|
|
107
|
+
controller && controller.valid_encoding? &&
|
|
108
|
+
request.controller_class_for(controller).action_encoding_template(action)
|
|
103
109
|
rescue MissingController
|
|
104
110
|
nil
|
|
105
111
|
end
|
|
@@ -101,7 +101,7 @@ module ActionDispatch
|
|
|
101
101
|
{ controller: /#{filter[:controller].underscore.sub(/_?controller\z/, "")}/ }
|
|
102
102
|
elsif filter[:grep]
|
|
103
103
|
grep_pattern = Regexp.new(filter[:grep])
|
|
104
|
-
path = RFC2396_PARSER.escape(filter[:grep])
|
|
104
|
+
path = URI::RFC2396_PARSER.escape(filter[:grep])
|
|
105
105
|
normalized_path = ("/" + path).squeeze("/")
|
|
106
106
|
|
|
107
107
|
{
|
|
@@ -375,35 +375,18 @@ module ActionDispatch
|
|
|
375
375
|
Routing::RouteSet::Dispatcher.new raise_on_name_error
|
|
376
376
|
end
|
|
377
377
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
next if location.path.start_with?(action_dispatch_dir)
|
|
378
|
+
def route_source_location
|
|
379
|
+
if Mapper.route_source_locations
|
|
380
|
+
action_dispatch_dir = File.expand_path("..", __dir__)
|
|
381
|
+
Thread.each_caller_location do |location|
|
|
382
|
+
next if location.path.start_with?(action_dispatch_dir)
|
|
384
383
|
|
|
385
|
-
|
|
386
|
-
|
|
384
|
+
cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
|
|
385
|
+
next if cleaned_path.nil?
|
|
387
386
|
|
|
388
|
-
|
|
389
|
-
end
|
|
390
|
-
nil
|
|
391
|
-
end
|
|
392
|
-
end
|
|
393
|
-
else
|
|
394
|
-
def route_source_location
|
|
395
|
-
if Mapper.route_source_locations
|
|
396
|
-
action_dispatch_dir = File.expand_path("..", __dir__)
|
|
397
|
-
caller_locations.each do |location|
|
|
398
|
-
next if location.path.start_with?(action_dispatch_dir)
|
|
399
|
-
|
|
400
|
-
cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
|
|
401
|
-
next if cleaned_path.nil?
|
|
402
|
-
|
|
403
|
-
return "#{cleaned_path}:#{location.lineno}"
|
|
404
|
-
end
|
|
405
|
-
nil
|
|
387
|
+
return "#{cleaned_path}:#{location.lineno}"
|
|
406
388
|
end
|
|
389
|
+
nil
|
|
407
390
|
end
|
|
408
391
|
end
|
|
409
392
|
end
|
|
@@ -470,7 +453,6 @@ module ActionDispatch
|
|
|
470
453
|
# When a pattern points to an internal route, the route's `:action` and
|
|
471
454
|
# `:controller` should be set in options or hash shorthand. Examples:
|
|
472
455
|
#
|
|
473
|
-
# match 'photos/:id' => 'photos#show', via: :get
|
|
474
456
|
# match 'photos/:id', to: 'photos#show', via: :get
|
|
475
457
|
# match 'photos/:id', controller: 'photos', action: 'show', via: :get
|
|
476
458
|
#
|
|
@@ -614,10 +596,6 @@ module ActionDispatch
|
|
|
614
596
|
#
|
|
615
597
|
# mount SomeRackApp, at: "some_route"
|
|
616
598
|
#
|
|
617
|
-
# Alternatively:
|
|
618
|
-
#
|
|
619
|
-
# mount(SomeRackApp => "some_route")
|
|
620
|
-
#
|
|
621
599
|
# For options, see `match`, as `mount` uses it internally.
|
|
622
600
|
#
|
|
623
601
|
# All mounted applications come with routing helpers to access them. These are
|
|
@@ -625,7 +603,7 @@ module ActionDispatch
|
|
|
625
603
|
# `some_rack_app_path` or `some_rack_app_url`. To customize this helper's name,
|
|
626
604
|
# use the `:as` option:
|
|
627
605
|
#
|
|
628
|
-
# mount(SomeRackApp
|
|
606
|
+
# mount(SomeRackApp, at: "some_route", as: "exciting")
|
|
629
607
|
#
|
|
630
608
|
# This will generate the `exciting_path` and `exciting_url` helpers which can be
|
|
631
609
|
# used to navigate to this mounted app.
|
|
@@ -773,6 +751,16 @@ module ActionDispatch
|
|
|
773
751
|
map_method(:options, args, &block)
|
|
774
752
|
end
|
|
775
753
|
|
|
754
|
+
# Define a route that recognizes HTTP CONNECT (and GET) requests. More
|
|
755
|
+
# specifically this recognizes HTTP/1 protocol upgrade requests and HTTP/2
|
|
756
|
+
# CONNECT requests with the protocol pseudo header. For supported arguments,
|
|
757
|
+
# see [match](rdoc-ref:Base#match)
|
|
758
|
+
#
|
|
759
|
+
# connect 'live', to: 'live#index'
|
|
760
|
+
def connect(*args, &block)
|
|
761
|
+
map_method([:get, :connect], args, &block)
|
|
762
|
+
end
|
|
763
|
+
|
|
776
764
|
private
|
|
777
765
|
def map_method(method, args, &block)
|
|
778
766
|
options = args.extract_options!
|
|
@@ -852,7 +840,7 @@ module ActionDispatch
|
|
|
852
840
|
#
|
|
853
841
|
# Takes same options as `Base#match` and `Resources#resources`.
|
|
854
842
|
#
|
|
855
|
-
# # route /posts (without the prefix /admin) to
|
|
843
|
+
# # route /posts (without the prefix /admin) to Admin::PostsController
|
|
856
844
|
# scope module: "admin" do
|
|
857
845
|
# resources :posts
|
|
858
846
|
# end
|
|
@@ -862,7 +850,7 @@ module ActionDispatch
|
|
|
862
850
|
# resources :posts
|
|
863
851
|
# end
|
|
864
852
|
#
|
|
865
|
-
# # prefix the routing helper name:
|
|
853
|
+
# # prefix the routing helper name: sekret_posts_path instead of posts_path
|
|
866
854
|
# scope as: "sekret" do
|
|
867
855
|
# resources :posts
|
|
868
856
|
# end
|
|
@@ -961,12 +949,12 @@ module ActionDispatch
|
|
|
961
949
|
# resources :posts
|
|
962
950
|
# end
|
|
963
951
|
#
|
|
964
|
-
# # maps to
|
|
952
|
+
# # maps to Sekret::PostsController rather than Admin::PostsController
|
|
965
953
|
# namespace :admin, module: "sekret" do
|
|
966
954
|
# resources :posts
|
|
967
955
|
# end
|
|
968
956
|
#
|
|
969
|
-
# # generates
|
|
957
|
+
# # generates sekret_posts_path rather than admin_posts_path
|
|
970
958
|
# namespace :admin, as: "sekret" do
|
|
971
959
|
# resources :posts
|
|
972
960
|
# end
|
|
@@ -1174,6 +1162,16 @@ module ActionDispatch
|
|
|
1174
1162
|
CANONICAL_ACTIONS = %w(index create new show update destroy)
|
|
1175
1163
|
|
|
1176
1164
|
class Resource # :nodoc:
|
|
1165
|
+
class << self
|
|
1166
|
+
def default_actions(api_only)
|
|
1167
|
+
if api_only
|
|
1168
|
+
[:index, :create, :show, :update, :destroy]
|
|
1169
|
+
else
|
|
1170
|
+
[:index, :create, :new, :show, :update, :destroy, :edit]
|
|
1171
|
+
end
|
|
1172
|
+
end
|
|
1173
|
+
end
|
|
1174
|
+
|
|
1177
1175
|
attr_reader :controller, :path, :param
|
|
1178
1176
|
|
|
1179
1177
|
def initialize(entities, api_only, shallow, options = {})
|
|
@@ -1181,6 +1179,12 @@ module ActionDispatch
|
|
|
1181
1179
|
raise ArgumentError, ":param option can't contain colons"
|
|
1182
1180
|
end
|
|
1183
1181
|
|
|
1182
|
+
valid_actions = self.class.default_actions(false) # ignore api_only for this validation
|
|
1183
|
+
if invalid_actions = invalid_only_except_options(options, valid_actions).presence
|
|
1184
|
+
error_prefix = "Route `resource#{"s" unless singleton?} :#{entities}`"
|
|
1185
|
+
raise ArgumentError, "#{error_prefix} - :only and :except must include only #{valid_actions}, but also included #{invalid_actions}"
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1184
1188
|
@name = entities.to_s
|
|
1185
1189
|
@path = (options[:path] || @name).to_s
|
|
1186
1190
|
@controller = (options[:controller] || @name).to_s
|
|
@@ -1194,11 +1198,7 @@ module ActionDispatch
|
|
|
1194
1198
|
end
|
|
1195
1199
|
|
|
1196
1200
|
def default_actions
|
|
1197
|
-
|
|
1198
|
-
[:index, :create, :show, :update, :destroy]
|
|
1199
|
-
else
|
|
1200
|
-
[:index, :create, :new, :show, :update, :destroy, :edit]
|
|
1201
|
-
end
|
|
1201
|
+
self.class.default_actions(@api_only)
|
|
1202
1202
|
end
|
|
1203
1203
|
|
|
1204
1204
|
def actions
|
|
@@ -1266,9 +1266,24 @@ module ActionDispatch
|
|
|
1266
1266
|
end
|
|
1267
1267
|
|
|
1268
1268
|
def singleton?; false; end
|
|
1269
|
+
|
|
1270
|
+
private
|
|
1271
|
+
def invalid_only_except_options(options, valid_actions)
|
|
1272
|
+
options.values_at(:only, :except).flatten.compact.uniq.map(&:to_sym) - valid_actions
|
|
1273
|
+
end
|
|
1269
1274
|
end
|
|
1270
1275
|
|
|
1271
1276
|
class SingletonResource < Resource # :nodoc:
|
|
1277
|
+
class << self
|
|
1278
|
+
def default_actions(api_only)
|
|
1279
|
+
if api_only
|
|
1280
|
+
[:show, :create, :update, :destroy]
|
|
1281
|
+
else
|
|
1282
|
+
[:show, :create, :update, :destroy, :new, :edit]
|
|
1283
|
+
end
|
|
1284
|
+
end
|
|
1285
|
+
end
|
|
1286
|
+
|
|
1272
1287
|
def initialize(entities, api_only, shallow, options)
|
|
1273
1288
|
super
|
|
1274
1289
|
@as = nil
|
|
@@ -1277,11 +1292,7 @@ module ActionDispatch
|
|
|
1277
1292
|
end
|
|
1278
1293
|
|
|
1279
1294
|
def default_actions
|
|
1280
|
-
|
|
1281
|
-
[:show, :create, :update, :destroy]
|
|
1282
|
-
else
|
|
1283
|
-
[:show, :create, :update, :destroy, :new, :edit]
|
|
1284
|
-
end
|
|
1295
|
+
self.class.default_actions(@api_only)
|
|
1285
1296
|
end
|
|
1286
1297
|
|
|
1287
1298
|
def plural
|
|
@@ -1342,7 +1353,7 @@ module ActionDispatch
|
|
|
1342
1353
|
end
|
|
1343
1354
|
|
|
1344
1355
|
with_scope_level(:resource) do
|
|
1345
|
-
options = apply_action_options options
|
|
1356
|
+
options = apply_action_options :resource, options
|
|
1346
1357
|
resource_scope(SingletonResource.new(resources.pop, api_only?, @scope[:shallow], options)) do
|
|
1347
1358
|
yield if block_given?
|
|
1348
1359
|
|
|
@@ -1499,7 +1510,7 @@ module ActionDispatch
|
|
|
1499
1510
|
#
|
|
1500
1511
|
# ### Examples
|
|
1501
1512
|
#
|
|
1502
|
-
# # routes call
|
|
1513
|
+
# # routes call Admin::PostsController
|
|
1503
1514
|
# resources :posts, module: "admin"
|
|
1504
1515
|
#
|
|
1505
1516
|
# # resource actions are at /admin/posts.
|
|
@@ -1512,7 +1523,7 @@ module ActionDispatch
|
|
|
1512
1523
|
end
|
|
1513
1524
|
|
|
1514
1525
|
with_scope_level(:resources) do
|
|
1515
|
-
options = apply_action_options options
|
|
1526
|
+
options = apply_action_options :resources, options
|
|
1516
1527
|
resource_scope(Resource.new(resources.pop, api_only?, @scope[:shallow], options)) do
|
|
1517
1528
|
yield if block_given?
|
|
1518
1529
|
|
|
@@ -1673,7 +1684,6 @@ module ActionDispatch
|
|
|
1673
1684
|
# Matches a URL pattern to one or more routes. For more information, see
|
|
1674
1685
|
# [match](rdoc-ref:Base#match).
|
|
1675
1686
|
#
|
|
1676
|
-
# match 'path' => 'controller#action', via: :patch
|
|
1677
1687
|
# match 'path', to: 'controller#action', via: :post
|
|
1678
1688
|
# match 'path', 'otherpath', on: :member, via: :get
|
|
1679
1689
|
def match(path, *rest, &block)
|
|
@@ -1782,17 +1792,32 @@ module ActionDispatch
|
|
|
1782
1792
|
false
|
|
1783
1793
|
end
|
|
1784
1794
|
|
|
1785
|
-
def apply_action_options(options)
|
|
1795
|
+
def apply_action_options(method, options)
|
|
1786
1796
|
return options if action_options? options
|
|
1787
|
-
options.merge scope_action_options
|
|
1797
|
+
options.merge scope_action_options(method)
|
|
1788
1798
|
end
|
|
1789
1799
|
|
|
1790
1800
|
def action_options?(options)
|
|
1791
1801
|
options[:only] || options[:except]
|
|
1792
1802
|
end
|
|
1793
1803
|
|
|
1794
|
-
def scope_action_options
|
|
1795
|
-
@scope[:action_options]
|
|
1804
|
+
def scope_action_options(method)
|
|
1805
|
+
return {} unless @scope[:action_options]
|
|
1806
|
+
|
|
1807
|
+
actions = applicable_actions_for(method)
|
|
1808
|
+
@scope[:action_options].dup.tap do |options|
|
|
1809
|
+
(options[:only] = Array(options[:only]) & actions) if options[:only]
|
|
1810
|
+
(options[:except] = Array(options[:except]) & actions) if options[:except]
|
|
1811
|
+
end
|
|
1812
|
+
end
|
|
1813
|
+
|
|
1814
|
+
def applicable_actions_for(method)
|
|
1815
|
+
case method
|
|
1816
|
+
when :resource
|
|
1817
|
+
SingletonResource.default_actions(api_only?)
|
|
1818
|
+
when :resources
|
|
1819
|
+
Resource.default_actions(api_only?)
|
|
1820
|
+
end
|
|
1796
1821
|
end
|
|
1797
1822
|
|
|
1798
1823
|
def resource_scope?
|
|
@@ -1935,6 +1960,11 @@ module ActionDispatch
|
|
|
1935
1960
|
end
|
|
1936
1961
|
|
|
1937
1962
|
def map_match(paths, options)
|
|
1963
|
+
ActionDispatch.deprecator.warn(<<-MSG.squish) if paths.count > 1
|
|
1964
|
+
Mapping a route with multiple paths is deprecated and
|
|
1965
|
+
will be removed in Rails 8.1. Please use multiple method calls instead.
|
|
1966
|
+
MSG
|
|
1967
|
+
|
|
1938
1968
|
if (on = options[:on]) && !VALID_ON_OPTIONS.include?(on)
|
|
1939
1969
|
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
|
|
1940
1970
|
end
|
|
@@ -2025,7 +2055,7 @@ module ActionDispatch
|
|
|
2025
2055
|
name_for_action(options.delete(:as), action)
|
|
2026
2056
|
end
|
|
2027
2057
|
|
|
2028
|
-
path = Mapping.normalize_path RFC2396_PARSER.escape(path), formatted
|
|
2058
|
+
path = Mapping.normalize_path URI::RFC2396_PARSER.escape(path), formatted
|
|
2029
2059
|
ast = Journey::Parser.parse path
|
|
2030
2060
|
|
|
2031
2061
|
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
|
|
@@ -2200,8 +2230,8 @@ module ActionDispatch
|
|
|
2200
2230
|
end
|
|
2201
2231
|
|
|
2202
2232
|
# Define custom polymorphic mappings of models to URLs. This alters the behavior
|
|
2203
|
-
# of `polymorphic_url` and consequently the behavior of `link_to
|
|
2204
|
-
# when passed a model instance, e.g:
|
|
2233
|
+
# of `polymorphic_url` and consequently the behavior of `link_to`, `form_with`
|
|
2234
|
+
# and `form_for` when passed a model instance, e.g:
|
|
2205
2235
|
#
|
|
2206
2236
|
# resource :basket
|
|
2207
2237
|
#
|
|
@@ -2210,7 +2240,7 @@ module ActionDispatch
|
|
|
2210
2240
|
# end
|
|
2211
2241
|
#
|
|
2212
2242
|
# This will now generate "/basket" when a `Basket` instance is passed to
|
|
2213
|
-
# `link_to` or `form_for` instead of the standard "/baskets/:id".
|
|
2243
|
+
# `link_to`, `form_with` or `form_for` instead of the standard "/baskets/:id".
|
|
2214
2244
|
#
|
|
2215
2245
|
# NOTE: This custom behavior only applies to simple polymorphic URLs where a
|
|
2216
2246
|
# single model instance is passed and not more complicated forms, e.g:
|
|
@@ -2267,9 +2297,9 @@ module ActionDispatch
|
|
|
2267
2297
|
|
|
2268
2298
|
attr_reader :parent, :scope_level
|
|
2269
2299
|
|
|
2270
|
-
def initialize(hash, parent =
|
|
2271
|
-
@hash = hash
|
|
2300
|
+
def initialize(hash, parent = ROOT, scope_level = nil)
|
|
2272
2301
|
@parent = parent
|
|
2302
|
+
@hash = parent ? parent.frame.merge(hash) : hash
|
|
2273
2303
|
@scope_level = scope_level
|
|
2274
2304
|
end
|
|
2275
2305
|
|
|
@@ -2282,7 +2312,7 @@ module ActionDispatch
|
|
|
2282
2312
|
end
|
|
2283
2313
|
|
|
2284
2314
|
def root?
|
|
2285
|
-
@parent
|
|
2315
|
+
@parent == ROOT
|
|
2286
2316
|
end
|
|
2287
2317
|
|
|
2288
2318
|
def resources?
|
|
@@ -2327,23 +2357,22 @@ module ActionDispatch
|
|
|
2327
2357
|
end
|
|
2328
2358
|
|
|
2329
2359
|
def [](key)
|
|
2330
|
-
|
|
2331
|
-
scope && scope.frame[key]
|
|
2360
|
+
frame[key]
|
|
2332
2361
|
end
|
|
2333
2362
|
|
|
2363
|
+
def frame; @hash; end
|
|
2364
|
+
|
|
2334
2365
|
include Enumerable
|
|
2335
2366
|
|
|
2336
2367
|
def each
|
|
2337
2368
|
node = self
|
|
2338
|
-
until node.equal?
|
|
2369
|
+
until node.equal? ROOT
|
|
2339
2370
|
yield node
|
|
2340
2371
|
node = node.parent
|
|
2341
2372
|
end
|
|
2342
2373
|
end
|
|
2343
2374
|
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
NULL = Scope.new(nil, nil)
|
|
2375
|
+
ROOT = Scope.new({}, nil)
|
|
2347
2376
|
end
|
|
2348
2377
|
|
|
2349
2378
|
def initialize(set) # :nodoc:
|
|
@@ -31,7 +31,7 @@ module ActionDispatch
|
|
|
31
31
|
# * `url_for`, so you can use it with a record as the argument, e.g.
|
|
32
32
|
# `url_for(@article)`;
|
|
33
33
|
# * ActionView::Helpers::FormHelper uses `polymorphic_path`, so you can write
|
|
34
|
-
# `
|
|
34
|
+
# `form_with(model: @article)` without having to specify `:url` parameter for the
|
|
35
35
|
# form action;
|
|
36
36
|
# * `redirect_to` (which, in fact, uses `url_for`) so you can write
|
|
37
37
|
# `redirect_to(post)` in your controllers;
|
|
@@ -61,7 +61,7 @@ module ActionDispatch
|
|
|
61
61
|
# argument to the method. For example:
|
|
62
62
|
#
|
|
63
63
|
# polymorphic_url([blog, @post]) # calls blog.post_path(@post)
|
|
64
|
-
#
|
|
64
|
+
# form_with(model: [blog, @post]) # => "/blog/posts/1"
|
|
65
65
|
#
|
|
66
66
|
module PolymorphicRoutes
|
|
67
67
|
# Constructs a call to a named RESTful route for the given record and returns
|
|
@@ -29,7 +29,7 @@ module ActionDispatch
|
|
|
29
29
|
def from_requirements(requirements)
|
|
30
30
|
routes.find { |route| route.requirements == requirements }
|
|
31
31
|
end
|
|
32
|
-
# :
|
|
32
|
+
# :enddoc:
|
|
33
33
|
|
|
34
34
|
# Since the router holds references to many parts of the system like engines,
|
|
35
35
|
# controllers and the application itself, inspecting the route set can actually
|
|
@@ -351,7 +351,7 @@ module ActionDispatch
|
|
|
351
351
|
PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
|
|
352
352
|
UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
|
|
353
353
|
|
|
354
|
-
attr_accessor :formatter, :set, :named_routes, :
|
|
354
|
+
attr_accessor :formatter, :set, :named_routes, :router
|
|
355
355
|
attr_accessor :disable_clear_and_finalize, :resources_path_names
|
|
356
356
|
attr_accessor :default_url_options, :draw_paths
|
|
357
357
|
attr_reader :env_key, :polymorphic_mappings
|
|
@@ -363,7 +363,7 @@ module ActionDispatch
|
|
|
363
363
|
end
|
|
364
364
|
|
|
365
365
|
def self.new_with_config(config)
|
|
366
|
-
route_set_config = DEFAULT_CONFIG
|
|
366
|
+
route_set_config = DEFAULT_CONFIG.dup
|
|
367
367
|
|
|
368
368
|
# engines apparently don't have this set
|
|
369
369
|
if config.respond_to? :relative_url_root
|
|
@@ -374,14 +374,18 @@ module ActionDispatch
|
|
|
374
374
|
route_set_config.api_only = config.api_only
|
|
375
375
|
end
|
|
376
376
|
|
|
377
|
+
if config.respond_to? :default_scope
|
|
378
|
+
route_set_config.default_scope = config.default_scope
|
|
379
|
+
end
|
|
380
|
+
|
|
377
381
|
new route_set_config
|
|
378
382
|
end
|
|
379
383
|
|
|
380
|
-
Config = Struct.new :relative_url_root, :api_only
|
|
384
|
+
Config = Struct.new :relative_url_root, :api_only, :default_scope
|
|
381
385
|
|
|
382
|
-
DEFAULT_CONFIG = Config.new(nil, false)
|
|
386
|
+
DEFAULT_CONFIG = Config.new(nil, false, nil)
|
|
383
387
|
|
|
384
|
-
def initialize(config = DEFAULT_CONFIG)
|
|
388
|
+
def initialize(config = DEFAULT_CONFIG.dup)
|
|
385
389
|
self.named_routes = NamedRouteCollection.new
|
|
386
390
|
self.resources_path_names = self.class.default_resources_path_names
|
|
387
391
|
self.default_url_options = {}
|
|
@@ -416,6 +420,14 @@ module ActionDispatch
|
|
|
416
420
|
@config.api_only
|
|
417
421
|
end
|
|
418
422
|
|
|
423
|
+
def default_scope
|
|
424
|
+
@config.default_scope
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def default_scope=(new_default_scope)
|
|
428
|
+
@config.default_scope = new_default_scope
|
|
429
|
+
end
|
|
430
|
+
|
|
419
431
|
def request_class
|
|
420
432
|
ActionDispatch::Request
|
|
421
433
|
end
|
|
@@ -647,14 +659,14 @@ module ActionDispatch
|
|
|
647
659
|
if route.segment_keys.include?(:controller)
|
|
648
660
|
ActionDispatch.deprecator.warn(<<-MSG.squish)
|
|
649
661
|
Using a dynamic :controller segment in a route is deprecated and
|
|
650
|
-
will be removed in Rails
|
|
662
|
+
will be removed in Rails 8.1.
|
|
651
663
|
MSG
|
|
652
664
|
end
|
|
653
665
|
|
|
654
666
|
if route.segment_keys.include?(:action)
|
|
655
667
|
ActionDispatch.deprecator.warn(<<-MSG.squish)
|
|
656
668
|
Using a dynamic :action segment in a route is deprecated and
|
|
657
|
-
will be removed in Rails
|
|
669
|
+
will be removed in Rails 8.1.
|
|
658
670
|
MSG
|
|
659
671
|
end
|
|
660
672
|
|
|
@@ -917,7 +929,7 @@ module ActionDispatch
|
|
|
917
929
|
params.each do |key, value|
|
|
918
930
|
if value.is_a?(String)
|
|
919
931
|
value = value.dup.force_encoding(Encoding::BINARY)
|
|
920
|
-
params[key] = RFC2396_PARSER.unescape(value)
|
|
932
|
+
params[key] = URI::RFC2396_PARSER.unescape(value)
|
|
921
933
|
end
|
|
922
934
|
end
|
|
923
935
|
req.path_parameters = params
|
|
@@ -941,6 +953,5 @@ module ActionDispatch
|
|
|
941
953
|
end
|
|
942
954
|
end
|
|
943
955
|
end
|
|
944
|
-
# :startdoc:
|
|
945
956
|
end
|
|
946
957
|
end
|
|
@@ -54,6 +54,7 @@ module ActionDispatch
|
|
|
54
54
|
# dependent part.
|
|
55
55
|
def merge_script_names(previous_script_name, new_script_name)
|
|
56
56
|
return new_script_name unless previous_script_name
|
|
57
|
+
new_script_name = new_script_name.chomp("/")
|
|
57
58
|
|
|
58
59
|
resolved_parts = new_script_name.count("/")
|
|
59
60
|
previous_parts = previous_script_name.count("/")
|
|
@@ -9,7 +9,6 @@ module ActionDispatch
|
|
|
9
9
|
|
|
10
10
|
def initialize(name)
|
|
11
11
|
@name = name
|
|
12
|
-
set_default_options
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
def type
|
|
@@ -27,9 +26,9 @@ module ActionDispatch
|
|
|
27
26
|
@options ||=
|
|
28
27
|
case type
|
|
29
28
|
when :chrome
|
|
30
|
-
|
|
29
|
+
default_chrome_options
|
|
31
30
|
when :firefox
|
|
32
|
-
|
|
31
|
+
default_firefox_options
|
|
33
32
|
end
|
|
34
33
|
end
|
|
35
34
|
|
|
@@ -49,26 +48,18 @@ module ActionDispatch
|
|
|
49
48
|
end
|
|
50
49
|
|
|
51
50
|
private
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
end
|
|
51
|
+
def default_chrome_options
|
|
52
|
+
options = ::Selenium::WebDriver::Chrome::Options.new
|
|
53
|
+
options.add_argument("--disable-search-engine-choice-screen")
|
|
54
|
+
options.add_argument("--headless") if name == :headless_chrome
|
|
55
|
+
options.add_argument("--disable-gpu") if Gem.win_platform?
|
|
56
|
+
options
|
|
59
57
|
end
|
|
60
58
|
|
|
61
|
-
def
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def set_headless_firefox_browser_options
|
|
69
|
-
configure do |capabilities|
|
|
70
|
-
capabilities.add_argument("-headless")
|
|
71
|
-
end
|
|
59
|
+
def default_firefox_options
|
|
60
|
+
options = ::Selenium::WebDriver::Firefox::Options.new
|
|
61
|
+
options.add_argument("-headless") if name == :headless_firefox
|
|
62
|
+
options
|
|
72
63
|
end
|
|
73
64
|
|
|
74
65
|
def resolve_driver_path(namespace)
|
|
@@ -87,8 +87,13 @@ module ActionDispatch
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def generate_response_message(expected, actual = @response.response_code)
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
lambda do
|
|
91
|
+
(+"Expected response to be a <#{code_with_name(expected)}>,"\
|
|
92
|
+
" but was a <#{code_with_name(actual)}>").
|
|
93
|
+
concat(location_if_redirected).
|
|
94
|
+
concat(exception_if_present).
|
|
95
|
+
concat(response_body_if_short)
|
|
96
|
+
end
|
|
92
97
|
end
|
|
93
98
|
|
|
94
99
|
def response_body_if_short
|
|
@@ -96,6 +101,11 @@ module ActionDispatch
|
|
|
96
101
|
"\nResponse body: #{@response.body}"
|
|
97
102
|
end
|
|
98
103
|
|
|
104
|
+
def exception_if_present
|
|
105
|
+
return "" unless ex = @request&.env&.[]("action_dispatch.exception")
|
|
106
|
+
"\n\nException while processing request: #{Minitest::UnexpectedError.new(ex).message}\n"
|
|
107
|
+
end
|
|
108
|
+
|
|
99
109
|
def location_if_redirected
|
|
100
110
|
return "" unless @response.redirection? && @response.location.present?
|
|
101
111
|
location = normalize_argument_to_redirection(@response.location)
|