actionpack 7.2.3 → 8.1.3
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 +394 -119
- data/lib/abstract_controller/asset_paths.rb +4 -2
- data/lib/abstract_controller/base.rb +11 -5
- data/lib/abstract_controller/caching.rb +6 -3
- data/lib/abstract_controller/callbacks.rb +6 -0
- data/lib/abstract_controller/logger.rb +2 -1
- data/lib/abstract_controller/rendering.rb +0 -1
- data/lib/action_controller/api.rb +1 -0
- data/lib/action_controller/base.rb +3 -2
- data/lib/action_controller/caching.rb +1 -2
- data/lib/action_controller/form_builder.rb +4 -4
- data/lib/action_controller/log_subscriber.rb +22 -3
- data/lib/action_controller/metal/allow_browser.rb +12 -2
- data/lib/action_controller/metal/conditional_get.rb +30 -1
- data/lib/action_controller/metal/data_streaming.rb +5 -5
- data/lib/action_controller/metal/exceptions.rb +5 -0
- data/lib/action_controller/metal/flash.rb +1 -4
- data/lib/action_controller/metal/head.rb +3 -1
- data/lib/action_controller/metal/instrumentation.rb +1 -2
- data/lib/action_controller/metal/live.rb +65 -25
- data/lib/action_controller/metal/permissions_policy.rb +9 -0
- data/lib/action_controller/metal/rate_limiting.rb +39 -9
- data/lib/action_controller/metal/redirecting.rb +105 -13
- data/lib/action_controller/metal/renderers.rb +29 -9
- data/lib/action_controller/metal/rendering.rb +7 -1
- data/lib/action_controller/metal/request_forgery_protection.rb +18 -10
- data/lib/action_controller/metal/rescue.rb +9 -0
- data/lib/action_controller/metal/streaming.rb +5 -84
- data/lib/action_controller/metal/strong_parameters.rb +277 -89
- data/lib/action_controller/railtie.rb +33 -15
- data/lib/action_controller/structured_event_subscriber.rb +116 -0
- data/lib/action_controller/test_case.rb +12 -2
- data/lib/action_dispatch/http/cache.rb +138 -11
- data/lib/action_dispatch/http/content_security_policy.rb +14 -1
- data/lib/action_dispatch/http/filter_parameters.rb +5 -3
- data/lib/action_dispatch/http/mime_negotiation.rb +55 -1
- data/lib/action_dispatch/http/mime_types.rb +1 -0
- data/lib/action_dispatch/http/param_builder.rb +187 -0
- data/lib/action_dispatch/http/param_error.rb +26 -0
- data/lib/action_dispatch/http/parameters.rb +3 -3
- data/lib/action_dispatch/http/permissions_policy.rb +6 -0
- data/lib/action_dispatch/http/query_parser.rb +55 -0
- data/lib/action_dispatch/http/request.rb +70 -21
- data/lib/action_dispatch/http/response.rb +50 -16
- data/lib/action_dispatch/http/url.rb +110 -14
- data/lib/action_dispatch/journey/gtg/simulator.rb +33 -12
- data/lib/action_dispatch/journey/gtg/transition_table.rb +33 -41
- data/lib/action_dispatch/journey/nodes/node.rb +2 -1
- data/lib/action_dispatch/journey/parser.rb +99 -196
- data/lib/action_dispatch/journey/route.rb +45 -31
- data/lib/action_dispatch/journey/router/utils.rb +8 -14
- data/lib/action_dispatch/journey/router.rb +59 -81
- data/lib/action_dispatch/journey/routes.rb +7 -0
- data/lib/action_dispatch/journey/scanner.rb +44 -42
- data/lib/action_dispatch/journey/visitors.rb +55 -23
- data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
- data/lib/action_dispatch/log_subscriber.rb +7 -3
- data/lib/action_dispatch/middleware/cookies.rb +8 -4
- data/lib/action_dispatch/middleware/debug_exceptions.rb +24 -5
- data/lib/action_dispatch/middleware/debug_view.rb +11 -5
- data/lib/action_dispatch/middleware/exception_wrapper.rb +11 -11
- data/lib/action_dispatch/middleware/executor.rb +12 -2
- data/lib/action_dispatch/middleware/public_exceptions.rb +1 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +11 -5
- data/lib/action_dispatch/middleware/request_id.rb +2 -1
- data/lib/action_dispatch/middleware/session/cache_store.rb +17 -0
- data/lib/action_dispatch/middleware/ssl.rb +13 -3
- data/lib/action_dispatch/middleware/templates/rescues/_copy_button.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +3 -5
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +9 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +50 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -0
- data/lib/action_dispatch/railtie.rb +21 -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 +80 -57
- data/lib/action_dispatch/routing/mapper.rb +404 -223
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -2
- data/lib/action_dispatch/routing/redirection.rb +10 -7
- data/lib/action_dispatch/routing/route_set.rb +21 -12
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -0
- data/lib/action_dispatch/structured_event_subscriber.rb +20 -0
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +12 -21
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +2 -2
- data/lib/action_dispatch/testing/assertions/response.rb +26 -2
- data/lib/action_dispatch/testing/assertions/routing.rb +27 -15
- data/lib/action_dispatch/testing/integration.rb +18 -7
- data/lib/action_dispatch.rb +14 -4
- data/lib/action_pack/gem_version.rb +2 -2
- metadata +18 -48
- data/lib/action_dispatch/journey/parser.y +0 -50
- data/lib/action_dispatch/journey/parser_extras.rb +0 -33
|
@@ -87,7 +87,7 @@ module ActionDispatch
|
|
|
87
87
|
attr_reader :path, :requirements, :defaults, :to, :default_controller,
|
|
88
88
|
:default_action, :required_defaults, :ast, :scope_options
|
|
89
89
|
|
|
90
|
-
def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
|
|
90
|
+
def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, internal, options)
|
|
91
91
|
scope_params = {
|
|
92
92
|
blocks: scope[:blocks] || [],
|
|
93
93
|
constraints: scope[:constraints] || {},
|
|
@@ -98,7 +98,7 @@ module ActionDispatch
|
|
|
98
98
|
|
|
99
99
|
new set: set, ast: ast, controller: controller, default_action: default_action,
|
|
100
100
|
to: to, formatted: formatted, via: via, options_constraints: options_constraints,
|
|
101
|
-
anchor: anchor, scope_params: scope_params, options: scope_params[:options].merge(options)
|
|
101
|
+
anchor: anchor, scope_params: scope_params, internal: internal, options: scope_params[:options].merge(options)
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def self.check_via(via)
|
|
@@ -129,7 +129,7 @@ module ActionDispatch
|
|
|
129
129
|
format != false && !path.match?(OPTIONAL_FORMAT_REGEX)
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
-
def initialize(set:, ast:, controller:, default_action:, to:, formatted:, via:, options_constraints:, anchor:, scope_params:, options:)
|
|
132
|
+
def initialize(set:, ast:, controller:, default_action:, to:, formatted:, via:, options_constraints:, anchor:, scope_params:, internal:, options:)
|
|
133
133
|
@defaults = scope_params[:defaults]
|
|
134
134
|
@set = set
|
|
135
135
|
@to = intern(to)
|
|
@@ -137,7 +137,7 @@ module ActionDispatch
|
|
|
137
137
|
@default_action = intern(default_action)
|
|
138
138
|
@anchor = anchor
|
|
139
139
|
@via = via
|
|
140
|
-
@internal =
|
|
140
|
+
@internal = internal
|
|
141
141
|
@scope_options = scope_params[:options]
|
|
142
142
|
ast = Journey::Ast.new(ast, formatted)
|
|
143
143
|
|
|
@@ -183,7 +183,7 @@ module ActionDispatch
|
|
|
183
183
|
def make_route(name, precedence)
|
|
184
184
|
Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
|
|
185
185
|
required_defaults: required_defaults, defaults: defaults,
|
|
186
|
-
|
|
186
|
+
via: @via, precedence: precedence,
|
|
187
187
|
scope_options: scope_options, internal: @internal, source_location: route_source_location)
|
|
188
188
|
end
|
|
189
189
|
|
|
@@ -204,11 +204,6 @@ module ActionDispatch
|
|
|
204
204
|
end
|
|
205
205
|
private :build_conditions
|
|
206
206
|
|
|
207
|
-
def request_method
|
|
208
|
-
@via.map { |x| Journey::Route.verb_matcher(x) }
|
|
209
|
-
end
|
|
210
|
-
private :request_method
|
|
211
|
-
|
|
212
207
|
private
|
|
213
208
|
def intern(object)
|
|
214
209
|
object.is_a?(String) ? -object : object
|
|
@@ -375,35 +370,18 @@ module ActionDispatch
|
|
|
375
370
|
Routing::RouteSet::Dispatcher.new raise_on_name_error
|
|
376
371
|
end
|
|
377
372
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
next if location.path.start_with?(action_dispatch_dir)
|
|
384
|
-
|
|
385
|
-
cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
|
|
386
|
-
next if cleaned_path.nil?
|
|
387
|
-
|
|
388
|
-
return "#{cleaned_path}:#{location.lineno}"
|
|
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)
|
|
373
|
+
def route_source_location
|
|
374
|
+
if Mapper.route_source_locations
|
|
375
|
+
action_dispatch_dir = File.expand_path("..", __dir__)
|
|
376
|
+
Thread.each_caller_location do |location|
|
|
377
|
+
next if location.path.start_with?(action_dispatch_dir)
|
|
399
378
|
|
|
400
|
-
|
|
401
|
-
|
|
379
|
+
cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
|
|
380
|
+
next if cleaned_path.nil?
|
|
402
381
|
|
|
403
|
-
|
|
404
|
-
end
|
|
405
|
-
nil
|
|
382
|
+
return "#{cleaned_path}:#{location.lineno}"
|
|
406
383
|
end
|
|
384
|
+
nil
|
|
407
385
|
end
|
|
408
386
|
end
|
|
409
387
|
end
|
|
@@ -470,7 +448,6 @@ module ActionDispatch
|
|
|
470
448
|
# When a pattern points to an internal route, the route's `:action` and
|
|
471
449
|
# `:controller` should be set in options or hash shorthand. Examples:
|
|
472
450
|
#
|
|
473
|
-
# match 'photos/:id' => 'photos#show', via: :get
|
|
474
451
|
# match 'photos/:id', to: 'photos#show', via: :get
|
|
475
452
|
# match 'photos/:id', controller: 'photos', action: 'show', via: :get
|
|
476
453
|
#
|
|
@@ -614,10 +591,6 @@ module ActionDispatch
|
|
|
614
591
|
#
|
|
615
592
|
# mount SomeRackApp, at: "some_route"
|
|
616
593
|
#
|
|
617
|
-
# Alternatively:
|
|
618
|
-
#
|
|
619
|
-
# mount(SomeRackApp => "some_route")
|
|
620
|
-
#
|
|
621
594
|
# For options, see `match`, as `mount` uses it internally.
|
|
622
595
|
#
|
|
623
596
|
# All mounted applications come with routing helpers to access them. These are
|
|
@@ -625,21 +598,36 @@ module ActionDispatch
|
|
|
625
598
|
# `some_rack_app_path` or `some_rack_app_url`. To customize this helper's name,
|
|
626
599
|
# use the `:as` option:
|
|
627
600
|
#
|
|
628
|
-
# mount(SomeRackApp
|
|
601
|
+
# mount(SomeRackApp, at: "some_route", as: "exciting")
|
|
629
602
|
#
|
|
630
603
|
# This will generate the `exciting_path` and `exciting_url` helpers which can be
|
|
631
604
|
# used to navigate to this mounted app.
|
|
632
|
-
def mount(app,
|
|
633
|
-
if
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
605
|
+
def mount(app = nil, deprecated_options = nil, as: DEFAULT, via: nil, at: nil, defaults: nil, constraints: nil, anchor: false, format: false, path: nil, internal: nil, **mapping, &block)
|
|
606
|
+
if deprecated_options.is_a?(Hash)
|
|
607
|
+
as = assign_deprecated_option(deprecated_options, :as, :mount) if deprecated_options.key?(:as)
|
|
608
|
+
via ||= assign_deprecated_option(deprecated_options, :via, :mount)
|
|
609
|
+
at ||= assign_deprecated_option(deprecated_options, :at, :mount)
|
|
610
|
+
defaults ||= assign_deprecated_option(deprecated_options, :defaults, :mount)
|
|
611
|
+
constraints ||= assign_deprecated_option(deprecated_options, :constraints, :mount)
|
|
612
|
+
anchor = assign_deprecated_option(deprecated_options, :anchor, :mount) if deprecated_options.key?(:anchor)
|
|
613
|
+
format = assign_deprecated_option(deprecated_options, :format, :mount) if deprecated_options.key?(:format)
|
|
614
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :mount)
|
|
615
|
+
internal ||= assign_deprecated_option(deprecated_options, :internal, :mount)
|
|
616
|
+
assign_deprecated_options(deprecated_options, mapping, :mount)
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
path_or_action = at
|
|
620
|
+
|
|
621
|
+
if app.nil?
|
|
622
|
+
hash_app, hash_path = mapping.find { |key, _| key.respond_to?(:call) }
|
|
623
|
+
mapping.delete(hash_app) if hash_app
|
|
624
|
+
|
|
625
|
+
app ||= hash_app
|
|
626
|
+
path_or_action ||= hash_path
|
|
639
627
|
end
|
|
640
628
|
|
|
641
629
|
raise ArgumentError, "A rack application must be specified" unless app.respond_to?(:call)
|
|
642
|
-
raise ArgumentError, <<~MSG unless
|
|
630
|
+
raise ArgumentError, <<~MSG unless path_or_action
|
|
643
631
|
Must be called with mount point
|
|
644
632
|
|
|
645
633
|
mount SomeRackApp, at: "some_route"
|
|
@@ -648,12 +636,12 @@ module ActionDispatch
|
|
|
648
636
|
MSG
|
|
649
637
|
|
|
650
638
|
rails_app = rails_app? app
|
|
651
|
-
|
|
639
|
+
as = app_name(app, rails_app) if as == DEFAULT
|
|
652
640
|
|
|
653
|
-
target_as
|
|
654
|
-
|
|
641
|
+
target_as = name_for_action(as, path_or_action)
|
|
642
|
+
via ||= :all
|
|
655
643
|
|
|
656
|
-
match(
|
|
644
|
+
match(path_or_action, to: app, as:, via:, defaults:, constraints:, anchor:, format:, path:, internal:, **mapping, &block)
|
|
657
645
|
|
|
658
646
|
define_generate_prefix(app, target_as) if rails_app
|
|
659
647
|
self
|
|
@@ -665,7 +653,7 @@ module ActionDispatch
|
|
|
665
653
|
alias_method :default_url_options, :default_url_options=
|
|
666
654
|
|
|
667
655
|
def with_default_scope(scope, &block)
|
|
668
|
-
scope(scope) do
|
|
656
|
+
scope(**scope) do
|
|
669
657
|
instance_exec(&block)
|
|
670
658
|
end
|
|
671
659
|
end
|
|
@@ -676,6 +664,24 @@ module ActionDispatch
|
|
|
676
664
|
end
|
|
677
665
|
|
|
678
666
|
private
|
|
667
|
+
def assign_deprecated_option(deprecated_options, key, method_name)
|
|
668
|
+
if (deprecated_value = deprecated_options.delete(key))
|
|
669
|
+
ActionDispatch.deprecator.warn(<<~MSG.squish)
|
|
670
|
+
#{method_name} received a hash argument #{key}. Please use a keyword instead. Support to hash argument will be removed in Rails 8.2.
|
|
671
|
+
MSG
|
|
672
|
+
deprecated_value
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
def assign_deprecated_options(deprecated_options, options, method_name)
|
|
677
|
+
deprecated_options.each do |key, value|
|
|
678
|
+
ActionDispatch.deprecator.warn(<<~MSG.squish)
|
|
679
|
+
#{method_name} received a hash argument #{key}. Please use a keyword instead. Support to hash argument will be removed in Rails 8.2.
|
|
680
|
+
MSG
|
|
681
|
+
options[key] = value
|
|
682
|
+
end
|
|
683
|
+
end
|
|
684
|
+
|
|
679
685
|
def rails_app?(app)
|
|
680
686
|
app.is_a?(Class) && app < Rails::Railtie
|
|
681
687
|
end
|
|
@@ -729,57 +735,171 @@ module ActionDispatch
|
|
|
729
735
|
# [match](rdoc-ref:Base#match)
|
|
730
736
|
#
|
|
731
737
|
# get 'bacon', to: 'food#bacon'
|
|
732
|
-
def get(*
|
|
733
|
-
|
|
738
|
+
def get(*path_or_actions, as: DEFAULT, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &block)
|
|
739
|
+
if path_or_actions.grep(Hash).any? && (deprecated_options = path_or_actions.extract_options!)
|
|
740
|
+
as = assign_deprecated_option(deprecated_options, :as, :get) if deprecated_options.key?(:as)
|
|
741
|
+
to ||= assign_deprecated_option(deprecated_options, :to, :get)
|
|
742
|
+
controller ||= assign_deprecated_option(deprecated_options, :controller, :get)
|
|
743
|
+
action ||= assign_deprecated_option(deprecated_options, :action, :get)
|
|
744
|
+
on ||= assign_deprecated_option(deprecated_options, :on, :get)
|
|
745
|
+
defaults ||= assign_deprecated_option(deprecated_options, :defaults, :get)
|
|
746
|
+
constraints ||= assign_deprecated_option(deprecated_options, :constraints, :get)
|
|
747
|
+
anchor = assign_deprecated_option(deprecated_options, :anchor, :get) if deprecated_options.key?(:anchor)
|
|
748
|
+
format = assign_deprecated_option(deprecated_options, :format, :get) if deprecated_options.key?(:format)
|
|
749
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :get)
|
|
750
|
+
internal ||= assign_deprecated_option(deprecated_options, :internal, :get)
|
|
751
|
+
assign_deprecated_options(deprecated_options, mapping, :get)
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
match(*path_or_actions, as:, to:, controller:, action:, on:, defaults:, constraints:, anchor:, format:, path:, internal:, **mapping, via: :get, &block)
|
|
755
|
+
self
|
|
734
756
|
end
|
|
735
757
|
|
|
736
758
|
# Define a route that only recognizes HTTP POST. For supported arguments, see
|
|
737
759
|
# [match](rdoc-ref:Base#match)
|
|
738
760
|
#
|
|
739
761
|
# post 'bacon', to: 'food#bacon'
|
|
740
|
-
def post(*
|
|
741
|
-
|
|
762
|
+
def post(*path_or_actions, as: DEFAULT, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &block)
|
|
763
|
+
if path_or_actions.grep(Hash).any? && (deprecated_options = path_or_actions.extract_options!)
|
|
764
|
+
as = assign_deprecated_option(deprecated_options, :as, :post) if deprecated_options.key?(:as)
|
|
765
|
+
to ||= assign_deprecated_option(deprecated_options, :to, :post)
|
|
766
|
+
controller ||= assign_deprecated_option(deprecated_options, :controller, :post)
|
|
767
|
+
action ||= assign_deprecated_option(deprecated_options, :action, :post)
|
|
768
|
+
on ||= assign_deprecated_option(deprecated_options, :on, :post)
|
|
769
|
+
defaults ||= assign_deprecated_option(deprecated_options, :defaults, :post)
|
|
770
|
+
constraints ||= assign_deprecated_option(deprecated_options, :constraints, :post)
|
|
771
|
+
anchor = assign_deprecated_option(deprecated_options, :anchor, :post) if deprecated_options.key?(:anchor)
|
|
772
|
+
format = assign_deprecated_option(deprecated_options, :format, :post) if deprecated_options.key?(:format)
|
|
773
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :post)
|
|
774
|
+
internal ||= assign_deprecated_option(deprecated_options, :internal, :post)
|
|
775
|
+
assign_deprecated_options(deprecated_options, mapping, :post)
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
match(*path_or_actions, as:, to:, controller:, action:, on:, defaults:, constraints:, anchor:, format:, path:, internal:, **mapping, via: :post, &block)
|
|
779
|
+
self
|
|
742
780
|
end
|
|
743
781
|
|
|
744
782
|
# Define a route that only recognizes HTTP PATCH. For supported arguments, see
|
|
745
783
|
# [match](rdoc-ref:Base#match)
|
|
746
784
|
#
|
|
747
785
|
# patch 'bacon', to: 'food#bacon'
|
|
748
|
-
def patch(*
|
|
749
|
-
|
|
786
|
+
def patch(*path_or_actions, as: DEFAULT, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &block)
|
|
787
|
+
if path_or_actions.grep(Hash).any? && (deprecated_options = path_or_actions.extract_options!)
|
|
788
|
+
as = assign_deprecated_option(deprecated_options, :as, :patch) if deprecated_options.key?(:as)
|
|
789
|
+
to ||= assign_deprecated_option(deprecated_options, :to, :patch)
|
|
790
|
+
controller ||= assign_deprecated_option(deprecated_options, :controller, :patch)
|
|
791
|
+
action ||= assign_deprecated_option(deprecated_options, :action, :patch)
|
|
792
|
+
on ||= assign_deprecated_option(deprecated_options, :on, :patch)
|
|
793
|
+
defaults ||= assign_deprecated_option(deprecated_options, :defaults, :patch)
|
|
794
|
+
constraints ||= assign_deprecated_option(deprecated_options, :constraints, :patch)
|
|
795
|
+
anchor = assign_deprecated_option(deprecated_options, :anchor, :patch) if deprecated_options.key?(:anchor)
|
|
796
|
+
format = assign_deprecated_option(deprecated_options, :format, :patch) if deprecated_options.key?(:format)
|
|
797
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :patch)
|
|
798
|
+
internal ||= assign_deprecated_option(deprecated_options, :internal, :patch)
|
|
799
|
+
assign_deprecated_options(deprecated_options, mapping, :patch)
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
match(*path_or_actions, as:, to:, controller:, action:, on:, defaults:, constraints:, anchor:, format:, path:, internal:, **mapping, via: :patch, &block)
|
|
803
|
+
self
|
|
750
804
|
end
|
|
751
805
|
|
|
752
806
|
# Define a route that only recognizes HTTP PUT. For supported arguments, see
|
|
753
807
|
# [match](rdoc-ref:Base#match)
|
|
754
808
|
#
|
|
755
809
|
# put 'bacon', to: 'food#bacon'
|
|
756
|
-
def put(*
|
|
757
|
-
|
|
810
|
+
def put(*path_or_actions, as: DEFAULT, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &block)
|
|
811
|
+
if path_or_actions.grep(Hash).any? && (deprecated_options = path_or_actions.extract_options!)
|
|
812
|
+
as = assign_deprecated_option(deprecated_options, :as, :put) if deprecated_options.key?(:as)
|
|
813
|
+
to ||= assign_deprecated_option(deprecated_options, :to, :put)
|
|
814
|
+
controller ||= assign_deprecated_option(deprecated_options, :controller, :put)
|
|
815
|
+
action ||= assign_deprecated_option(deprecated_options, :action, :put)
|
|
816
|
+
on ||= assign_deprecated_option(deprecated_options, :on, :put)
|
|
817
|
+
defaults ||= assign_deprecated_option(deprecated_options, :defaults, :put)
|
|
818
|
+
constraints ||= assign_deprecated_option(deprecated_options, :constraints, :put)
|
|
819
|
+
anchor = assign_deprecated_option(deprecated_options, :anchor, :put) if deprecated_options.key?(:anchor)
|
|
820
|
+
format = assign_deprecated_option(deprecated_options, :format, :put) if deprecated_options.key?(:format)
|
|
821
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :put)
|
|
822
|
+
internal ||= assign_deprecated_option(deprecated_options, :internal, :put)
|
|
823
|
+
assign_deprecated_options(deprecated_options, mapping, :put)
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
match(*path_or_actions, as:, to:, controller:, action:, on:, defaults:, constraints:, anchor:, format:, path:, internal:, **mapping, via: :put, &block)
|
|
827
|
+
self
|
|
758
828
|
end
|
|
759
829
|
|
|
760
830
|
# Define a route that only recognizes HTTP DELETE. For supported arguments, see
|
|
761
831
|
# [match](rdoc-ref:Base#match)
|
|
762
832
|
#
|
|
763
833
|
# delete 'broccoli', to: 'food#broccoli'
|
|
764
|
-
def delete(*
|
|
765
|
-
|
|
834
|
+
def delete(*path_or_actions, as: DEFAULT, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &block)
|
|
835
|
+
if path_or_actions.grep(Hash).any? && (deprecated_options = path_or_actions.extract_options!)
|
|
836
|
+
as = assign_deprecated_option(deprecated_options, :as, :delete) if deprecated_options.key?(:as)
|
|
837
|
+
to ||= assign_deprecated_option(deprecated_options, :to, :delete)
|
|
838
|
+
controller ||= assign_deprecated_option(deprecated_options, :controller, :delete)
|
|
839
|
+
action ||= assign_deprecated_option(deprecated_options, :action, :delete)
|
|
840
|
+
on ||= assign_deprecated_option(deprecated_options, :on, :delete)
|
|
841
|
+
defaults ||= assign_deprecated_option(deprecated_options, :defaults, :delete)
|
|
842
|
+
constraints ||= assign_deprecated_option(deprecated_options, :constraints, :delete)
|
|
843
|
+
anchor = assign_deprecated_option(deprecated_options, :anchor, :delete) if deprecated_options.key?(:anchor)
|
|
844
|
+
format = assign_deprecated_option(deprecated_options, :format, :delete) if deprecated_options.key?(:format)
|
|
845
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :delete)
|
|
846
|
+
internal ||= assign_deprecated_option(deprecated_options, :internal, :delete)
|
|
847
|
+
assign_deprecated_options(deprecated_options, mapping, :delete)
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
match(*path_or_actions, as:, to:, controller:, action:, on:, defaults:, constraints:, anchor:, format:, path:, internal:, **mapping, via: :delete, &block)
|
|
851
|
+
self
|
|
766
852
|
end
|
|
767
853
|
|
|
768
854
|
# Define a route that only recognizes HTTP OPTIONS. For supported arguments, see
|
|
769
855
|
# [match](rdoc-ref:Base#match)
|
|
770
856
|
#
|
|
771
857
|
# options 'carrots', to: 'food#carrots'
|
|
772
|
-
def options(*
|
|
773
|
-
|
|
858
|
+
def options(*path_or_actions, as: DEFAULT, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: false, format: false, path: nil, internal: nil, **mapping, &block)
|
|
859
|
+
if path_or_actions.grep(Hash).any? && (deprecated_options = path_or_actions.extract_options!)
|
|
860
|
+
as = assign_deprecated_option(deprecated_options, :as, :options) if deprecated_options.key?(:as)
|
|
861
|
+
to ||= assign_deprecated_option(deprecated_options, :to, :options)
|
|
862
|
+
controller ||= assign_deprecated_option(deprecated_options, :controller, :options)
|
|
863
|
+
action ||= assign_deprecated_option(deprecated_options, :action, :options)
|
|
864
|
+
on ||= assign_deprecated_option(deprecated_options, :on, :options)
|
|
865
|
+
defaults ||= assign_deprecated_option(deprecated_options, :defaults, :options)
|
|
866
|
+
constraints ||= assign_deprecated_option(deprecated_options, :constraints, :options)
|
|
867
|
+
anchor = assign_deprecated_option(deprecated_options, :anchor, :options) if deprecated_options.key?(:anchor)
|
|
868
|
+
format = assign_deprecated_option(deprecated_options, :format, :options) if deprecated_options.key?(:format)
|
|
869
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :options)
|
|
870
|
+
internal ||= assign_deprecated_option(deprecated_options, :internal, :options)
|
|
871
|
+
assign_deprecated_options(deprecated_options, mapping, :options)
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
match(*path_or_actions, as:, to:, controller:, action:, on:, defaults:, constraints:, anchor:, format:, path:, internal:, **mapping, via: :options, &block)
|
|
875
|
+
self
|
|
774
876
|
end
|
|
775
877
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
878
|
+
# Define a route that recognizes HTTP CONNECT (and GET) requests. More
|
|
879
|
+
# specifically this recognizes HTTP/1 protocol upgrade requests and HTTP/2
|
|
880
|
+
# CONNECT requests with the protocol pseudo header. For supported arguments,
|
|
881
|
+
# see [match](rdoc-ref:Base#match)
|
|
882
|
+
#
|
|
883
|
+
# connect 'live', to: 'live#index'
|
|
884
|
+
def connect(*path_or_actions, as: DEFAULT, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: false, format: false, path: nil, internal: nil, **mapping, &block)
|
|
885
|
+
if path_or_actions.grep(Hash).any? && (deprecated_options = path_or_actions.extract_options!)
|
|
886
|
+
as = assign_deprecated_option(deprecated_options, :as, :connect) if deprecated_options.key?(:as)
|
|
887
|
+
to ||= assign_deprecated_option(deprecated_options, :to, :connect)
|
|
888
|
+
controller ||= assign_deprecated_option(deprecated_options, :controller, :connect)
|
|
889
|
+
action ||= assign_deprecated_option(deprecated_options, :action, :connect)
|
|
890
|
+
on ||= assign_deprecated_option(deprecated_options, :on, :connect)
|
|
891
|
+
defaults ||= assign_deprecated_option(deprecated_options, :defaults, :connect)
|
|
892
|
+
constraints ||= assign_deprecated_option(deprecated_options, :constraints, :connect)
|
|
893
|
+
anchor = assign_deprecated_option(deprecated_options, :anchor, :connect) if deprecated_options.key?(:anchor)
|
|
894
|
+
format = assign_deprecated_option(deprecated_options, :format, :connect) if deprecated_options.key?(:format)
|
|
895
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :connect)
|
|
896
|
+
internal ||= assign_deprecated_option(deprecated_options, :internal, :connect)
|
|
897
|
+
assign_deprecated_options(deprecated_options, mapping, :connect)
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
match(*path_or_actions, as:, to:, controller:, action:, on:, defaults:, constraints:, anchor:, format:, path:, internal:, **mapping, via: [:get, :connect], &block)
|
|
901
|
+
self
|
|
902
|
+
end
|
|
783
903
|
end
|
|
784
904
|
|
|
785
905
|
# You may wish to organize groups of controllers under a namespace. Most
|
|
@@ -866,8 +986,13 @@ module ActionDispatch
|
|
|
866
986
|
# scope as: "sekret" do
|
|
867
987
|
# resources :posts
|
|
868
988
|
# end
|
|
869
|
-
def scope(*args)
|
|
870
|
-
|
|
989
|
+
def scope(*args, only: nil, except: nil, **options)
|
|
990
|
+
if args.grep(Hash).any? && (deprecated_options = args.extract_options!)
|
|
991
|
+
only ||= assign_deprecated_option(deprecated_options, :only, :scope)
|
|
992
|
+
only ||= assign_deprecated_option(deprecated_options, :except, :scope)
|
|
993
|
+
assign_deprecated_options(deprecated_options, options, :scope)
|
|
994
|
+
end
|
|
995
|
+
|
|
871
996
|
scope = {}
|
|
872
997
|
|
|
873
998
|
options[:path] = args.flatten.join("/") if args.any?
|
|
@@ -888,9 +1013,8 @@ module ActionDispatch
|
|
|
888
1013
|
block, options[:constraints] = options[:constraints], {}
|
|
889
1014
|
end
|
|
890
1015
|
|
|
891
|
-
if
|
|
892
|
-
scope[:action_options] = { only:
|
|
893
|
-
except: options.delete(:except) }
|
|
1016
|
+
if only || except
|
|
1017
|
+
scope[:action_options] = { only:, except: }
|
|
894
1018
|
end
|
|
895
1019
|
|
|
896
1020
|
if options.key? :anchor
|
|
@@ -970,18 +1094,24 @@ module ActionDispatch
|
|
|
970
1094
|
# namespace :admin, as: "sekret" do
|
|
971
1095
|
# resources :posts
|
|
972
1096
|
# end
|
|
973
|
-
def namespace(
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1097
|
+
def namespace(name, deprecated_options = nil, as: DEFAULT, path: DEFAULT, shallow_path: DEFAULT, shallow_prefix: DEFAULT, **options, &block)
|
|
1098
|
+
if deprecated_options.is_a?(Hash)
|
|
1099
|
+
as = assign_deprecated_option(deprecated_options, :as, :namespace) if deprecated_options.key?(:as)
|
|
1100
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :namespace) if deprecated_options.key?(:path)
|
|
1101
|
+
shallow_path ||= assign_deprecated_option(deprecated_options, :shallow_path, :namespace) if deprecated_options.key?(:shallow_path)
|
|
1102
|
+
shallow_prefix ||= assign_deprecated_option(deprecated_options, :shallow_prefix, :namespace) if deprecated_options.key?(:shallow_prefix)
|
|
1103
|
+
assign_deprecated_options(deprecated_options, options, :namespace)
|
|
1104
|
+
end
|
|
1105
|
+
|
|
1106
|
+
name = name.to_s
|
|
1107
|
+
options[:module] ||= name
|
|
1108
|
+
as = name if as == DEFAULT
|
|
1109
|
+
path = name if path == DEFAULT
|
|
1110
|
+
shallow_path = path if shallow_path == DEFAULT
|
|
1111
|
+
shallow_prefix = as if shallow_prefix == DEFAULT
|
|
982
1112
|
|
|
983
|
-
path_scope(
|
|
984
|
-
scope(
|
|
1113
|
+
path_scope(path) do
|
|
1114
|
+
scope(**options, as:, shallow_path:, shallow_prefix:, &block)
|
|
985
1115
|
end
|
|
986
1116
|
end
|
|
987
1117
|
|
|
@@ -1174,13 +1304,29 @@ module ActionDispatch
|
|
|
1174
1304
|
CANONICAL_ACTIONS = %w(index create new show update destroy)
|
|
1175
1305
|
|
|
1176
1306
|
class Resource # :nodoc:
|
|
1307
|
+
class << self
|
|
1308
|
+
def default_actions(api_only)
|
|
1309
|
+
if api_only
|
|
1310
|
+
[:index, :create, :show, :update, :destroy]
|
|
1311
|
+
else
|
|
1312
|
+
[:index, :create, :new, :show, :update, :destroy, :edit]
|
|
1313
|
+
end
|
|
1314
|
+
end
|
|
1315
|
+
end
|
|
1316
|
+
|
|
1177
1317
|
attr_reader :controller, :path, :param
|
|
1178
1318
|
|
|
1179
|
-
def initialize(entities, api_only, shallow,
|
|
1319
|
+
def initialize(entities, api_only, shallow, only: nil, except: nil, **options)
|
|
1180
1320
|
if options[:param].to_s.include?(":")
|
|
1181
1321
|
raise ArgumentError, ":param option can't contain colons"
|
|
1182
1322
|
end
|
|
1183
1323
|
|
|
1324
|
+
valid_actions = self.class.default_actions(false) # ignore api_only for this validation
|
|
1325
|
+
if (invalid_actions = invalid_only_except_options(valid_actions, only:, except:).presence)
|
|
1326
|
+
error_prefix = "Route `resource#{"s" unless singleton?} :#{entities}`"
|
|
1327
|
+
raise ArgumentError, "#{error_prefix} - :only and :except must include only #{valid_actions}, but also included #{invalid_actions}"
|
|
1328
|
+
end
|
|
1329
|
+
|
|
1184
1330
|
@name = entities.to_s
|
|
1185
1331
|
@path = (options[:path] || @name).to_s
|
|
1186
1332
|
@controller = (options[:controller] || @name).to_s
|
|
@@ -1189,16 +1335,12 @@ module ActionDispatch
|
|
|
1189
1335
|
@options = options
|
|
1190
1336
|
@shallow = shallow
|
|
1191
1337
|
@api_only = api_only
|
|
1192
|
-
@only =
|
|
1193
|
-
@except =
|
|
1338
|
+
@only = only
|
|
1339
|
+
@except = except
|
|
1194
1340
|
end
|
|
1195
1341
|
|
|
1196
1342
|
def default_actions
|
|
1197
|
-
|
|
1198
|
-
[:index, :create, :show, :update, :destroy]
|
|
1199
|
-
else
|
|
1200
|
-
[:index, :create, :new, :show, :update, :destroy, :edit]
|
|
1201
|
-
end
|
|
1343
|
+
self.class.default_actions(@api_only)
|
|
1202
1344
|
end
|
|
1203
1345
|
|
|
1204
1346
|
def actions
|
|
@@ -1266,10 +1408,25 @@ module ActionDispatch
|
|
|
1266
1408
|
end
|
|
1267
1409
|
|
|
1268
1410
|
def singleton?; false; end
|
|
1411
|
+
|
|
1412
|
+
private
|
|
1413
|
+
def invalid_only_except_options(valid_actions, only:, except:)
|
|
1414
|
+
[only, except].flatten.compact.uniq.map(&:to_sym) - valid_actions
|
|
1415
|
+
end
|
|
1269
1416
|
end
|
|
1270
1417
|
|
|
1271
1418
|
class SingletonResource < Resource # :nodoc:
|
|
1272
|
-
|
|
1419
|
+
class << self
|
|
1420
|
+
def default_actions(api_only)
|
|
1421
|
+
if api_only
|
|
1422
|
+
[:show, :create, :update, :destroy]
|
|
1423
|
+
else
|
|
1424
|
+
[:show, :create, :update, :destroy, :new, :edit]
|
|
1425
|
+
end
|
|
1426
|
+
end
|
|
1427
|
+
end
|
|
1428
|
+
|
|
1429
|
+
def initialize(entities, api_only, shallow, **options)
|
|
1273
1430
|
super
|
|
1274
1431
|
@as = nil
|
|
1275
1432
|
@controller = (options[:controller] || plural).to_s
|
|
@@ -1277,11 +1434,7 @@ module ActionDispatch
|
|
|
1277
1434
|
end
|
|
1278
1435
|
|
|
1279
1436
|
def default_actions
|
|
1280
|
-
|
|
1281
|
-
[:show, :create, :update, :destroy]
|
|
1282
|
-
else
|
|
1283
|
-
[:show, :create, :update, :destroy, :new, :edit]
|
|
1284
|
-
end
|
|
1437
|
+
self.class.default_actions(@api_only)
|
|
1285
1438
|
end
|
|
1286
1439
|
|
|
1287
1440
|
def plural
|
|
@@ -1334,19 +1487,22 @@ module ActionDispatch
|
|
|
1334
1487
|
#
|
|
1335
1488
|
# ### Options
|
|
1336
1489
|
# Takes same options as [resources](rdoc-ref:#resources)
|
|
1337
|
-
def resource(*resources, &block)
|
|
1338
|
-
|
|
1490
|
+
def resource(*resources, concerns: nil, **options, &block)
|
|
1491
|
+
if resources.grep(Hash).any? && (deprecated_options = resources.extract_options!)
|
|
1492
|
+
concerns = assign_deprecated_option(deprecated_options, :concerns, :resource) if deprecated_options.key?(:concerns)
|
|
1493
|
+
assign_deprecated_options(deprecated_options, options, :resource)
|
|
1494
|
+
end
|
|
1339
1495
|
|
|
1340
|
-
if apply_common_behavior_for(:resource, resources, options, &block)
|
|
1496
|
+
if apply_common_behavior_for(:resource, resources, concerns:, **options, &block)
|
|
1341
1497
|
return self
|
|
1342
1498
|
end
|
|
1343
1499
|
|
|
1344
1500
|
with_scope_level(:resource) do
|
|
1345
|
-
options = apply_action_options options
|
|
1346
|
-
resource_scope(SingletonResource.new(resources.pop, api_only?, @scope[:shallow], options)) do
|
|
1501
|
+
options = apply_action_options :resource, options
|
|
1502
|
+
resource_scope(SingletonResource.new(resources.pop, api_only?, @scope[:shallow], **options)) do
|
|
1347
1503
|
yield if block_given?
|
|
1348
1504
|
|
|
1349
|
-
concerns(
|
|
1505
|
+
concerns(*concerns) if concerns
|
|
1350
1506
|
|
|
1351
1507
|
new do
|
|
1352
1508
|
get :new
|
|
@@ -1504,19 +1660,22 @@ module ActionDispatch
|
|
|
1504
1660
|
#
|
|
1505
1661
|
# # resource actions are at /admin/posts.
|
|
1506
1662
|
# resources :posts, path: "admin/posts"
|
|
1507
|
-
def resources(*resources, &block)
|
|
1508
|
-
|
|
1663
|
+
def resources(*resources, concerns: nil, **options, &block)
|
|
1664
|
+
if resources.grep(Hash).any? && (deprecated_options = resources.extract_options!)
|
|
1665
|
+
concerns = assign_deprecated_option(deprecated_options, :concerns, :resources) if deprecated_options.key?(:concerns)
|
|
1666
|
+
assign_deprecated_options(deprecated_options, options, :resources)
|
|
1667
|
+
end
|
|
1509
1668
|
|
|
1510
|
-
if apply_common_behavior_for(:resources, resources, options, &block)
|
|
1669
|
+
if apply_common_behavior_for(:resources, resources, concerns:, **options, &block)
|
|
1511
1670
|
return self
|
|
1512
1671
|
end
|
|
1513
1672
|
|
|
1514
1673
|
with_scope_level(:resources) do
|
|
1515
|
-
options = apply_action_options options
|
|
1516
|
-
resource_scope(Resource.new(resources.pop, api_only?, @scope[:shallow], options)) do
|
|
1674
|
+
options = apply_action_options :resources, options
|
|
1675
|
+
resource_scope(Resource.new(resources.pop, api_only?, @scope[:shallow], **options)) do
|
|
1517
1676
|
yield if block_given?
|
|
1518
1677
|
|
|
1519
|
-
concerns(
|
|
1678
|
+
concerns(*concerns) if concerns
|
|
1520
1679
|
|
|
1521
1680
|
collection do
|
|
1522
1681
|
get :index if parent_resource.actions.include?(:index)
|
|
@@ -1601,19 +1760,19 @@ module ActionDispatch
|
|
|
1601
1760
|
if shallow? && shallow_nesting_depth >= 1
|
|
1602
1761
|
shallow_scope do
|
|
1603
1762
|
path_scope(parent_resource.nested_scope) do
|
|
1604
|
-
scope(nested_options, &block)
|
|
1763
|
+
scope(**nested_options, &block)
|
|
1605
1764
|
end
|
|
1606
1765
|
end
|
|
1607
1766
|
else
|
|
1608
1767
|
path_scope(parent_resource.nested_scope) do
|
|
1609
|
-
scope(nested_options, &block)
|
|
1768
|
+
scope(**nested_options, &block)
|
|
1610
1769
|
end
|
|
1611
1770
|
end
|
|
1612
1771
|
end
|
|
1613
1772
|
end
|
|
1614
1773
|
|
|
1615
1774
|
# See ActionDispatch::Routing::Mapper::Scoping#namespace.
|
|
1616
|
-
def namespace(path, options
|
|
1775
|
+
def namespace(name, deprecated_options = nil, as: DEFAULT, path: DEFAULT, shallow_path: DEFAULT, shallow_prefix: DEFAULT, **options, &block)
|
|
1617
1776
|
if resource_scope?
|
|
1618
1777
|
nested { super }
|
|
1619
1778
|
else
|
|
@@ -1673,40 +1832,58 @@ module ActionDispatch
|
|
|
1673
1832
|
# Matches a URL pattern to one or more routes. For more information, see
|
|
1674
1833
|
# [match](rdoc-ref:Base#match).
|
|
1675
1834
|
#
|
|
1676
|
-
# match 'path' => 'controller#action', via: :patch
|
|
1677
1835
|
# match 'path', to: 'controller#action', via: :post
|
|
1678
|
-
# match '
|
|
1679
|
-
def match(path,
|
|
1680
|
-
if
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1836
|
+
# match 'otherpath', on: :member, via: :get
|
|
1837
|
+
def match(*path_or_actions, as: DEFAULT, via: nil, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &block)
|
|
1838
|
+
if path_or_actions.grep(Hash).any? && (deprecated_options = path_or_actions.extract_options!)
|
|
1839
|
+
as = assign_deprecated_option(deprecated_options, :as, :match) if deprecated_options.key?(:as)
|
|
1840
|
+
via ||= assign_deprecated_option(deprecated_options, :via, :match)
|
|
1841
|
+
to ||= assign_deprecated_option(deprecated_options, :to, :match)
|
|
1842
|
+
controller ||= assign_deprecated_option(deprecated_options, :controller, :match)
|
|
1843
|
+
action ||= assign_deprecated_option(deprecated_options, :action, :match)
|
|
1844
|
+
on ||= assign_deprecated_option(deprecated_options, :on, :match)
|
|
1845
|
+
defaults ||= assign_deprecated_option(deprecated_options, :defaults, :match)
|
|
1846
|
+
constraints ||= assign_deprecated_option(deprecated_options, :constraints, :match)
|
|
1847
|
+
anchor = assign_deprecated_option(deprecated_options, :anchor, :match) if deprecated_options.key?(:anchor)
|
|
1848
|
+
format = assign_deprecated_option(deprecated_options, :format, :match) if deprecated_options.key?(:format)
|
|
1849
|
+
path ||= assign_deprecated_option(deprecated_options, :path, :match)
|
|
1850
|
+
internal ||= assign_deprecated_option(deprecated_options, :internal, :match)
|
|
1851
|
+
assign_deprecated_options(deprecated_options, mapping, :match)
|
|
1852
|
+
end
|
|
1853
|
+
|
|
1854
|
+
raise ArgumentError, "Wrong number of arguments (expect 1, got #{path_or_actions.count})" if path_or_actions.count > 1
|
|
1855
|
+
|
|
1856
|
+
if path_or_actions.none? && mapping.any?
|
|
1857
|
+
hash_path, hash_to = mapping.find { |key, _| key.is_a?(String) }
|
|
1858
|
+
if hash_path.nil?
|
|
1859
|
+
raise ArgumentError, "Route path not specified"
|
|
1860
|
+
else
|
|
1861
|
+
mapping.delete(hash_path)
|
|
1862
|
+
end
|
|
1685
1863
|
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1864
|
+
if hash_path
|
|
1865
|
+
path_or_actions.push hash_path
|
|
1866
|
+
case hash_to
|
|
1867
|
+
when Symbol
|
|
1868
|
+
action ||= hash_to
|
|
1869
|
+
when String
|
|
1870
|
+
if hash_to.include?("#")
|
|
1871
|
+
to ||= hash_to
|
|
1872
|
+
else
|
|
1873
|
+
controller ||= hash_to
|
|
1874
|
+
end
|
|
1692
1875
|
else
|
|
1693
|
-
|
|
1876
|
+
to ||= hash_to
|
|
1694
1877
|
end
|
|
1695
|
-
else
|
|
1696
|
-
options[:to] = to
|
|
1697
1878
|
end
|
|
1698
|
-
|
|
1699
|
-
options.delete(path)
|
|
1700
|
-
paths = [path]
|
|
1701
|
-
else
|
|
1702
|
-
options = rest.pop || {}
|
|
1703
|
-
paths = [path] + rest
|
|
1704
1879
|
end
|
|
1705
1880
|
|
|
1706
|
-
|
|
1707
|
-
defaults
|
|
1708
|
-
|
|
1709
|
-
|
|
1881
|
+
path_or_actions.each do |path_or_action|
|
|
1882
|
+
if defaults
|
|
1883
|
+
defaults(defaults) { map_match(path_or_action, as:, via:, to:, controller:, action:, on:, constraints:, anchor:, format:, path:, internal:, mapping:, &block) }
|
|
1884
|
+
else
|
|
1885
|
+
map_match(path_or_action, as:, via:, to:, controller:, action:, on:, constraints:, anchor:, format:, path:, internal:, mapping:, &block)
|
|
1886
|
+
end
|
|
1710
1887
|
end
|
|
1711
1888
|
end
|
|
1712
1889
|
|
|
@@ -1748,22 +1925,21 @@ module ActionDispatch
|
|
|
1748
1925
|
@scope[:scope_level_resource]
|
|
1749
1926
|
end
|
|
1750
1927
|
|
|
1751
|
-
def apply_common_behavior_for(method, resources, options, &block)
|
|
1928
|
+
def apply_common_behavior_for(method, resources, shallow: nil, **options, &block)
|
|
1752
1929
|
if resources.length > 1
|
|
1753
|
-
resources.each { |r| public_send(method, r, options, &block) }
|
|
1930
|
+
resources.each { |r| public_send(method, r, shallow:, **options, &block) }
|
|
1754
1931
|
return true
|
|
1755
1932
|
end
|
|
1756
1933
|
|
|
1757
|
-
if
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
public_send(method, resources.pop, options, &block)
|
|
1934
|
+
if shallow
|
|
1935
|
+
self.shallow do
|
|
1936
|
+
public_send(method, resources.pop, **options, &block)
|
|
1761
1937
|
end
|
|
1762
1938
|
return true
|
|
1763
1939
|
end
|
|
1764
1940
|
|
|
1765
1941
|
if resource_scope?
|
|
1766
|
-
nested { public_send(method, resources.pop, options, &block) }
|
|
1942
|
+
nested { public_send(method, resources.pop, shallow:, **options, &block) }
|
|
1767
1943
|
return true
|
|
1768
1944
|
end
|
|
1769
1945
|
|
|
@@ -1772,9 +1948,11 @@ module ActionDispatch
|
|
|
1772
1948
|
end
|
|
1773
1949
|
|
|
1774
1950
|
scope_options = options.slice!(*RESOURCE_OPTIONS)
|
|
1951
|
+
scope_options[:shallow] = shallow unless shallow.nil?
|
|
1952
|
+
|
|
1775
1953
|
unless scope_options.empty?
|
|
1776
|
-
scope(scope_options) do
|
|
1777
|
-
public_send(method, resources.pop, options, &block)
|
|
1954
|
+
scope(**scope_options) do
|
|
1955
|
+
public_send(method, resources.pop, **options, &block)
|
|
1778
1956
|
end
|
|
1779
1957
|
return true
|
|
1780
1958
|
end
|
|
@@ -1782,17 +1960,32 @@ module ActionDispatch
|
|
|
1782
1960
|
false
|
|
1783
1961
|
end
|
|
1784
1962
|
|
|
1785
|
-
def apply_action_options(options)
|
|
1963
|
+
def apply_action_options(method, options)
|
|
1786
1964
|
return options if action_options? options
|
|
1787
|
-
options.merge scope_action_options
|
|
1965
|
+
options.merge scope_action_options(method)
|
|
1788
1966
|
end
|
|
1789
1967
|
|
|
1790
1968
|
def action_options?(options)
|
|
1791
1969
|
options[:only] || options[:except]
|
|
1792
1970
|
end
|
|
1793
1971
|
|
|
1794
|
-
def scope_action_options
|
|
1795
|
-
@scope[:action_options]
|
|
1972
|
+
def scope_action_options(method)
|
|
1973
|
+
return {} unless @scope[:action_options]
|
|
1974
|
+
|
|
1975
|
+
actions = applicable_actions_for(method)
|
|
1976
|
+
@scope[:action_options].dup.tap do |options|
|
|
1977
|
+
(options[:only] = Array(options[:only]) & actions) if options[:only]
|
|
1978
|
+
(options[:except] = Array(options[:except]) & actions) if options[:except]
|
|
1979
|
+
end
|
|
1980
|
+
end
|
|
1981
|
+
|
|
1982
|
+
def applicable_actions_for(method)
|
|
1983
|
+
case method
|
|
1984
|
+
when :resource
|
|
1985
|
+
SingletonResource.default_actions(api_only?)
|
|
1986
|
+
when :resources
|
|
1987
|
+
Resource.default_actions(api_only?)
|
|
1988
|
+
end
|
|
1796
1989
|
end
|
|
1797
1990
|
|
|
1798
1991
|
def resource_scope?
|
|
@@ -1850,9 +2043,10 @@ module ActionDispatch
|
|
|
1850
2043
|
end
|
|
1851
2044
|
|
|
1852
2045
|
def shallow_scope
|
|
1853
|
-
scope =
|
|
1854
|
-
|
|
1855
|
-
|
|
2046
|
+
@scope = @scope.new(
|
|
2047
|
+
as: @scope[:shallow_prefix],
|
|
2048
|
+
path: @scope[:shallow_path],
|
|
2049
|
+
)
|
|
1856
2050
|
|
|
1857
2051
|
yield
|
|
1858
2052
|
ensure
|
|
@@ -1874,7 +2068,7 @@ module ActionDispatch
|
|
|
1874
2068
|
end
|
|
1875
2069
|
|
|
1876
2070
|
def prefix_name_for_action(as, action)
|
|
1877
|
-
if as
|
|
2071
|
+
if as && as != DEFAULT
|
|
1878
2072
|
prefix = as
|
|
1879
2073
|
elsif !canonical_action?(action)
|
|
1880
2074
|
prefix = action
|
|
@@ -1890,7 +2084,7 @@ module ActionDispatch
|
|
|
1890
2084
|
name_prefix = @scope[:as]
|
|
1891
2085
|
|
|
1892
2086
|
if parent_resource
|
|
1893
|
-
return nil unless as || action
|
|
2087
|
+
return nil unless as != DEFAULT || action
|
|
1894
2088
|
|
|
1895
2089
|
collection_name = parent_resource.collection_name
|
|
1896
2090
|
member_name = parent_resource.member_name
|
|
@@ -1903,7 +2097,7 @@ module ActionDispatch
|
|
|
1903
2097
|
# If a name was not explicitly given, we check if it is valid and return nil in
|
|
1904
2098
|
# case it isn't. Otherwise, we pass the invalid name forward so the underlying
|
|
1905
2099
|
# router engine treats it and raises an exception.
|
|
1906
|
-
if as
|
|
2100
|
+
if as == DEFAULT
|
|
1907
2101
|
candidate unless !candidate.match?(/\A[_a-z]/i) || has_named_route?(candidate)
|
|
1908
2102
|
else
|
|
1909
2103
|
candidate
|
|
@@ -1934,42 +2128,35 @@ module ActionDispatch
|
|
|
1934
2128
|
@scope = @scope.parent
|
|
1935
2129
|
end
|
|
1936
2130
|
|
|
1937
|
-
def map_match(
|
|
1938
|
-
if
|
|
2131
|
+
def map_match(path_or_action, constraints: nil, anchor: nil, format: nil, path: nil, as: DEFAULT, via: nil, to: nil, controller: nil, action: nil, on: nil, internal: nil, mapping: nil)
|
|
2132
|
+
if on && !VALID_ON_OPTIONS.include?(on)
|
|
1939
2133
|
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
|
|
1940
2134
|
end
|
|
1941
2135
|
|
|
1942
2136
|
if @scope[:to]
|
|
1943
|
-
|
|
2137
|
+
to ||= @scope[:to]
|
|
1944
2138
|
end
|
|
1945
2139
|
|
|
1946
2140
|
if @scope[:controller] && @scope[:action]
|
|
1947
|
-
|
|
2141
|
+
to ||= "#{@scope[:controller]}##{@scope[:action]}"
|
|
1948
2142
|
end
|
|
1949
2143
|
|
|
1950
|
-
controller
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
path_types = paths.group_by(&:class)
|
|
1961
|
-
(path_types[String] || []).each do |_path|
|
|
1962
|
-
route_options = options.dup
|
|
1963
|
-
if _path && option_path
|
|
2144
|
+
controller ||= @scope[:controller]
|
|
2145
|
+
via = Mapping.check_via Array(via || @scope[:via])
|
|
2146
|
+
format ||= @scope[:format] if format.nil?
|
|
2147
|
+
anchor ||= true if anchor.nil?
|
|
2148
|
+
constraints ||= {}
|
|
2149
|
+
|
|
2150
|
+
case path_or_action
|
|
2151
|
+
when String
|
|
2152
|
+
if path_or_action && path
|
|
1964
2153
|
raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings."
|
|
1965
2154
|
end
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
route_options = options.dup
|
|
1972
|
-
decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
|
|
2155
|
+
path = path_or_action
|
|
2156
|
+
to = get_to_from_path(path_or_action, to, action)
|
|
2157
|
+
decomposed_match(path, controller, as, action, path, to, via, format, anchor, constraints, internal, mapping, on)
|
|
2158
|
+
when Symbol
|
|
2159
|
+
decomposed_match(path_or_action, controller, as, action, path, to, via, format, anchor, constraints, internal, mapping, on)
|
|
1973
2160
|
end
|
|
1974
2161
|
|
|
1975
2162
|
self
|
|
@@ -1990,28 +2177,28 @@ module ActionDispatch
|
|
|
1990
2177
|
%r{^/?[-\w]+/[-\w/]+$}.match?(path)
|
|
1991
2178
|
end
|
|
1992
2179
|
|
|
1993
|
-
def decomposed_match(path, controller,
|
|
1994
|
-
if on
|
|
1995
|
-
send(on) { decomposed_match(path, controller,
|
|
2180
|
+
def decomposed_match(path, controller, as, action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping, on = nil)
|
|
2181
|
+
if on
|
|
2182
|
+
send(on) { decomposed_match(path, controller, as, action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping) }
|
|
1996
2183
|
else
|
|
1997
2184
|
case @scope.scope_level
|
|
1998
2185
|
when :resources
|
|
1999
|
-
nested { decomposed_match(path, controller,
|
|
2186
|
+
nested { decomposed_match(path, controller, as, action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping) }
|
|
2000
2187
|
when :resource
|
|
2001
|
-
member { decomposed_match(path, controller,
|
|
2188
|
+
member { decomposed_match(path, controller, as, action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping) }
|
|
2002
2189
|
else
|
|
2003
|
-
add_route(path, controller,
|
|
2190
|
+
add_route(path, controller, as, action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping)
|
|
2004
2191
|
end
|
|
2005
2192
|
end
|
|
2006
2193
|
end
|
|
2007
2194
|
|
|
2008
|
-
def add_route(action, controller,
|
|
2195
|
+
def add_route(action, controller, as, options_action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping)
|
|
2009
2196
|
path = path_for_action(action, _path)
|
|
2010
2197
|
raise ArgumentError, "path is required" if path.blank?
|
|
2011
2198
|
|
|
2012
2199
|
action = action.to_s
|
|
2013
2200
|
|
|
2014
|
-
default_action =
|
|
2201
|
+
default_action = options_action || @scope[:action]
|
|
2015
2202
|
|
|
2016
2203
|
if /^[\w\-\/]+$/.match?(action)
|
|
2017
2204
|
default_action ||= action.tr("-", "_") unless action.include?("/")
|
|
@@ -2019,22 +2206,16 @@ module ActionDispatch
|
|
|
2019
2206
|
action = nil
|
|
2020
2207
|
end
|
|
2021
2208
|
|
|
2022
|
-
as
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
name_for_action(options.delete(:as), action)
|
|
2026
|
-
end
|
|
2027
|
-
|
|
2028
|
-
path = Mapping.normalize_path RFC2396_PARSER.escape(path), formatted
|
|
2029
|
-
ast = Journey::Parser.parse path
|
|
2209
|
+
as = name_for_action(as, action) if as
|
|
2210
|
+
path = Mapping.normalize_path URI::RFC2396_PARSER.escape(path), formatted
|
|
2211
|
+
ast = Journey::Parser.parse path
|
|
2030
2212
|
|
|
2031
|
-
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor,
|
|
2213
|
+
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, internal, options_mapping)
|
|
2032
2214
|
@set.add_route(mapping, as)
|
|
2033
2215
|
end
|
|
2034
2216
|
|
|
2035
2217
|
def match_root_route(options)
|
|
2036
|
-
|
|
2037
|
-
match(*args)
|
|
2218
|
+
match("/", as: :root, via: :get, **options)
|
|
2038
2219
|
end
|
|
2039
2220
|
end
|
|
2040
2221
|
|
|
@@ -2129,8 +2310,7 @@ module ActionDispatch
|
|
|
2129
2310
|
# namespace :posts do
|
|
2130
2311
|
# concerns :commentable
|
|
2131
2312
|
# end
|
|
2132
|
-
def concerns(*args)
|
|
2133
|
-
options = args.extract_options!
|
|
2313
|
+
def concerns(*args, **options)
|
|
2134
2314
|
args.flatten.each do |name|
|
|
2135
2315
|
if concern = @concerns[name]
|
|
2136
2316
|
concern.call(self, options)
|
|
@@ -2200,8 +2380,8 @@ module ActionDispatch
|
|
|
2200
2380
|
end
|
|
2201
2381
|
|
|
2202
2382
|
# 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:
|
|
2383
|
+
# of `polymorphic_url` and consequently the behavior of `link_to`, `form_with`
|
|
2384
|
+
# and `form_for` when passed a model instance, e.g:
|
|
2205
2385
|
#
|
|
2206
2386
|
# resource :basket
|
|
2207
2387
|
#
|
|
@@ -2210,7 +2390,7 @@ module ActionDispatch
|
|
|
2210
2390
|
# end
|
|
2211
2391
|
#
|
|
2212
2392
|
# This will now generate "/basket" when a `Basket` instance is passed to
|
|
2213
|
-
# `link_to` or `form_for` instead of the standard "/baskets/:id".
|
|
2393
|
+
# `link_to`, `form_with` or `form_for` instead of the standard "/baskets/:id".
|
|
2214
2394
|
#
|
|
2215
2395
|
# NOTE: This custom behavior only applies to simple polymorphic URLs where a
|
|
2216
2396
|
# single model instance is passed and not more complicated forms, e.g:
|
|
@@ -2267,9 +2447,9 @@ module ActionDispatch
|
|
|
2267
2447
|
|
|
2268
2448
|
attr_reader :parent, :scope_level
|
|
2269
2449
|
|
|
2270
|
-
def initialize(hash, parent =
|
|
2271
|
-
@hash = hash
|
|
2450
|
+
def initialize(hash, parent = ROOT, scope_level = nil)
|
|
2272
2451
|
@parent = parent
|
|
2452
|
+
@hash = parent ? parent.frame.merge(hash) : hash
|
|
2273
2453
|
@scope_level = scope_level
|
|
2274
2454
|
end
|
|
2275
2455
|
|
|
@@ -2282,7 +2462,7 @@ module ActionDispatch
|
|
|
2282
2462
|
end
|
|
2283
2463
|
|
|
2284
2464
|
def root?
|
|
2285
|
-
@parent
|
|
2465
|
+
@parent == ROOT
|
|
2286
2466
|
end
|
|
2287
2467
|
|
|
2288
2468
|
def resources?
|
|
@@ -2327,25 +2507,26 @@ module ActionDispatch
|
|
|
2327
2507
|
end
|
|
2328
2508
|
|
|
2329
2509
|
def [](key)
|
|
2330
|
-
|
|
2331
|
-
scope && scope.frame[key]
|
|
2510
|
+
frame[key]
|
|
2332
2511
|
end
|
|
2333
2512
|
|
|
2513
|
+
def frame; @hash; end
|
|
2514
|
+
|
|
2334
2515
|
include Enumerable
|
|
2335
2516
|
|
|
2336
2517
|
def each
|
|
2337
2518
|
node = self
|
|
2338
|
-
until node.equal?
|
|
2519
|
+
until node.equal? ROOT
|
|
2339
2520
|
yield node
|
|
2340
2521
|
node = node.parent
|
|
2341
2522
|
end
|
|
2342
2523
|
end
|
|
2343
2524
|
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
NULL = Scope.new(nil, nil)
|
|
2525
|
+
ROOT = Scope.new({}, nil)
|
|
2347
2526
|
end
|
|
2348
2527
|
|
|
2528
|
+
DEFAULT = Object.new # :nodoc:
|
|
2529
|
+
|
|
2349
2530
|
def initialize(set) # :nodoc:
|
|
2350
2531
|
@set = set
|
|
2351
2532
|
@draw_paths = set.draw_paths
|