actionpack 7.1.5.1 → 8.1.2
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 +308 -523
- data/README.rdoc +1 -1
- data/lib/abstract_controller/asset_paths.rb +6 -2
- data/lib/abstract_controller/base.rb +104 -105
- data/lib/abstract_controller/caching/fragments.rb +50 -53
- data/lib/abstract_controller/caching.rb +8 -3
- data/lib/abstract_controller/callbacks.rb +70 -62
- data/lib/abstract_controller/collector.rb +7 -7
- data/lib/abstract_controller/deprecator.rb +2 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +71 -84
- data/lib/abstract_controller/logger.rb +4 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +13 -13
- data/lib/abstract_controller/translation.rb +12 -13
- data/lib/abstract_controller/url_for.rb +8 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +76 -72
- data/lib/action_controller/base.rb +199 -126
- data/lib/action_controller/caching.rb +16 -14
- data/lib/action_controller/deprecator.rb +2 -0
- data/lib/action_controller/form_builder.rb +21 -18
- data/lib/action_controller/log_subscriber.rb +23 -2
- data/lib/action_controller/metal/allow_browser.rb +133 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +217 -175
- data/lib/action_controller/metal/content_security_policy.rb +25 -24
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +72 -63
- data/lib/action_controller/metal/default_headers.rb +5 -3
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
- data/lib/action_controller/metal/exceptions.rb +16 -9
- data/lib/action_controller/metal/flash.rb +13 -14
- data/lib/action_controller/metal/head.rb +15 -11
- data/lib/action_controller/metal/helpers.rb +63 -55
- data/lib/action_controller/metal/http_authentication.rb +209 -201
- data/lib/action_controller/metal/implicit_render.rb +17 -15
- data/lib/action_controller/metal/instrumentation.rb +16 -14
- data/lib/action_controller/metal/live.rb +177 -128
- data/lib/action_controller/metal/logging.rb +6 -4
- data/lib/action_controller/metal/mime_responds.rb +151 -142
- data/lib/action_controller/metal/parameter_encoding.rb +34 -32
- data/lib/action_controller/metal/params_wrapper.rb +57 -59
- data/lib/action_controller/metal/permissions_policy.rb +22 -12
- data/lib/action_controller/metal/rate_limiting.rb +92 -0
- data/lib/action_controller/metal/redirecting.rb +213 -94
- data/lib/action_controller/metal/renderers.rb +78 -57
- data/lib/action_controller/metal/rendering.rb +111 -77
- data/lib/action_controller/metal/request_forgery_protection.rb +182 -143
- data/lib/action_controller/metal/rescue.rb +20 -9
- data/lib/action_controller/metal/streaming.rb +118 -195
- data/lib/action_controller/metal/strong_parameters.rb +720 -530
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +17 -15
- data/lib/action_controller/metal.rb +86 -60
- data/lib/action_controller/railtie.rb +36 -15
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +41 -36
- data/lib/action_controller/structured_event_subscriber.rb +116 -0
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +160 -131
- data/lib/action_controller.rb +5 -1
- data/lib/action_dispatch/constants.rb +8 -0
- data/lib/action_dispatch/deprecator.rb +2 -0
- data/lib/action_dispatch/http/cache.rb +163 -35
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +54 -39
- data/lib/action_dispatch/http/filter_parameters.rb +14 -8
- data/lib/action_dispatch/http/filter_redirect.rb +22 -1
- data/lib/action_dispatch/http/headers.rb +22 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +89 -41
- data/lib/action_dispatch/http/mime_type.rb +25 -21
- data/lib/action_dispatch/http/mime_types.rb +3 -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 +14 -12
- data/lib/action_dispatch/http/permissions_policy.rb +25 -36
- data/lib/action_dispatch/http/query_parser.rb +55 -0
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +141 -92
- data/lib/action_dispatch/http/response.rb +137 -77
- data/lib/action_dispatch/http/upload.rb +18 -16
- data/lib/action_dispatch/http/url.rb +187 -89
- data/lib/action_dispatch/journey/formatter.rb +21 -9
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +34 -11
- data/lib/action_dispatch/journey/gtg/transition_table.rb +47 -53
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +8 -6
- data/lib/action_dispatch/journey/parser.rb +99 -195
- data/lib/action_dispatch/journey/path/pattern.rb +4 -1
- data/lib/action_dispatch/journey/route.rb +54 -38
- data/lib/action_dispatch/journey/router/utils.rb +22 -27
- data/lib/action_dispatch/journey/router.rb +63 -83
- data/lib/action_dispatch/journey/routes.rb +11 -2
- data/lib/action_dispatch/journey/scanner.rb +46 -42
- data/lib/action_dispatch/journey/visitors.rb +57 -23
- data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +7 -1
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
- data/lib/action_dispatch/middleware/callbacks.rb +3 -1
- data/lib/action_dispatch/middleware/cookies.rb +125 -106
- data/lib/action_dispatch/middleware/debug_exceptions.rb +37 -8
- data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
- data/lib/action_dispatch/middleware/debug_view.rb +13 -5
- data/lib/action_dispatch/middleware/exception_wrapper.rb +18 -23
- data/lib/action_dispatch/middleware/executor.rb +19 -4
- data/lib/action_dispatch/middleware/flash.rb +63 -51
- data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
- data/lib/action_dispatch/middleware/public_exceptions.rb +14 -12
- data/lib/action_dispatch/middleware/reloader.rb +5 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +87 -77
- data/lib/action_dispatch/middleware/request_id.rb +16 -10
- data/lib/action_dispatch/middleware/server_timing.rb +4 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +30 -8
- data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +16 -16
- data/lib/action_dispatch/middleware/ssl.rb +53 -40
- data/lib/action_dispatch/middleware/stack.rb +11 -10
- data/lib/action_dispatch/middleware/static.rb +33 -31
- 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/middleware/templates/routes/_table.html.erb +1 -1
- data/lib/action_dispatch/railtie.rb +23 -3
- data/lib/action_dispatch/request/session.rb +24 -21
- data/lib/action_dispatch/request/utils.rb +11 -3
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +85 -60
- data/lib/action_dispatch/routing/mapper.rb +1031 -851
- data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
- data/lib/action_dispatch/routing/redirection.rb +47 -39
- data/lib/action_dispatch/routing/route_set.rb +79 -56
- data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
- data/lib/action_dispatch/routing/url_for.rb +130 -125
- data/lib/action_dispatch/routing.rb +150 -148
- data/lib/action_dispatch/structured_event_subscriber.rb +20 -0
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +16 -23
- data/lib/action_dispatch/system_testing/driver.rb +2 -0
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +34 -23
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
- data/lib/action_dispatch/testing/assertion_response.rb +9 -7
- data/lib/action_dispatch/testing/assertions/response.rb +52 -25
- data/lib/action_dispatch/testing/assertions/routing.rb +168 -87
- data/lib/action_dispatch/testing/assertions.rb +2 -0
- data/lib/action_dispatch/testing/integration.rb +233 -223
- data/lib/action_dispatch/testing/request_encoder.rb +11 -9
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +11 -8
- data/lib/action_dispatch/testing/test_request.rb +3 -1
- data/lib/action_dispatch/testing/test_response.rb +27 -26
- data/lib/action_dispatch.rb +36 -32
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +17 -16
- metadata +36 -32
- data/lib/action_dispatch/journey/parser.y +0 -50
- data/lib/action_dispatch/journey/parser_extras.rb +0 -31
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
3
5
|
require "active_support/core_ext/hash/slice"
|
|
4
6
|
require "active_support/core_ext/enumerable"
|
|
5
7
|
require "active_support/core_ext/array/extract_options"
|
|
@@ -31,10 +33,10 @@ module ActionDispatch
|
|
|
31
33
|
CALL = ->(app, req) { app.call req.env }
|
|
32
34
|
|
|
33
35
|
def initialize(app, constraints, strategy)
|
|
34
|
-
# Unwrap Constraints objects. I don't actually think it's possible
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
36
|
+
# Unwrap Constraints objects. I don't actually think it's possible to pass a
|
|
37
|
+
# Constraints object to this constructor, but there were multiple places that
|
|
38
|
+
# kept testing children of this object. I **think** they were just being
|
|
39
|
+
# defensive, but I have no idea.
|
|
38
40
|
if app.is_a?(self.class)
|
|
39
41
|
constraints += app.constraints
|
|
40
42
|
app = app.app
|
|
@@ -85,7 +87,7 @@ module ActionDispatch
|
|
|
85
87
|
attr_reader :path, :requirements, :defaults, :to, :default_controller,
|
|
86
88
|
:default_action, :required_defaults, :ast, :scope_options
|
|
87
89
|
|
|
88
|
-
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)
|
|
89
91
|
scope_params = {
|
|
90
92
|
blocks: scope[:blocks] || [],
|
|
91
93
|
constraints: scope[:constraints] || {},
|
|
@@ -96,7 +98,7 @@ module ActionDispatch
|
|
|
96
98
|
|
|
97
99
|
new set: set, ast: ast, controller: controller, default_action: default_action,
|
|
98
100
|
to: to, formatted: formatted, via: via, options_constraints: options_constraints,
|
|
99
|
-
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)
|
|
100
102
|
end
|
|
101
103
|
|
|
102
104
|
def self.check_via(via)
|
|
@@ -127,7 +129,7 @@ module ActionDispatch
|
|
|
127
129
|
format != false && !path.match?(OPTIONAL_FORMAT_REGEX)
|
|
128
130
|
end
|
|
129
131
|
|
|
130
|
-
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:)
|
|
131
133
|
@defaults = scope_params[:defaults]
|
|
132
134
|
@set = set
|
|
133
135
|
@to = intern(to)
|
|
@@ -135,7 +137,7 @@ module ActionDispatch
|
|
|
135
137
|
@default_action = intern(default_action)
|
|
136
138
|
@anchor = anchor
|
|
137
139
|
@via = via
|
|
138
|
-
@internal =
|
|
140
|
+
@internal = internal
|
|
139
141
|
@scope_options = scope_params[:options]
|
|
140
142
|
ast = Journey::Ast.new(ast, formatted)
|
|
141
143
|
|
|
@@ -181,7 +183,7 @@ module ActionDispatch
|
|
|
181
183
|
def make_route(name, precedence)
|
|
182
184
|
Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
|
|
183
185
|
required_defaults: required_defaults, defaults: defaults,
|
|
184
|
-
|
|
186
|
+
via: @via, precedence: precedence,
|
|
185
187
|
scope_options: scope_options, internal: @internal, source_location: route_source_location)
|
|
186
188
|
end
|
|
187
189
|
|
|
@@ -202,11 +204,6 @@ module ActionDispatch
|
|
|
202
204
|
end
|
|
203
205
|
private :build_conditions
|
|
204
206
|
|
|
205
|
-
def request_method
|
|
206
|
-
@via.map { |x| Journey::Route.verb_matcher(x) }
|
|
207
|
-
end
|
|
208
|
-
private :request_method
|
|
209
|
-
|
|
210
207
|
private
|
|
211
208
|
def intern(object)
|
|
212
209
|
object.is_a?(String) ? -object : object
|
|
@@ -219,7 +216,7 @@ module ActionDispatch
|
|
|
219
216
|
# Add a default constraint for :controller path segments that matches namespaced
|
|
220
217
|
# controllers with default routes like :controller/:action/:id(.:format), e.g:
|
|
221
218
|
# GET /admin/products/show/1
|
|
222
|
-
#
|
|
219
|
+
# # > { controller: 'admin/products', action: 'show', id: '1' }
|
|
223
220
|
options[:controller] ||= /.+?/
|
|
224
221
|
end
|
|
225
222
|
|
|
@@ -373,55 +370,36 @@ module ActionDispatch
|
|
|
373
370
|
Routing::RouteSet::Dispatcher.new raise_on_name_error
|
|
374
371
|
end
|
|
375
372
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
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)
|
|
382
378
|
|
|
383
|
-
|
|
384
|
-
|
|
379
|
+
cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
|
|
380
|
+
next if cleaned_path.nil?
|
|
385
381
|
|
|
386
|
-
|
|
387
|
-
end
|
|
388
|
-
nil
|
|
389
|
-
end
|
|
390
|
-
end
|
|
391
|
-
else
|
|
392
|
-
def route_source_location
|
|
393
|
-
if Mapper.route_source_locations
|
|
394
|
-
action_dispatch_dir = File.expand_path("..", __dir__)
|
|
395
|
-
caller_locations.each do |location|
|
|
396
|
-
next if location.path.start_with?(action_dispatch_dir)
|
|
397
|
-
|
|
398
|
-
cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
|
|
399
|
-
next if cleaned_path.nil?
|
|
400
|
-
|
|
401
|
-
return "#{cleaned_path}:#{location.lineno}"
|
|
402
|
-
end
|
|
403
|
-
nil
|
|
382
|
+
return "#{cleaned_path}:#{location.lineno}"
|
|
404
383
|
end
|
|
384
|
+
nil
|
|
405
385
|
end
|
|
406
386
|
end
|
|
407
387
|
end
|
|
408
388
|
|
|
409
|
-
# Invokes Journey::Router::Utils.normalize_path, then ensures that
|
|
410
|
-
#
|
|
411
|
-
#
|
|
389
|
+
# Invokes Journey::Router::Utils.normalize_path, then ensures that /(:locale)
|
|
390
|
+
# becomes (/:locale). Except for root cases, where the former is the correct
|
|
391
|
+
# one.
|
|
412
392
|
def self.normalize_path(path)
|
|
413
393
|
path = Journey::Router::Utils.normalize_path(path)
|
|
414
394
|
|
|
415
395
|
# the path for a root URL at this point can be something like
|
|
416
396
|
# "/(/:locale)(/:platform)/(:browser)", and we would want
|
|
417
|
-
# "/(:locale)(/:platform)(/:browser)"
|
|
418
|
-
|
|
419
|
-
# reverse "/(", "/((" etc to "(/", "((/" etc
|
|
397
|
+
# "/(:locale)(/:platform)(/:browser)" reverse "/(", "/((" etc to "(/", "((/" etc
|
|
420
398
|
path.gsub!(%r{/(\(+)/?}, '\1/')
|
|
421
|
-
# if a path is all optional segments, change the leading "(/" back to
|
|
422
|
-
#
|
|
423
|
-
#
|
|
424
|
-
#
|
|
399
|
+
# if a path is all optional segments, change the leading "(/" back to "/(" so it
|
|
400
|
+
# evaluates to "/" when interpreted with no options. Unless, however, at least
|
|
401
|
+
# one secondary segment consists of a static part, ex.
|
|
402
|
+
# "(/:locale)(/pages/:page)"
|
|
425
403
|
path.sub!(%r{^(\(+)/}, '/\1') if %r{^(\(+[^)]+\))(\(+/:[^)]+\))*$}.match?(path)
|
|
426
404
|
path
|
|
427
405
|
end
|
|
@@ -433,217 +411,223 @@ module ActionDispatch
|
|
|
433
411
|
module Base
|
|
434
412
|
# Matches a URL pattern to one or more routes.
|
|
435
413
|
#
|
|
436
|
-
# You should not use the
|
|
437
|
-
#
|
|
414
|
+
# You should not use the `match` method in your router without specifying an
|
|
415
|
+
# HTTP method.
|
|
438
416
|
#
|
|
439
417
|
# If you want to expose your action to both GET and POST, use:
|
|
440
418
|
#
|
|
441
|
-
#
|
|
442
|
-
#
|
|
419
|
+
# # sets :controller, :action, and :id in params
|
|
420
|
+
# match ':controller/:action/:id', via: [:get, :post]
|
|
443
421
|
#
|
|
444
|
-
# Note that
|
|
445
|
-
#
|
|
422
|
+
# Note that `:controller`, `:action`, and `:id` are interpreted as URL query
|
|
423
|
+
# parameters and thus available through `params` in an action.
|
|
446
424
|
#
|
|
447
|
-
# If you want to expose your action to GET, use
|
|
425
|
+
# If you want to expose your action to GET, use `get` in the router:
|
|
448
426
|
#
|
|
449
427
|
# Instead of:
|
|
450
428
|
#
|
|
451
|
-
#
|
|
429
|
+
# match ":controller/:action/:id"
|
|
452
430
|
#
|
|
453
431
|
# Do:
|
|
454
432
|
#
|
|
455
|
-
#
|
|
433
|
+
# get ":controller/:action/:id"
|
|
456
434
|
#
|
|
457
|
-
# Two of these symbols are special,
|
|
458
|
-
#
|
|
459
|
-
#
|
|
435
|
+
# Two of these symbols are special, `:controller` maps to the controller and
|
|
436
|
+
# `:action` to the controller's action. A pattern can also map wildcard segments
|
|
437
|
+
# (globs) to params:
|
|
460
438
|
#
|
|
461
|
-
#
|
|
439
|
+
# get 'songs/*category/:title', to: 'songs#show'
|
|
462
440
|
#
|
|
463
|
-
#
|
|
464
|
-
#
|
|
465
|
-
#
|
|
441
|
+
# # 'songs/rock/classic/stairway-to-heaven' sets
|
|
442
|
+
# # params[:category] = 'rock/classic'
|
|
443
|
+
# # params[:title] = 'stairway-to-heaven'
|
|
466
444
|
#
|
|
467
|
-
# To match a wildcard parameter, it must have a name assigned to it.
|
|
468
|
-
#
|
|
469
|
-
# can't be parsed.
|
|
445
|
+
# To match a wildcard parameter, it must have a name assigned to it. Without a
|
|
446
|
+
# variable name to attach the glob parameter to, the route can't be parsed.
|
|
470
447
|
#
|
|
471
|
-
# When a pattern points to an internal route, the route's
|
|
472
|
-
#
|
|
448
|
+
# When a pattern points to an internal route, the route's `:action` and
|
|
449
|
+
# `:controller` should be set in options or hash shorthand. Examples:
|
|
473
450
|
#
|
|
474
|
-
#
|
|
475
|
-
#
|
|
476
|
-
# match 'photos/:id', controller: 'photos', action: 'show', via: :get
|
|
451
|
+
# match 'photos/:id', to: 'photos#show', via: :get
|
|
452
|
+
# match 'photos/:id', controller: 'photos', action: 'show', via: :get
|
|
477
453
|
#
|
|
478
|
-
# A pattern can also point to a
|
|
479
|
-
#
|
|
454
|
+
# A pattern can also point to a `Rack` endpoint i.e. anything that responds to
|
|
455
|
+
# `call`:
|
|
480
456
|
#
|
|
481
|
-
#
|
|
482
|
-
#
|
|
483
|
-
#
|
|
484
|
-
#
|
|
457
|
+
# match 'photos/:id', to: -> (hash) { [200, {}, ["Coming soon"]] }, via: :get
|
|
458
|
+
# match 'photos/:id', to: PhotoRackApp, via: :get
|
|
459
|
+
# # Yes, controller actions are just rack endpoints
|
|
460
|
+
# match 'photos/:id', to: PhotosController.action(:show), via: :get
|
|
485
461
|
#
|
|
486
462
|
# Because requesting various HTTP verbs with a single action has security
|
|
487
|
-
# implications, you must either specify the actions in
|
|
488
|
-
#
|
|
489
|
-
# instead +match+
|
|
463
|
+
# implications, you must either specify the actions in the via options or use
|
|
464
|
+
# one of the [HttpHelpers](rdoc-ref:HttpHelpers) instead `match`
|
|
490
465
|
#
|
|
491
|
-
#
|
|
466
|
+
# ### Options
|
|
492
467
|
#
|
|
493
468
|
# Any options not seen here are passed on as params with the URL.
|
|
494
469
|
#
|
|
495
|
-
#
|
|
496
|
-
# The route's controller.
|
|
470
|
+
# :controller
|
|
471
|
+
# : The route's controller.
|
|
497
472
|
#
|
|
498
|
-
#
|
|
499
|
-
# The route's action.
|
|
473
|
+
# :action
|
|
474
|
+
# : The route's action.
|
|
500
475
|
#
|
|
501
|
-
#
|
|
502
|
-
# Overrides the default resource identifier
|
|
503
|
-
#
|
|
504
|
-
#
|
|
505
|
-
# <tt>params[<:param>]</tt>.
|
|
506
|
-
# In your router:
|
|
476
|
+
# :param
|
|
477
|
+
# : Overrides the default resource identifier `:id` (name of the dynamic
|
|
478
|
+
# segment used to generate the routes). You can access that segment from
|
|
479
|
+
# your controller using `params[<:param>]`. In your router:
|
|
507
480
|
#
|
|
508
|
-
#
|
|
481
|
+
# resources :users, param: :name
|
|
509
482
|
#
|
|
510
|
-
#
|
|
483
|
+
# The `users` resource here will have the following routes generated for it:
|
|
511
484
|
#
|
|
512
|
-
#
|
|
513
|
-
#
|
|
514
|
-
#
|
|
515
|
-
#
|
|
516
|
-
#
|
|
517
|
-
#
|
|
518
|
-
#
|
|
485
|
+
# GET /users(.:format)
|
|
486
|
+
# POST /users(.:format)
|
|
487
|
+
# GET /users/new(.:format)
|
|
488
|
+
# GET /users/:name/edit(.:format)
|
|
489
|
+
# GET /users/:name(.:format)
|
|
490
|
+
# PATCH/PUT /users/:name(.:format)
|
|
491
|
+
# DELETE /users/:name(.:format)
|
|
519
492
|
#
|
|
520
|
-
#
|
|
521
|
-
#
|
|
493
|
+
# You can override `ActiveRecord::Base#to_param` of a related model to
|
|
494
|
+
# construct a URL:
|
|
522
495
|
#
|
|
523
|
-
#
|
|
524
|
-
#
|
|
525
|
-
#
|
|
526
|
-
#
|
|
527
|
-
#
|
|
496
|
+
# class User < ActiveRecord::Base
|
|
497
|
+
# def to_param
|
|
498
|
+
# name
|
|
499
|
+
# end
|
|
500
|
+
# end
|
|
528
501
|
#
|
|
529
|
-
#
|
|
530
|
-
#
|
|
502
|
+
# user = User.find_by(name: 'Phusion')
|
|
503
|
+
# user_path(user) # => "/users/Phusion"
|
|
531
504
|
#
|
|
532
|
-
#
|
|
533
|
-
# The path prefix for the routes.
|
|
505
|
+
# :path
|
|
506
|
+
# : The path prefix for the routes.
|
|
534
507
|
#
|
|
535
|
-
#
|
|
536
|
-
# The namespace for :controller.
|
|
508
|
+
# :module
|
|
509
|
+
# : The namespace for :controller.
|
|
537
510
|
#
|
|
538
|
-
#
|
|
539
|
-
#
|
|
511
|
+
# match 'path', to: 'c#a', module: 'sekret', controller: 'posts', via: :get
|
|
512
|
+
# # => Sekret::PostsController
|
|
540
513
|
#
|
|
541
|
-
#
|
|
514
|
+
# See `Scoping#namespace` for its scope equivalent.
|
|
542
515
|
#
|
|
543
|
-
#
|
|
544
|
-
# The name used to generate routing helpers.
|
|
516
|
+
# :as
|
|
517
|
+
# : The name used to generate routing helpers.
|
|
545
518
|
#
|
|
546
|
-
#
|
|
547
|
-
# Allowed HTTP verb(s) for route.
|
|
519
|
+
# :via
|
|
520
|
+
# : Allowed HTTP verb(s) for route.
|
|
548
521
|
#
|
|
549
|
-
#
|
|
550
|
-
#
|
|
551
|
-
#
|
|
522
|
+
# match 'path', to: 'c#a', via: :get
|
|
523
|
+
# match 'path', to: 'c#a', via: [:get, :post]
|
|
524
|
+
# match 'path', to: 'c#a', via: :all
|
|
552
525
|
#
|
|
553
|
-
#
|
|
554
|
-
# Points to a
|
|
555
|
-
#
|
|
526
|
+
# :to
|
|
527
|
+
# : Points to a `Rack` endpoint. Can be an object that responds to `call` or a
|
|
528
|
+
# string representing a controller's action.
|
|
556
529
|
#
|
|
557
|
-
#
|
|
558
|
-
#
|
|
559
|
-
#
|
|
530
|
+
# match 'path', to: 'controller#action', via: :get
|
|
531
|
+
# match 'path', to: -> (env) { [200, {}, ["Success!"]] }, via: :get
|
|
532
|
+
# match 'path', to: RackApp, via: :get
|
|
560
533
|
#
|
|
561
|
-
#
|
|
562
|
-
# Shorthand for wrapping routes in a specific RESTful context. Valid
|
|
563
|
-
#
|
|
564
|
-
#
|
|
534
|
+
# :on
|
|
535
|
+
# : Shorthand for wrapping routes in a specific RESTful context. Valid values
|
|
536
|
+
# are `:member`, `:collection`, and `:new`. Only use within `resource(s)`
|
|
537
|
+
# block. For example:
|
|
565
538
|
#
|
|
566
|
-
#
|
|
567
|
-
#
|
|
568
|
-
#
|
|
539
|
+
# resource :bar do
|
|
540
|
+
# match 'foo', to: 'c#a', on: :member, via: [:get, :post]
|
|
541
|
+
# end
|
|
569
542
|
#
|
|
570
|
-
#
|
|
543
|
+
# Is equivalent to:
|
|
571
544
|
#
|
|
572
|
-
#
|
|
573
|
-
#
|
|
574
|
-
#
|
|
575
|
-
#
|
|
576
|
-
#
|
|
545
|
+
# resource :bar do
|
|
546
|
+
# member do
|
|
547
|
+
# match 'foo', to: 'c#a', via: [:get, :post]
|
|
548
|
+
# end
|
|
549
|
+
# end
|
|
577
550
|
#
|
|
578
|
-
#
|
|
579
|
-
# Constrains parameters with a hash of regular expressions
|
|
580
|
-
#
|
|
581
|
-
#
|
|
582
|
-
#
|
|
551
|
+
# :constraints
|
|
552
|
+
# : Constrains parameters with a hash of regular expressions or an object that
|
|
553
|
+
# responds to `matches?`. In addition, constraints other than path can also
|
|
554
|
+
# be specified with any object that responds to `===` (e.g. String, Array,
|
|
555
|
+
# Range, etc.).
|
|
583
556
|
#
|
|
584
|
-
#
|
|
557
|
+
# match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }, via: :get
|
|
585
558
|
#
|
|
586
|
-
#
|
|
559
|
+
# match 'json_only', constraints: { format: 'json' }, via: :get
|
|
587
560
|
#
|
|
588
|
-
#
|
|
589
|
-
#
|
|
590
|
-
#
|
|
591
|
-
#
|
|
561
|
+
# class PermitList
|
|
562
|
+
# def matches?(request) request.remote_ip == '1.2.3.4' end
|
|
563
|
+
# end
|
|
564
|
+
# match 'path', to: 'c#a', constraints: PermitList.new, via: :get
|
|
565
|
+
#
|
|
566
|
+
# See `Scoping#constraints` for more examples with its scope equivalent.
|
|
592
567
|
#
|
|
593
|
-
#
|
|
594
|
-
#
|
|
568
|
+
# :defaults
|
|
569
|
+
# : Sets defaults for parameters
|
|
595
570
|
#
|
|
596
|
-
# [:
|
|
597
|
-
#
|
|
571
|
+
# # Sets params[:format] to 'jpg' by default
|
|
572
|
+
# match 'path', to: 'c#a', defaults: { format: 'jpg' }, via: :get
|
|
598
573
|
#
|
|
599
|
-
# #
|
|
600
|
-
# match 'path', to: 'c#a', defaults: { format: 'jpg' }, via: :get
|
|
574
|
+
# See `Scoping#defaults` for its scope equivalent.
|
|
601
575
|
#
|
|
602
|
-
#
|
|
576
|
+
# :anchor
|
|
577
|
+
# : Boolean to anchor a `match` pattern. Default is true. When set to false,
|
|
578
|
+
# the pattern matches any request prefixed with the given path.
|
|
603
579
|
#
|
|
604
|
-
#
|
|
605
|
-
#
|
|
606
|
-
# false, the pattern matches any request prefixed with the given path.
|
|
580
|
+
# # Matches any request starting with 'path'
|
|
581
|
+
# match 'path', to: 'c#a', anchor: false, via: :get
|
|
607
582
|
#
|
|
608
|
-
#
|
|
609
|
-
#
|
|
583
|
+
# :format
|
|
584
|
+
# : Allows you to specify the default value for optional `format` segment or
|
|
585
|
+
# disable it by supplying `false`.
|
|
610
586
|
#
|
|
611
|
-
# [:format]
|
|
612
|
-
# Allows you to specify the default value for optional +format+
|
|
613
|
-
# segment or disable it by supplying +false+.
|
|
614
587
|
def match(path, options = nil)
|
|
615
588
|
end
|
|
616
589
|
|
|
617
590
|
# Mount a Rack-based application to be used within the application.
|
|
618
591
|
#
|
|
619
|
-
#
|
|
620
|
-
#
|
|
621
|
-
# Alternatively:
|
|
622
|
-
#
|
|
623
|
-
# mount(SomeRackApp => "some_route")
|
|
592
|
+
# mount SomeRackApp, at: "some_route"
|
|
624
593
|
#
|
|
625
|
-
# For options, see
|
|
594
|
+
# For options, see `match`, as `mount` uses it internally.
|
|
626
595
|
#
|
|
627
|
-
# All mounted applications come with routing helpers to access them.
|
|
628
|
-
#
|
|
629
|
-
#
|
|
630
|
-
#
|
|
596
|
+
# All mounted applications come with routing helpers to access them. These are
|
|
597
|
+
# named after the class specified, so for the above example the helper is either
|
|
598
|
+
# `some_rack_app_path` or `some_rack_app_url`. To customize this helper's name,
|
|
599
|
+
# use the `:as` option:
|
|
631
600
|
#
|
|
632
|
-
#
|
|
601
|
+
# mount(SomeRackApp, at: "some_route", as: "exciting")
|
|
633
602
|
#
|
|
634
|
-
# This will generate the
|
|
635
|
-
#
|
|
636
|
-
def mount(app,
|
|
637
|
-
if
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
603
|
+
# This will generate the `exciting_path` and `exciting_url` helpers which can be
|
|
604
|
+
# used to navigate to this mounted app.
|
|
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
|
|
643
627
|
end
|
|
644
628
|
|
|
645
629
|
raise ArgumentError, "A rack application must be specified" unless app.respond_to?(:call)
|
|
646
|
-
raise ArgumentError, <<~MSG unless
|
|
630
|
+
raise ArgumentError, <<~MSG unless path_or_action
|
|
647
631
|
Must be called with mount point
|
|
648
632
|
|
|
649
633
|
mount SomeRackApp, at: "some_route"
|
|
@@ -652,12 +636,12 @@ module ActionDispatch
|
|
|
652
636
|
MSG
|
|
653
637
|
|
|
654
638
|
rails_app = rails_app? app
|
|
655
|
-
|
|
639
|
+
as = app_name(app, rails_app) if as == DEFAULT
|
|
656
640
|
|
|
657
|
-
target_as
|
|
658
|
-
|
|
641
|
+
target_as = name_for_action(as, path_or_action)
|
|
642
|
+
via ||= :all
|
|
659
643
|
|
|
660
|
-
match(
|
|
644
|
+
match(path_or_action, to: app, as:, via:, defaults:, constraints:, anchor:, format:, path:, internal:, **mapping, &block)
|
|
661
645
|
|
|
662
646
|
define_generate_prefix(app, target_as) if rails_app
|
|
663
647
|
self
|
|
@@ -669,7 +653,7 @@ module ActionDispatch
|
|
|
669
653
|
alias_method :default_url_options, :default_url_options=
|
|
670
654
|
|
|
671
655
|
def with_default_scope(scope, &block)
|
|
672
|
-
scope(scope) do
|
|
656
|
+
scope(**scope) do
|
|
673
657
|
instance_exec(&block)
|
|
674
658
|
end
|
|
675
659
|
end
|
|
@@ -680,6 +664,24 @@ module ActionDispatch
|
|
|
680
664
|
end
|
|
681
665
|
|
|
682
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
|
+
|
|
683
685
|
def rails_app?(app)
|
|
684
686
|
app.is_a?(Class) && app < Rails::Railtie
|
|
685
687
|
end
|
|
@@ -706,7 +708,8 @@ module ActionDispatch
|
|
|
706
708
|
prefix_options.reverse_merge!(options[:_recall].slice(*_route.segment_keys))
|
|
707
709
|
end
|
|
708
710
|
|
|
709
|
-
# We must actually delete prefix segment keys to avoid passing them to next
|
|
711
|
+
# We must actually delete prefix segment keys to avoid passing them to next
|
|
712
|
+
# url_for.
|
|
710
713
|
_route.segment_keys.each { |k| options.delete(k) }
|
|
711
714
|
_url_helpers.public_send("#{name}_path", prefix_options)
|
|
712
715
|
end
|
|
@@ -728,150 +731,268 @@ module ActionDispatch
|
|
|
728
731
|
end
|
|
729
732
|
|
|
730
733
|
module HttpHelpers
|
|
731
|
-
# Define a route that only recognizes HTTP GET.
|
|
732
|
-
#
|
|
733
|
-
#
|
|
734
|
-
#
|
|
735
|
-
def get(*
|
|
736
|
-
|
|
734
|
+
# Define a route that only recognizes HTTP GET. For supported arguments, see
|
|
735
|
+
# [match](rdoc-ref:Base#match)
|
|
736
|
+
#
|
|
737
|
+
# get 'bacon', to: 'food#bacon'
|
|
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
|
|
737
756
|
end
|
|
738
757
|
|
|
739
|
-
# Define a route that only recognizes HTTP POST.
|
|
740
|
-
#
|
|
741
|
-
#
|
|
742
|
-
#
|
|
743
|
-
def post(*
|
|
744
|
-
|
|
758
|
+
# Define a route that only recognizes HTTP POST. For supported arguments, see
|
|
759
|
+
# [match](rdoc-ref:Base#match)
|
|
760
|
+
#
|
|
761
|
+
# post 'bacon', to: 'food#bacon'
|
|
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
|
|
745
780
|
end
|
|
746
781
|
|
|
747
|
-
# Define a route that only recognizes HTTP PATCH.
|
|
748
|
-
#
|
|
749
|
-
#
|
|
750
|
-
#
|
|
751
|
-
def patch(*
|
|
752
|
-
|
|
782
|
+
# Define a route that only recognizes HTTP PATCH. For supported arguments, see
|
|
783
|
+
# [match](rdoc-ref:Base#match)
|
|
784
|
+
#
|
|
785
|
+
# patch 'bacon', to: 'food#bacon'
|
|
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
|
|
753
804
|
end
|
|
754
805
|
|
|
755
|
-
# Define a route that only recognizes HTTP PUT.
|
|
756
|
-
#
|
|
757
|
-
#
|
|
758
|
-
#
|
|
759
|
-
def put(*
|
|
760
|
-
|
|
806
|
+
# Define a route that only recognizes HTTP PUT. For supported arguments, see
|
|
807
|
+
# [match](rdoc-ref:Base#match)
|
|
808
|
+
#
|
|
809
|
+
# put 'bacon', to: 'food#bacon'
|
|
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
|
|
761
828
|
end
|
|
762
829
|
|
|
763
|
-
# Define a route that only recognizes HTTP DELETE.
|
|
764
|
-
#
|
|
765
|
-
#
|
|
766
|
-
#
|
|
767
|
-
def delete(*
|
|
768
|
-
|
|
830
|
+
# Define a route that only recognizes HTTP DELETE. For supported arguments, see
|
|
831
|
+
# [match](rdoc-ref:Base#match)
|
|
832
|
+
#
|
|
833
|
+
# delete 'broccoli', to: 'food#broccoli'
|
|
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
|
|
769
852
|
end
|
|
770
853
|
|
|
771
|
-
# Define a route that only recognizes HTTP OPTIONS.
|
|
772
|
-
#
|
|
773
|
-
#
|
|
774
|
-
#
|
|
775
|
-
def options(*
|
|
776
|
-
|
|
854
|
+
# Define a route that only recognizes HTTP OPTIONS. For supported arguments, see
|
|
855
|
+
# [match](rdoc-ref:Base#match)
|
|
856
|
+
#
|
|
857
|
+
# options 'carrots', to: 'food#carrots'
|
|
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
|
|
777
876
|
end
|
|
778
877
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
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
|
|
786
903
|
end
|
|
787
904
|
|
|
788
|
-
# You may wish to organize groups of controllers under a namespace.
|
|
789
|
-
#
|
|
790
|
-
#
|
|
791
|
-
#
|
|
792
|
-
#
|
|
905
|
+
# You may wish to organize groups of controllers under a namespace. Most
|
|
906
|
+
# commonly, you might group a number of administrative controllers under an
|
|
907
|
+
# `admin` namespace. You would place these controllers under the
|
|
908
|
+
# `app/controllers/admin` directory, and you can group them together in your
|
|
909
|
+
# router:
|
|
793
910
|
#
|
|
794
|
-
#
|
|
795
|
-
#
|
|
796
|
-
#
|
|
911
|
+
# namespace "admin" do
|
|
912
|
+
# resources :posts, :comments
|
|
913
|
+
# end
|
|
797
914
|
#
|
|
798
915
|
# This will create a number of routes for each of the posts and comments
|
|
799
|
-
# controller. For
|
|
916
|
+
# controller. For `Admin::PostsController`, Rails will create:
|
|
800
917
|
#
|
|
801
|
-
#
|
|
802
|
-
#
|
|
803
|
-
#
|
|
804
|
-
#
|
|
805
|
-
#
|
|
806
|
-
#
|
|
807
|
-
#
|
|
918
|
+
# GET /admin/posts
|
|
919
|
+
# GET /admin/posts/new
|
|
920
|
+
# POST /admin/posts
|
|
921
|
+
# GET /admin/posts/1
|
|
922
|
+
# GET /admin/posts/1/edit
|
|
923
|
+
# PATCH/PUT /admin/posts/1
|
|
924
|
+
# DELETE /admin/posts/1
|
|
808
925
|
#
|
|
809
926
|
# If you want to route /posts (without the prefix /admin) to
|
|
810
|
-
#
|
|
927
|
+
# `Admin::PostsController`, you could use
|
|
811
928
|
#
|
|
812
|
-
#
|
|
813
|
-
#
|
|
814
|
-
#
|
|
929
|
+
# scope module: "admin" do
|
|
930
|
+
# resources :posts
|
|
931
|
+
# end
|
|
815
932
|
#
|
|
816
933
|
# or, for a single case
|
|
817
934
|
#
|
|
818
|
-
#
|
|
935
|
+
# resources :posts, module: "admin"
|
|
819
936
|
#
|
|
820
|
-
# If you want to route /admin/posts to
|
|
821
|
-
#
|
|
937
|
+
# If you want to route /admin/posts to `PostsController` (without the `Admin::`
|
|
938
|
+
# module prefix), you could use
|
|
822
939
|
#
|
|
823
|
-
#
|
|
824
|
-
#
|
|
825
|
-
#
|
|
940
|
+
# scope "/admin" do
|
|
941
|
+
# resources :posts
|
|
942
|
+
# end
|
|
826
943
|
#
|
|
827
944
|
# or, for a single case
|
|
828
945
|
#
|
|
829
|
-
#
|
|
946
|
+
# resources :posts, path: "/admin/posts"
|
|
830
947
|
#
|
|
831
|
-
# In each of these cases, the named routes remain the same as if you did
|
|
832
|
-
#
|
|
833
|
-
# +PostsController+:
|
|
948
|
+
# In each of these cases, the named routes remain the same as if you did not use
|
|
949
|
+
# scope. In the last case, the following paths map to `PostsController`:
|
|
834
950
|
#
|
|
835
|
-
#
|
|
836
|
-
#
|
|
837
|
-
#
|
|
838
|
-
#
|
|
839
|
-
#
|
|
840
|
-
#
|
|
841
|
-
#
|
|
951
|
+
# GET /admin/posts
|
|
952
|
+
# GET /admin/posts/new
|
|
953
|
+
# POST /admin/posts
|
|
954
|
+
# GET /admin/posts/1
|
|
955
|
+
# GET /admin/posts/1/edit
|
|
956
|
+
# PATCH/PUT /admin/posts/1
|
|
957
|
+
# DELETE /admin/posts/1
|
|
842
958
|
module Scoping
|
|
843
959
|
# Scopes a set of routes to the given default options.
|
|
844
960
|
#
|
|
845
961
|
# Take the following route definition as an example:
|
|
846
962
|
#
|
|
847
|
-
#
|
|
848
|
-
#
|
|
849
|
-
#
|
|
963
|
+
# scope path: ":account_id", as: "account" do
|
|
964
|
+
# resources :projects
|
|
965
|
+
# end
|
|
850
966
|
#
|
|
851
|
-
# This generates helpers such as
|
|
852
|
-
# The difference here being that the routes generated are like
|
|
853
|
-
# rather than /accounts/:account_id/projects.
|
|
967
|
+
# This generates helpers such as `account_projects_path`, just like `resources`
|
|
968
|
+
# does. The difference here being that the routes generated are like
|
|
969
|
+
# /:account_id/projects, rather than /accounts/:account_id/projects.
|
|
854
970
|
#
|
|
855
|
-
#
|
|
971
|
+
# ### Options
|
|
856
972
|
#
|
|
857
|
-
# Takes same options as
|
|
973
|
+
# Takes same options as `Base#match` and `Resources#resources`.
|
|
858
974
|
#
|
|
859
|
-
#
|
|
860
|
-
#
|
|
861
|
-
#
|
|
862
|
-
#
|
|
975
|
+
# # route /posts (without the prefix /admin) to Admin::PostsController
|
|
976
|
+
# scope module: "admin" do
|
|
977
|
+
# resources :posts
|
|
978
|
+
# end
|
|
863
979
|
#
|
|
864
|
-
#
|
|
865
|
-
#
|
|
866
|
-
#
|
|
867
|
-
#
|
|
980
|
+
# # prefix the posts resource's requests with '/admin'
|
|
981
|
+
# scope path: "/admin" do
|
|
982
|
+
# resources :posts
|
|
983
|
+
# end
|
|
868
984
|
#
|
|
869
|
-
#
|
|
870
|
-
#
|
|
871
|
-
#
|
|
872
|
-
#
|
|
873
|
-
def scope(*args)
|
|
874
|
-
|
|
985
|
+
# # prefix the routing helper name: sekret_posts_path instead of posts_path
|
|
986
|
+
# scope as: "sekret" do
|
|
987
|
+
# resources :posts
|
|
988
|
+
# end
|
|
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
|
+
|
|
875
996
|
scope = {}
|
|
876
997
|
|
|
877
998
|
options[:path] = args.flatten.join("/") if args.any?
|
|
@@ -892,9 +1013,8 @@ module ActionDispatch
|
|
|
892
1013
|
block, options[:constraints] = options[:constraints], {}
|
|
893
1014
|
end
|
|
894
1015
|
|
|
895
|
-
if
|
|
896
|
-
scope[:action_options] = { only:
|
|
897
|
-
except: options.delete(:except) }
|
|
1016
|
+
if only || except
|
|
1017
|
+
scope[:action_options] = { only:, except: }
|
|
898
1018
|
end
|
|
899
1019
|
|
|
900
1020
|
if options.key? :anchor
|
|
@@ -926,9 +1046,9 @@ module ActionDispatch
|
|
|
926
1046
|
|
|
927
1047
|
# Scopes routes to a specific controller
|
|
928
1048
|
#
|
|
929
|
-
#
|
|
930
|
-
#
|
|
931
|
-
#
|
|
1049
|
+
# controller "food" do
|
|
1050
|
+
# match "bacon", action: :bacon, via: :get
|
|
1051
|
+
# end
|
|
932
1052
|
def controller(controller)
|
|
933
1053
|
@scope = @scope.new(controller: controller)
|
|
934
1054
|
yield
|
|
@@ -938,121 +1058,132 @@ module ActionDispatch
|
|
|
938
1058
|
|
|
939
1059
|
# Scopes routes to a specific namespace. For example:
|
|
940
1060
|
#
|
|
941
|
-
#
|
|
942
|
-
#
|
|
943
|
-
#
|
|
1061
|
+
# namespace :admin do
|
|
1062
|
+
# resources :posts
|
|
1063
|
+
# end
|
|
944
1064
|
#
|
|
945
1065
|
# This generates the following routes:
|
|
946
1066
|
#
|
|
947
|
-
#
|
|
948
|
-
#
|
|
949
|
-
#
|
|
950
|
-
#
|
|
951
|
-
#
|
|
952
|
-
#
|
|
953
|
-
#
|
|
954
|
-
#
|
|
955
|
-
#
|
|
956
|
-
#
|
|
957
|
-
# The
|
|
958
|
-
#
|
|
959
|
-
#
|
|
960
|
-
# For options, see
|
|
961
|
-
#
|
|
962
|
-
#
|
|
963
|
-
#
|
|
964
|
-
#
|
|
965
|
-
#
|
|
966
|
-
#
|
|
967
|
-
#
|
|
968
|
-
#
|
|
969
|
-
#
|
|
970
|
-
#
|
|
971
|
-
#
|
|
972
|
-
#
|
|
973
|
-
#
|
|
974
|
-
#
|
|
975
|
-
#
|
|
976
|
-
#
|
|
977
|
-
def namespace(
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
}
|
|
1067
|
+
# admin_posts GET /admin/posts(.:format) admin/posts#index
|
|
1068
|
+
# admin_posts POST /admin/posts(.:format) admin/posts#create
|
|
1069
|
+
# new_admin_post GET /admin/posts/new(.:format) admin/posts#new
|
|
1070
|
+
# edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit
|
|
1071
|
+
# admin_post GET /admin/posts/:id(.:format) admin/posts#show
|
|
1072
|
+
# admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update
|
|
1073
|
+
# admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy
|
|
1074
|
+
#
|
|
1075
|
+
# ### Options
|
|
1076
|
+
#
|
|
1077
|
+
# The `:path`, `:as`, `:module`, `:shallow_path`, and `:shallow_prefix` options
|
|
1078
|
+
# all default to the name of the namespace.
|
|
1079
|
+
#
|
|
1080
|
+
# For options, see `Base#match`. For `:shallow_path` option, see
|
|
1081
|
+
# `Resources#resources`.
|
|
1082
|
+
#
|
|
1083
|
+
# # accessible through /sekret/posts rather than /admin/posts
|
|
1084
|
+
# namespace :admin, path: "sekret" do
|
|
1085
|
+
# resources :posts
|
|
1086
|
+
# end
|
|
1087
|
+
#
|
|
1088
|
+
# # maps to Sekret::PostsController rather than Admin::PostsController
|
|
1089
|
+
# namespace :admin, module: "sekret" do
|
|
1090
|
+
# resources :posts
|
|
1091
|
+
# end
|
|
1092
|
+
#
|
|
1093
|
+
# # generates sekret_posts_path rather than admin_posts_path
|
|
1094
|
+
# namespace :admin, as: "sekret" do
|
|
1095
|
+
# resources :posts
|
|
1096
|
+
# end
|
|
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
|
|
986
1105
|
|
|
987
|
-
|
|
988
|
-
|
|
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
|
|
1112
|
+
|
|
1113
|
+
path_scope(path) do
|
|
1114
|
+
scope(**options, as:, shallow_path:, shallow_prefix:, &block)
|
|
989
1115
|
end
|
|
990
1116
|
end
|
|
991
1117
|
|
|
992
|
-
#
|
|
993
|
-
# Allows you to constrain the nested routes based on a set of rules.
|
|
994
|
-
#
|
|
1118
|
+
# ### Parameter Restriction
|
|
1119
|
+
# Allows you to constrain the nested routes based on a set of rules. For
|
|
1120
|
+
# instance, in order to change the routes to allow for a dot character in the
|
|
1121
|
+
# `id` parameter:
|
|
995
1122
|
#
|
|
996
|
-
#
|
|
997
|
-
#
|
|
998
|
-
#
|
|
1123
|
+
# constraints(id: /\d+\.\d+/) do
|
|
1124
|
+
# resources :posts
|
|
1125
|
+
# end
|
|
999
1126
|
#
|
|
1000
|
-
# Now routes such as
|
|
1001
|
-
# The
|
|
1127
|
+
# Now routes such as `/posts/1` will no longer be valid, but `/posts/1.1` will
|
|
1128
|
+
# be. The `id` parameter must match the constraint passed in for this example.
|
|
1002
1129
|
#
|
|
1003
1130
|
# You may use this to also restrict other parameters:
|
|
1004
1131
|
#
|
|
1005
|
-
#
|
|
1006
|
-
#
|
|
1007
|
-
#
|
|
1132
|
+
# resources :posts do
|
|
1133
|
+
# constraints(post_id: /\d+\.\d+/) do
|
|
1134
|
+
# resources :comments
|
|
1135
|
+
# end
|
|
1008
1136
|
# end
|
|
1009
|
-
# end
|
|
1010
1137
|
#
|
|
1011
|
-
#
|
|
1138
|
+
# ### Restricting based on IP
|
|
1012
1139
|
#
|
|
1013
1140
|
# Routes can also be constrained to an IP or a certain range of IP addresses:
|
|
1014
1141
|
#
|
|
1015
|
-
#
|
|
1016
|
-
#
|
|
1017
|
-
#
|
|
1142
|
+
# constraints(ip: /192\.168\.\d+\.\d+/) do
|
|
1143
|
+
# resources :posts
|
|
1144
|
+
# end
|
|
1018
1145
|
#
|
|
1019
|
-
# Any user connecting from the 192.168.* range will be able to see this
|
|
1020
|
-
# where as any user connecting outside of this range will be told
|
|
1146
|
+
# Any user connecting from the 192.168.* range will be able to see this
|
|
1147
|
+
# resource, where as any user connecting outside of this range will be told
|
|
1148
|
+
# there is no such route.
|
|
1021
1149
|
#
|
|
1022
|
-
#
|
|
1150
|
+
# ### Dynamic request matching
|
|
1023
1151
|
#
|
|
1024
1152
|
# Requests to routes can be constrained based on specific criteria:
|
|
1025
1153
|
#
|
|
1026
|
-
#
|
|
1027
|
-
#
|
|
1028
|
-
#
|
|
1154
|
+
# constraints(-> (req) { /iPhone/.match?(req.env["HTTP_USER_AGENT"]) }) do
|
|
1155
|
+
# resources :iphones
|
|
1156
|
+
# end
|
|
1029
1157
|
#
|
|
1030
|
-
# You are able to move this logic out into a class if it is too complex for
|
|
1031
|
-
# This class must have a
|
|
1032
|
-
# if the user should be given access to that route, or
|
|
1158
|
+
# You are able to move this logic out into a class if it is too complex for
|
|
1159
|
+
# routes. This class must have a `matches?` method defined on it which either
|
|
1160
|
+
# returns `true` if the user should be given access to that route, or `false` if
|
|
1161
|
+
# the user should not.
|
|
1033
1162
|
#
|
|
1034
|
-
#
|
|
1035
|
-
#
|
|
1036
|
-
#
|
|
1037
|
-
#
|
|
1038
|
-
#
|
|
1163
|
+
# class Iphone
|
|
1164
|
+
# def self.matches?(request)
|
|
1165
|
+
# /iPhone/.match?(request.env["HTTP_USER_AGENT"])
|
|
1166
|
+
# end
|
|
1167
|
+
# end
|
|
1039
1168
|
#
|
|
1040
|
-
# An expected place for this code would be
|
|
1169
|
+
# An expected place for this code would be `lib/constraints`.
|
|
1041
1170
|
#
|
|
1042
1171
|
# This class is then used like this:
|
|
1043
1172
|
#
|
|
1044
|
-
#
|
|
1045
|
-
#
|
|
1046
|
-
#
|
|
1173
|
+
# constraints(Iphone) do
|
|
1174
|
+
# resources :iphones
|
|
1175
|
+
# end
|
|
1047
1176
|
def constraints(constraints = {}, &block)
|
|
1048
1177
|
scope(constraints: constraints, &block)
|
|
1049
1178
|
end
|
|
1050
1179
|
|
|
1051
1180
|
# Allows you to set default parameters for a route, such as this:
|
|
1052
|
-
#
|
|
1053
|
-
#
|
|
1054
|
-
#
|
|
1055
|
-
#
|
|
1181
|
+
#
|
|
1182
|
+
# defaults id: 'home' do
|
|
1183
|
+
# match 'scoped_pages/(:id)', to: 'pages#show'
|
|
1184
|
+
# end
|
|
1185
|
+
#
|
|
1186
|
+
# Using this, the `:id` parameter here will default to 'home'.
|
|
1056
1187
|
def defaults(defaults = {})
|
|
1057
1188
|
@scope = @scope.new(defaults: merge_defaults_scope(@scope[:defaults], defaults))
|
|
1058
1189
|
yield
|
|
@@ -1128,60 +1259,74 @@ module ActionDispatch
|
|
|
1128
1259
|
end
|
|
1129
1260
|
end
|
|
1130
1261
|
|
|
1131
|
-
# Resource routing allows you to quickly declare all of the common routes
|
|
1132
|
-
#
|
|
1133
|
-
#
|
|
1134
|
-
#
|
|
1262
|
+
# Resource routing allows you to quickly declare all of the common routes for a
|
|
1263
|
+
# given resourceful controller. Instead of declaring separate routes for your
|
|
1264
|
+
# `index`, `show`, `new`, `edit`, `create`, `update`, and `destroy` actions, a
|
|
1265
|
+
# resourceful route declares them in a single line of code:
|
|
1135
1266
|
#
|
|
1136
|
-
#
|
|
1267
|
+
# resources :photos
|
|
1137
1268
|
#
|
|
1138
|
-
# Sometimes, you have a resource that clients always look up without
|
|
1139
|
-
#
|
|
1140
|
-
#
|
|
1141
|
-
#
|
|
1269
|
+
# Sometimes, you have a resource that clients always look up without referencing
|
|
1270
|
+
# an ID. A common example, /profile always shows the profile of the currently
|
|
1271
|
+
# logged in user. In this case, you can use a singular resource to map /profile
|
|
1272
|
+
# (rather than /profile/:id) to the show action.
|
|
1142
1273
|
#
|
|
1143
|
-
#
|
|
1274
|
+
# resource :profile
|
|
1144
1275
|
#
|
|
1145
|
-
# It's common to have resources that are logically children of other
|
|
1146
|
-
# resources:
|
|
1276
|
+
# It's common to have resources that are logically children of other resources:
|
|
1147
1277
|
#
|
|
1148
|
-
#
|
|
1149
|
-
#
|
|
1150
|
-
#
|
|
1278
|
+
# resources :magazines do
|
|
1279
|
+
# resources :ads
|
|
1280
|
+
# end
|
|
1151
1281
|
#
|
|
1152
1282
|
# You may wish to organize groups of controllers under a namespace. Most
|
|
1153
|
-
# commonly, you might group a number of administrative controllers under
|
|
1154
|
-
#
|
|
1155
|
-
#
|
|
1156
|
-
#
|
|
1157
|
-
#
|
|
1158
|
-
# namespace "admin" do
|
|
1159
|
-
# resources :posts, :comments
|
|
1160
|
-
# end
|
|
1283
|
+
# commonly, you might group a number of administrative controllers under an
|
|
1284
|
+
# `admin` namespace. You would place these controllers under the
|
|
1285
|
+
# `app/controllers/admin` directory, and you can group them together in your
|
|
1286
|
+
# router:
|
|
1161
1287
|
#
|
|
1162
|
-
#
|
|
1163
|
-
#
|
|
1164
|
-
#
|
|
1288
|
+
# namespace "admin" do
|
|
1289
|
+
# resources :posts, :comments
|
|
1290
|
+
# end
|
|
1165
1291
|
#
|
|
1166
|
-
#
|
|
1292
|
+
# By default the `:id` parameter doesn't accept dots. If you need to use dots as
|
|
1293
|
+
# part of the `:id` parameter add a constraint which overrides this restriction,
|
|
1294
|
+
# e.g:
|
|
1167
1295
|
#
|
|
1168
|
-
#
|
|
1296
|
+
# resources :articles, id: /[^\/]+/
|
|
1169
1297
|
#
|
|
1298
|
+
# This allows any character other than a slash as part of your `:id`.
|
|
1170
1299
|
module Resources
|
|
1171
|
-
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
|
|
1172
|
-
#
|
|
1300
|
+
# CANONICAL_ACTIONS holds all actions that does not need a prefix or a path
|
|
1301
|
+
# appended since they fit properly in their scope level.
|
|
1173
1302
|
VALID_ON_OPTIONS = [:new, :collection, :member]
|
|
1174
1303
|
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
|
|
1175
1304
|
CANONICAL_ACTIONS = %w(index create new show update destroy)
|
|
1176
1305
|
|
|
1177
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
|
+
|
|
1178
1317
|
attr_reader :controller, :path, :param
|
|
1179
1318
|
|
|
1180
|
-
def initialize(entities, api_only, shallow,
|
|
1319
|
+
def initialize(entities, api_only, shallow, only: nil, except: nil, **options)
|
|
1181
1320
|
if options[:param].to_s.include?(":")
|
|
1182
1321
|
raise ArgumentError, ":param option can't contain colons"
|
|
1183
1322
|
end
|
|
1184
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
|
+
|
|
1185
1330
|
@name = entities.to_s
|
|
1186
1331
|
@path = (options[:path] || @name).to_s
|
|
1187
1332
|
@controller = (options[:controller] || @name).to_s
|
|
@@ -1190,16 +1335,12 @@ module ActionDispatch
|
|
|
1190
1335
|
@options = options
|
|
1191
1336
|
@shallow = shallow
|
|
1192
1337
|
@api_only = api_only
|
|
1193
|
-
@only =
|
|
1194
|
-
@except =
|
|
1338
|
+
@only = only
|
|
1339
|
+
@except = except
|
|
1195
1340
|
end
|
|
1196
1341
|
|
|
1197
1342
|
def default_actions
|
|
1198
|
-
|
|
1199
|
-
[:index, :create, :show, :update, :destroy]
|
|
1200
|
-
else
|
|
1201
|
-
[:index, :create, :new, :show, :update, :destroy, :edit]
|
|
1202
|
-
end
|
|
1343
|
+
self.class.default_actions(@api_only)
|
|
1203
1344
|
end
|
|
1204
1345
|
|
|
1205
1346
|
def actions
|
|
@@ -1232,8 +1373,8 @@ module ActionDispatch
|
|
|
1232
1373
|
|
|
1233
1374
|
alias :member_name :singular
|
|
1234
1375
|
|
|
1235
|
-
# Checks for uncountable plurals, and appends "_index" if the plural
|
|
1236
|
-
#
|
|
1376
|
+
# Checks for uncountable plurals, and appends "_index" if the plural and
|
|
1377
|
+
# singular form are the same.
|
|
1237
1378
|
def collection_name
|
|
1238
1379
|
singular == plural ? "#{plural}_index" : plural
|
|
1239
1380
|
end
|
|
@@ -1267,10 +1408,25 @@ module ActionDispatch
|
|
|
1267
1408
|
end
|
|
1268
1409
|
|
|
1269
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
|
|
1270
1416
|
end
|
|
1271
1417
|
|
|
1272
1418
|
class SingletonResource < Resource # :nodoc:
|
|
1273
|
-
|
|
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)
|
|
1274
1430
|
super
|
|
1275
1431
|
@as = nil
|
|
1276
1432
|
@controller = (options[:controller] || plural).to_s
|
|
@@ -1278,11 +1434,7 @@ module ActionDispatch
|
|
|
1278
1434
|
end
|
|
1279
1435
|
|
|
1280
1436
|
def default_actions
|
|
1281
|
-
|
|
1282
|
-
[:show, :create, :update, :destroy]
|
|
1283
|
-
else
|
|
1284
|
-
[:show, :create, :update, :destroy, :new, :edit]
|
|
1285
|
-
end
|
|
1437
|
+
self.class.default_actions(@api_only)
|
|
1286
1438
|
end
|
|
1287
1439
|
|
|
1288
1440
|
def plural
|
|
@@ -1306,50 +1458,51 @@ module ActionDispatch
|
|
|
1306
1458
|
@scope[:path_names].merge!(options)
|
|
1307
1459
|
end
|
|
1308
1460
|
|
|
1309
|
-
# Sometimes, you have a resource that clients always look up without
|
|
1310
|
-
#
|
|
1311
|
-
#
|
|
1312
|
-
#
|
|
1313
|
-
# the show action:
|
|
1461
|
+
# Sometimes, you have a resource that clients always look up without referencing
|
|
1462
|
+
# an ID. A common example, /profile always shows the profile of the currently
|
|
1463
|
+
# logged in user. In this case, you can use a singular resource to map /profile
|
|
1464
|
+
# (rather than /profile/:id) to the show action:
|
|
1314
1465
|
#
|
|
1315
|
-
#
|
|
1466
|
+
# resource :profile
|
|
1316
1467
|
#
|
|
1317
|
-
# This creates six different routes in your application, all mapping to
|
|
1318
|
-
#
|
|
1319
|
-
# the plural):
|
|
1468
|
+
# This creates six different routes in your application, all mapping to the
|
|
1469
|
+
# `Profiles` controller (note that the controller is named after the plural):
|
|
1320
1470
|
#
|
|
1321
|
-
#
|
|
1322
|
-
#
|
|
1323
|
-
#
|
|
1324
|
-
#
|
|
1325
|
-
#
|
|
1326
|
-
#
|
|
1471
|
+
# GET /profile/new
|
|
1472
|
+
# GET /profile
|
|
1473
|
+
# GET /profile/edit
|
|
1474
|
+
# PATCH/PUT /profile
|
|
1475
|
+
# DELETE /profile
|
|
1476
|
+
# POST /profile
|
|
1327
1477
|
#
|
|
1328
|
-
# If you want instances of a model to work with this resource via
|
|
1329
|
-
#
|
|
1330
|
-
#
|
|
1478
|
+
# If you want instances of a model to work with this resource via record
|
|
1479
|
+
# identification (e.g. in `form_with` or `redirect_to`), you will need to call
|
|
1480
|
+
# [resolve](rdoc-ref:CustomUrls#resolve):
|
|
1331
1481
|
#
|
|
1332
|
-
#
|
|
1333
|
-
#
|
|
1482
|
+
# resource :profile
|
|
1483
|
+
# resolve('Profile') { [:profile] }
|
|
1334
1484
|
#
|
|
1335
|
-
#
|
|
1336
|
-
#
|
|
1485
|
+
# # Enables this to work with singular routes:
|
|
1486
|
+
# form_with(model: @profile) {}
|
|
1337
1487
|
#
|
|
1338
|
-
#
|
|
1339
|
-
# Takes same options as resources
|
|
1340
|
-
def resource(*resources, &block)
|
|
1341
|
-
|
|
1488
|
+
# ### Options
|
|
1489
|
+
# Takes same options as [resources](rdoc-ref:#resources)
|
|
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
|
|
1342
1495
|
|
|
1343
|
-
if apply_common_behavior_for(:resource, resources, options, &block)
|
|
1496
|
+
if apply_common_behavior_for(:resource, resources, concerns:, **options, &block)
|
|
1344
1497
|
return self
|
|
1345
1498
|
end
|
|
1346
1499
|
|
|
1347
1500
|
with_scope_level(:resource) do
|
|
1348
|
-
options = apply_action_options options
|
|
1349
|
-
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
|
|
1350
1503
|
yield if block_given?
|
|
1351
1504
|
|
|
1352
|
-
concerns(
|
|
1505
|
+
concerns(*concerns) if concerns
|
|
1353
1506
|
|
|
1354
1507
|
new do
|
|
1355
1508
|
get :new
|
|
@@ -1366,156 +1519,163 @@ module ActionDispatch
|
|
|
1366
1519
|
self
|
|
1367
1520
|
end
|
|
1368
1521
|
|
|
1369
|
-
# In
|
|
1370
|
-
# and
|
|
1371
|
-
#
|
|
1372
|
-
# routing file, such as
|
|
1522
|
+
# In Rails, a resourceful route provides a mapping between HTTP verbs and URLs
|
|
1523
|
+
# and controller actions. By convention, each action also maps to particular
|
|
1524
|
+
# CRUD operations in a database. A single entry in the routing file, such as
|
|
1373
1525
|
#
|
|
1374
|
-
#
|
|
1526
|
+
# resources :photos
|
|
1375
1527
|
#
|
|
1376
|
-
# creates seven different routes in your application, all mapping to
|
|
1377
|
-
#
|
|
1528
|
+
# creates seven different routes in your application, all mapping to the
|
|
1529
|
+
# `Photos` controller:
|
|
1378
1530
|
#
|
|
1379
|
-
#
|
|
1380
|
-
#
|
|
1381
|
-
#
|
|
1382
|
-
#
|
|
1383
|
-
#
|
|
1384
|
-
#
|
|
1385
|
-
#
|
|
1531
|
+
# GET /photos
|
|
1532
|
+
# GET /photos/new
|
|
1533
|
+
# POST /photos
|
|
1534
|
+
# GET /photos/:id
|
|
1535
|
+
# GET /photos/:id/edit
|
|
1536
|
+
# PATCH/PUT /photos/:id
|
|
1537
|
+
# DELETE /photos/:id
|
|
1386
1538
|
#
|
|
1387
1539
|
# Resources can also be nested infinitely by using this block syntax:
|
|
1388
1540
|
#
|
|
1389
|
-
#
|
|
1390
|
-
#
|
|
1391
|
-
#
|
|
1541
|
+
# resources :photos do
|
|
1542
|
+
# resources :comments
|
|
1543
|
+
# end
|
|
1392
1544
|
#
|
|
1393
1545
|
# This generates the following comments routes:
|
|
1394
1546
|
#
|
|
1395
|
-
#
|
|
1396
|
-
#
|
|
1397
|
-
#
|
|
1398
|
-
#
|
|
1399
|
-
#
|
|
1400
|
-
#
|
|
1401
|
-
#
|
|
1547
|
+
# GET /photos/:photo_id/comments
|
|
1548
|
+
# GET /photos/:photo_id/comments/new
|
|
1549
|
+
# POST /photos/:photo_id/comments
|
|
1550
|
+
# GET /photos/:photo_id/comments/:id
|
|
1551
|
+
# GET /photos/:photo_id/comments/:id/edit
|
|
1552
|
+
# PATCH/PUT /photos/:photo_id/comments/:id
|
|
1553
|
+
# DELETE /photos/:photo_id/comments/:id
|
|
1402
1554
|
#
|
|
1403
|
-
#
|
|
1404
|
-
# Takes same options as match
|
|
1555
|
+
# ### Options
|
|
1556
|
+
# Takes same options as [match](rdoc-ref:Base#match) as well as:
|
|
1405
1557
|
#
|
|
1406
|
-
#
|
|
1407
|
-
# Allows you to change the segment component of the
|
|
1408
|
-
#
|
|
1558
|
+
# :path_names
|
|
1559
|
+
# : Allows you to change the segment component of the `edit` and `new`
|
|
1560
|
+
# actions. Actions not specified are not changed.
|
|
1409
1561
|
#
|
|
1410
|
-
#
|
|
1562
|
+
# resources :posts, path_names: { new: "brand_new" }
|
|
1411
1563
|
#
|
|
1412
|
-
#
|
|
1564
|
+
# The above example will now change /posts/new to /posts/brand_new.
|
|
1413
1565
|
#
|
|
1414
|
-
#
|
|
1415
|
-
# Allows you to change the path prefix for the resource.
|
|
1566
|
+
# :path
|
|
1567
|
+
# : Allows you to change the path prefix for the resource.
|
|
1416
1568
|
#
|
|
1417
|
-
#
|
|
1569
|
+
# resources :posts, path: 'postings'
|
|
1418
1570
|
#
|
|
1419
|
-
#
|
|
1571
|
+
# The resource and all segments will now route to /postings instead of
|
|
1572
|
+
# /posts.
|
|
1420
1573
|
#
|
|
1421
|
-
#
|
|
1422
|
-
# Only generate routes for the given actions.
|
|
1574
|
+
# :only
|
|
1575
|
+
# : Only generate routes for the given actions.
|
|
1423
1576
|
#
|
|
1424
|
-
#
|
|
1425
|
-
#
|
|
1577
|
+
# resources :cows, only: :show
|
|
1578
|
+
# resources :cows, only: [:show, :index]
|
|
1426
1579
|
#
|
|
1427
|
-
#
|
|
1428
|
-
# Generate all routes except for the given actions.
|
|
1580
|
+
# :except
|
|
1581
|
+
# : Generate all routes except for the given actions.
|
|
1429
1582
|
#
|
|
1430
|
-
#
|
|
1431
|
-
#
|
|
1583
|
+
# resources :cows, except: :show
|
|
1584
|
+
# resources :cows, except: [:show, :index]
|
|
1432
1585
|
#
|
|
1433
|
-
#
|
|
1434
|
-
# Generates shallow routes for nested resource(s). When placed on a parent
|
|
1435
|
-
#
|
|
1586
|
+
# :shallow
|
|
1587
|
+
# : Generates shallow routes for nested resource(s). When placed on a parent
|
|
1588
|
+
# resource, generates shallow routes for all nested resources.
|
|
1436
1589
|
#
|
|
1437
|
-
#
|
|
1438
|
-
#
|
|
1439
|
-
#
|
|
1590
|
+
# resources :posts, shallow: true do
|
|
1591
|
+
# resources :comments
|
|
1592
|
+
# end
|
|
1440
1593
|
#
|
|
1441
|
-
#
|
|
1594
|
+
# Is the same as:
|
|
1442
1595
|
#
|
|
1443
|
-
#
|
|
1444
|
-
#
|
|
1445
|
-
#
|
|
1446
|
-
#
|
|
1596
|
+
# resources :posts do
|
|
1597
|
+
# resources :comments, except: [:show, :edit, :update, :destroy]
|
|
1598
|
+
# end
|
|
1599
|
+
# resources :comments, only: [:show, :edit, :update, :destroy]
|
|
1447
1600
|
#
|
|
1448
|
-
#
|
|
1449
|
-
#
|
|
1450
|
-
#
|
|
1601
|
+
# This allows URLs for resources that otherwise would be deeply nested such
|
|
1602
|
+
# as a comment on a blog post like `/posts/a-long-permalink/comments/1234`
|
|
1603
|
+
# to be shortened to just `/comments/1234`.
|
|
1451
1604
|
#
|
|
1452
|
-
#
|
|
1605
|
+
# Set `shallow: false` on a child resource to ignore a parent's shallow
|
|
1606
|
+
# parameter.
|
|
1453
1607
|
#
|
|
1454
|
-
#
|
|
1455
|
-
# Prefixes nested shallow routes with the specified path.
|
|
1608
|
+
# :shallow_path
|
|
1609
|
+
# : Prefixes nested shallow routes with the specified path.
|
|
1456
1610
|
#
|
|
1457
|
-
#
|
|
1458
|
-
#
|
|
1459
|
-
#
|
|
1460
|
-
#
|
|
1461
|
-
#
|
|
1611
|
+
# scope shallow_path: "sekret" do
|
|
1612
|
+
# resources :posts do
|
|
1613
|
+
# resources :comments, shallow: true
|
|
1614
|
+
# end
|
|
1615
|
+
# end
|
|
1462
1616
|
#
|
|
1463
|
-
#
|
|
1617
|
+
# The `comments` resource here will have the following routes generated for
|
|
1618
|
+
# it:
|
|
1464
1619
|
#
|
|
1465
|
-
#
|
|
1466
|
-
#
|
|
1467
|
-
#
|
|
1468
|
-
#
|
|
1469
|
-
#
|
|
1470
|
-
#
|
|
1471
|
-
#
|
|
1620
|
+
# post_comments GET /posts/:post_id/comments(.:format)
|
|
1621
|
+
# post_comments POST /posts/:post_id/comments(.:format)
|
|
1622
|
+
# new_post_comment GET /posts/:post_id/comments/new(.:format)
|
|
1623
|
+
# edit_comment GET /sekret/comments/:id/edit(.:format)
|
|
1624
|
+
# comment GET /sekret/comments/:id(.:format)
|
|
1625
|
+
# comment PATCH/PUT /sekret/comments/:id(.:format)
|
|
1626
|
+
# comment DELETE /sekret/comments/:id(.:format)
|
|
1472
1627
|
#
|
|
1473
|
-
#
|
|
1474
|
-
# Prefixes nested shallow route names with specified prefix.
|
|
1628
|
+
# :shallow_prefix
|
|
1629
|
+
# : Prefixes nested shallow route names with specified prefix.
|
|
1475
1630
|
#
|
|
1476
|
-
#
|
|
1477
|
-
#
|
|
1478
|
-
#
|
|
1479
|
-
#
|
|
1480
|
-
#
|
|
1631
|
+
# scope shallow_prefix: "sekret" do
|
|
1632
|
+
# resources :posts do
|
|
1633
|
+
# resources :comments, shallow: true
|
|
1634
|
+
# end
|
|
1635
|
+
# end
|
|
1636
|
+
#
|
|
1637
|
+
# The `comments` resource here will have the following routes generated for
|
|
1638
|
+
# it:
|
|
1481
1639
|
#
|
|
1482
|
-
#
|
|
1640
|
+
# post_comments GET /posts/:post_id/comments(.:format)
|
|
1641
|
+
# post_comments POST /posts/:post_id/comments(.:format)
|
|
1642
|
+
# new_post_comment GET /posts/:post_id/comments/new(.:format)
|
|
1643
|
+
# edit_sekret_comment GET /comments/:id/edit(.:format)
|
|
1644
|
+
# sekret_comment GET /comments/:id(.:format)
|
|
1645
|
+
# sekret_comment PATCH/PUT /comments/:id(.:format)
|
|
1646
|
+
# sekret_comment DELETE /comments/:id(.:format)
|
|
1483
1647
|
#
|
|
1484
|
-
#
|
|
1485
|
-
#
|
|
1486
|
-
#
|
|
1487
|
-
# edit_sekret_comment GET /comments/:id/edit(.:format)
|
|
1488
|
-
# sekret_comment GET /comments/:id(.:format)
|
|
1489
|
-
# sekret_comment PATCH/PUT /comments/:id(.:format)
|
|
1490
|
-
# sekret_comment DELETE /comments/:id(.:format)
|
|
1648
|
+
# :format
|
|
1649
|
+
# : Allows you to specify the default value for optional `format` segment or
|
|
1650
|
+
# disable it by supplying `false`.
|
|
1491
1651
|
#
|
|
1492
|
-
#
|
|
1493
|
-
# Allows you to
|
|
1494
|
-
# segment or disable it by supplying +false+.
|
|
1652
|
+
# :param
|
|
1653
|
+
# : Allows you to override the default param name of `:id` in the URL.
|
|
1495
1654
|
#
|
|
1496
|
-
# [:param]
|
|
1497
|
-
# Allows you to override the default param name of +:id+ in the URL.
|
|
1498
1655
|
#
|
|
1499
|
-
#
|
|
1656
|
+
# ### Examples
|
|
1500
1657
|
#
|
|
1501
|
-
#
|
|
1502
|
-
#
|
|
1658
|
+
# # routes call Admin::PostsController
|
|
1659
|
+
# resources :posts, module: "admin"
|
|
1503
1660
|
#
|
|
1504
|
-
#
|
|
1505
|
-
#
|
|
1506
|
-
def resources(*resources, &block)
|
|
1507
|
-
|
|
1661
|
+
# # resource actions are at /admin/posts.
|
|
1662
|
+
# resources :posts, path: "admin/posts"
|
|
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
|
|
1508
1668
|
|
|
1509
|
-
if apply_common_behavior_for(:resources, resources, options, &block)
|
|
1669
|
+
if apply_common_behavior_for(:resources, resources, concerns:, **options, &block)
|
|
1510
1670
|
return self
|
|
1511
1671
|
end
|
|
1512
1672
|
|
|
1513
1673
|
with_scope_level(:resources) do
|
|
1514
|
-
options = apply_action_options options
|
|
1515
|
-
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
|
|
1516
1676
|
yield if block_given?
|
|
1517
1677
|
|
|
1518
|
-
concerns(
|
|
1678
|
+
concerns(*concerns) if concerns
|
|
1519
1679
|
|
|
1520
1680
|
collection do
|
|
1521
1681
|
get :index if parent_resource.actions.include?(:index)
|
|
@@ -1535,16 +1695,15 @@ module ActionDispatch
|
|
|
1535
1695
|
|
|
1536
1696
|
# To add a route to the collection:
|
|
1537
1697
|
#
|
|
1538
|
-
#
|
|
1539
|
-
#
|
|
1540
|
-
#
|
|
1698
|
+
# resources :photos do
|
|
1699
|
+
# collection do
|
|
1700
|
+
# get 'search'
|
|
1701
|
+
# end
|
|
1541
1702
|
# end
|
|
1542
|
-
# end
|
|
1543
1703
|
#
|
|
1544
|
-
# This will enable
|
|
1545
|
-
#
|
|
1546
|
-
#
|
|
1547
|
-
# route helpers.
|
|
1704
|
+
# This will enable Rails to recognize paths such as `/photos/search` with GET,
|
|
1705
|
+
# and route to the search action of `PhotosController`. It will also create the
|
|
1706
|
+
# `search_photos_url` and `search_photos_path` route helpers.
|
|
1548
1707
|
def collection(&block)
|
|
1549
1708
|
unless resource_scope?
|
|
1550
1709
|
raise ArgumentError, "can't use collection outside resource(s) scope"
|
|
@@ -1557,15 +1716,15 @@ module ActionDispatch
|
|
|
1557
1716
|
|
|
1558
1717
|
# To add a member route, add a member block into the resource block:
|
|
1559
1718
|
#
|
|
1560
|
-
#
|
|
1561
|
-
#
|
|
1562
|
-
#
|
|
1719
|
+
# resources :photos do
|
|
1720
|
+
# member do
|
|
1721
|
+
# get 'preview'
|
|
1722
|
+
# end
|
|
1563
1723
|
# end
|
|
1564
|
-
# end
|
|
1565
1724
|
#
|
|
1566
|
-
# This will recognize
|
|
1567
|
-
#
|
|
1568
|
-
#
|
|
1725
|
+
# This will recognize `/photos/1/preview` with GET, and route to the preview
|
|
1726
|
+
# action of `PhotosController`. It will also create the `preview_photo_url` and
|
|
1727
|
+
# `preview_photo_path` helpers.
|
|
1569
1728
|
def member(&block)
|
|
1570
1729
|
unless resource_scope?
|
|
1571
1730
|
raise ArgumentError, "can't use member outside resource(s) scope"
|
|
@@ -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
|
|
@@ -1632,29 +1791,28 @@ module ActionDispatch
|
|
|
1632
1791
|
!parent_resource.singleton? && @scope[:shallow]
|
|
1633
1792
|
end
|
|
1634
1793
|
|
|
1635
|
-
# Loads another routes file with the given
|
|
1636
|
-
#
|
|
1637
|
-
#
|
|
1638
|
-
#
|
|
1639
|
-
#
|
|
1640
|
-
#
|
|
1641
|
-
#
|
|
1642
|
-
#
|
|
1643
|
-
#
|
|
1644
|
-
#
|
|
1645
|
-
#
|
|
1646
|
-
#
|
|
1647
|
-
#
|
|
1648
|
-
#
|
|
1649
|
-
#
|
|
1650
|
-
#
|
|
1651
|
-
#
|
|
1652
|
-
#
|
|
1653
|
-
#
|
|
1654
|
-
#
|
|
1655
|
-
#
|
|
1656
|
-
#
|
|
1657
|
-
# developers to have a single routes file.
|
|
1794
|
+
# Loads another routes file with the given `name` located inside the
|
|
1795
|
+
# `config/routes` directory. In that file, you can use the normal routing DSL,
|
|
1796
|
+
# but *do not* surround it with a `Rails.application.routes.draw` block.
|
|
1797
|
+
#
|
|
1798
|
+
# # config/routes.rb
|
|
1799
|
+
# Rails.application.routes.draw do
|
|
1800
|
+
# draw :admin # Loads `config/routes/admin.rb`
|
|
1801
|
+
# draw "third_party/some_gem" # Loads `config/routes/third_party/some_gem.rb`
|
|
1802
|
+
# end
|
|
1803
|
+
#
|
|
1804
|
+
# # config/routes/admin.rb
|
|
1805
|
+
# namespace :admin do
|
|
1806
|
+
# resources :accounts
|
|
1807
|
+
# end
|
|
1808
|
+
#
|
|
1809
|
+
# # config/routes/third_party/some_gem.rb
|
|
1810
|
+
# mount SomeGem::Engine, at: "/some_gem"
|
|
1811
|
+
#
|
|
1812
|
+
# **CAUTION:** Use this feature with care. Having multiple routes files can
|
|
1813
|
+
# negatively impact discoverability and readability. For most applications —
|
|
1814
|
+
# even those with a few hundred routes — it's easier for developers to have a
|
|
1815
|
+
# single routes file.
|
|
1658
1816
|
def draw(name)
|
|
1659
1817
|
path = @draw_paths.find do |_path|
|
|
1660
1818
|
File.exist? "#{_path}/#{name}.rb"
|
|
@@ -1671,59 +1829,77 @@ module ActionDispatch
|
|
|
1671
1829
|
instance_eval(File.read(route_path), route_path.to_s)
|
|
1672
1830
|
end
|
|
1673
1831
|
|
|
1674
|
-
# Matches a URL pattern to one or more routes.
|
|
1675
|
-
#
|
|
1676
|
-
#
|
|
1677
|
-
#
|
|
1678
|
-
#
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1832
|
+
# Matches a URL pattern to one or more routes. For more information, see
|
|
1833
|
+
# [match](rdoc-ref:Base#match).
|
|
1834
|
+
#
|
|
1835
|
+
# match 'path', to: 'controller#action', via: :post
|
|
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
|
|
1686
1863
|
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
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
|
|
1693
1875
|
else
|
|
1694
|
-
|
|
1876
|
+
to ||= hash_to
|
|
1695
1877
|
end
|
|
1696
|
-
else
|
|
1697
|
-
options[:to] = to
|
|
1698
1878
|
end
|
|
1699
|
-
|
|
1700
|
-
options.delete(path)
|
|
1701
|
-
paths = [path]
|
|
1702
|
-
else
|
|
1703
|
-
options = rest.pop || {}
|
|
1704
|
-
paths = [path] + rest
|
|
1705
1879
|
end
|
|
1706
1880
|
|
|
1707
|
-
|
|
1708
|
-
defaults
|
|
1709
|
-
|
|
1710
|
-
|
|
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
|
|
1711
1887
|
end
|
|
1712
1888
|
end
|
|
1713
1889
|
|
|
1714
|
-
# You can specify what
|
|
1890
|
+
# You can specify what Rails should route "/" to with the root method:
|
|
1715
1891
|
#
|
|
1716
|
-
#
|
|
1892
|
+
# root to: 'pages#main'
|
|
1717
1893
|
#
|
|
1718
|
-
# For options, see
|
|
1894
|
+
# For options, see `match`, as `root` uses it internally.
|
|
1719
1895
|
#
|
|
1720
1896
|
# You can also pass a string which will expand
|
|
1721
1897
|
#
|
|
1722
|
-
#
|
|
1898
|
+
# root 'pages#main'
|
|
1723
1899
|
#
|
|
1724
|
-
# You should put the root route at the top of
|
|
1725
|
-
#
|
|
1726
|
-
#
|
|
1900
|
+
# You should put the root route at the top of `config/routes.rb`, because this
|
|
1901
|
+
# means it will be matched first. As this is the most popular route of most
|
|
1902
|
+
# Rails applications, this is beneficial.
|
|
1727
1903
|
def root(path, options = {})
|
|
1728
1904
|
if path.is_a?(String)
|
|
1729
1905
|
options[:to] = path
|
|
@@ -1749,22 +1925,21 @@ module ActionDispatch
|
|
|
1749
1925
|
@scope[:scope_level_resource]
|
|
1750
1926
|
end
|
|
1751
1927
|
|
|
1752
|
-
def apply_common_behavior_for(method, resources, options, &block)
|
|
1928
|
+
def apply_common_behavior_for(method, resources, shallow: nil, **options, &block)
|
|
1753
1929
|
if resources.length > 1
|
|
1754
|
-
resources.each { |r| public_send(method, r, options, &block) }
|
|
1930
|
+
resources.each { |r| public_send(method, r, shallow:, **options, &block) }
|
|
1755
1931
|
return true
|
|
1756
1932
|
end
|
|
1757
1933
|
|
|
1758
|
-
if
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
public_send(method, resources.pop, options, &block)
|
|
1934
|
+
if shallow
|
|
1935
|
+
self.shallow do
|
|
1936
|
+
public_send(method, resources.pop, **options, &block)
|
|
1762
1937
|
end
|
|
1763
1938
|
return true
|
|
1764
1939
|
end
|
|
1765
1940
|
|
|
1766
1941
|
if resource_scope?
|
|
1767
|
-
nested { public_send(method, resources.pop, options, &block) }
|
|
1942
|
+
nested { public_send(method, resources.pop, shallow:, **options, &block) }
|
|
1768
1943
|
return true
|
|
1769
1944
|
end
|
|
1770
1945
|
|
|
@@ -1773,9 +1948,11 @@ module ActionDispatch
|
|
|
1773
1948
|
end
|
|
1774
1949
|
|
|
1775
1950
|
scope_options = options.slice!(*RESOURCE_OPTIONS)
|
|
1951
|
+
scope_options[:shallow] = shallow unless shallow.nil?
|
|
1952
|
+
|
|
1776
1953
|
unless scope_options.empty?
|
|
1777
|
-
scope(scope_options) do
|
|
1778
|
-
public_send(method, resources.pop, options, &block)
|
|
1954
|
+
scope(**scope_options) do
|
|
1955
|
+
public_send(method, resources.pop, **options, &block)
|
|
1779
1956
|
end
|
|
1780
1957
|
return true
|
|
1781
1958
|
end
|
|
@@ -1783,17 +1960,32 @@ module ActionDispatch
|
|
|
1783
1960
|
false
|
|
1784
1961
|
end
|
|
1785
1962
|
|
|
1786
|
-
def apply_action_options(options)
|
|
1963
|
+
def apply_action_options(method, options)
|
|
1787
1964
|
return options if action_options? options
|
|
1788
|
-
options.merge scope_action_options
|
|
1965
|
+
options.merge scope_action_options(method)
|
|
1789
1966
|
end
|
|
1790
1967
|
|
|
1791
1968
|
def action_options?(options)
|
|
1792
1969
|
options[:only] || options[:except]
|
|
1793
1970
|
end
|
|
1794
1971
|
|
|
1795
|
-
def scope_action_options
|
|
1796
|
-
@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
|
|
1797
1989
|
end
|
|
1798
1990
|
|
|
1799
1991
|
def resource_scope?
|
|
@@ -1851,9 +2043,10 @@ module ActionDispatch
|
|
|
1851
2043
|
end
|
|
1852
2044
|
|
|
1853
2045
|
def shallow_scope
|
|
1854
|
-
scope =
|
|
1855
|
-
|
|
1856
|
-
|
|
2046
|
+
@scope = @scope.new(
|
|
2047
|
+
as: @scope[:shallow_prefix],
|
|
2048
|
+
path: @scope[:shallow_path],
|
|
2049
|
+
)
|
|
1857
2050
|
|
|
1858
2051
|
yield
|
|
1859
2052
|
ensure
|
|
@@ -1875,7 +2068,7 @@ module ActionDispatch
|
|
|
1875
2068
|
end
|
|
1876
2069
|
|
|
1877
2070
|
def prefix_name_for_action(as, action)
|
|
1878
|
-
if as
|
|
2071
|
+
if as && as != DEFAULT
|
|
1879
2072
|
prefix = as
|
|
1880
2073
|
elsif !canonical_action?(action)
|
|
1881
2074
|
prefix = action
|
|
@@ -1891,7 +2084,7 @@ module ActionDispatch
|
|
|
1891
2084
|
name_prefix = @scope[:as]
|
|
1892
2085
|
|
|
1893
2086
|
if parent_resource
|
|
1894
|
-
return nil unless as || action
|
|
2087
|
+
return nil unless as != DEFAULT || action
|
|
1895
2088
|
|
|
1896
2089
|
collection_name = parent_resource.collection_name
|
|
1897
2090
|
member_name = parent_resource.member_name
|
|
@@ -1901,10 +2094,10 @@ module ActionDispatch
|
|
|
1901
2094
|
candidate = action_name.select(&:present?).join("_")
|
|
1902
2095
|
|
|
1903
2096
|
unless candidate.empty?
|
|
1904
|
-
# If a name was not explicitly given, we check if it is valid
|
|
1905
|
-
#
|
|
1906
|
-
#
|
|
1907
|
-
if as
|
|
2097
|
+
# If a name was not explicitly given, we check if it is valid and return nil in
|
|
2098
|
+
# case it isn't. Otherwise, we pass the invalid name forward so the underlying
|
|
2099
|
+
# router engine treats it and raises an exception.
|
|
2100
|
+
if as == DEFAULT
|
|
1908
2101
|
candidate unless !candidate.match?(/\A[_a-z]/i) || has_named_route?(candidate)
|
|
1909
2102
|
else
|
|
1910
2103
|
candidate
|
|
@@ -1935,42 +2128,35 @@ module ActionDispatch
|
|
|
1935
2128
|
@scope = @scope.parent
|
|
1936
2129
|
end
|
|
1937
2130
|
|
|
1938
|
-
def map_match(
|
|
1939
|
-
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)
|
|
1940
2133
|
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
|
|
1941
2134
|
end
|
|
1942
2135
|
|
|
1943
2136
|
if @scope[:to]
|
|
1944
|
-
|
|
2137
|
+
to ||= @scope[:to]
|
|
1945
2138
|
end
|
|
1946
2139
|
|
|
1947
2140
|
if @scope[:controller] && @scope[:action]
|
|
1948
|
-
|
|
2141
|
+
to ||= "#{@scope[:controller]}##{@scope[:action]}"
|
|
1949
2142
|
end
|
|
1950
2143
|
|
|
1951
|
-
controller
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
path_types = paths.group_by(&:class)
|
|
1962
|
-
(path_types[String] || []).each do |_path|
|
|
1963
|
-
route_options = options.dup
|
|
1964
|
-
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
|
|
1965
2153
|
raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings."
|
|
1966
2154
|
end
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
route_options = options.dup
|
|
1973
|
-
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)
|
|
1974
2160
|
end
|
|
1975
2161
|
|
|
1976
2162
|
self
|
|
@@ -1991,28 +2177,28 @@ module ActionDispatch
|
|
|
1991
2177
|
%r{^/?[-\w]+/[-\w/]+$}.match?(path)
|
|
1992
2178
|
end
|
|
1993
2179
|
|
|
1994
|
-
def decomposed_match(path, controller,
|
|
1995
|
-
if on
|
|
1996
|
-
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) }
|
|
1997
2183
|
else
|
|
1998
2184
|
case @scope.scope_level
|
|
1999
2185
|
when :resources
|
|
2000
|
-
nested { decomposed_match(path, controller,
|
|
2186
|
+
nested { decomposed_match(path, controller, as, action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping) }
|
|
2001
2187
|
when :resource
|
|
2002
|
-
member { decomposed_match(path, controller,
|
|
2188
|
+
member { decomposed_match(path, controller, as, action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping) }
|
|
2003
2189
|
else
|
|
2004
|
-
add_route(path, controller,
|
|
2190
|
+
add_route(path, controller, as, action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping)
|
|
2005
2191
|
end
|
|
2006
2192
|
end
|
|
2007
2193
|
end
|
|
2008
2194
|
|
|
2009
|
-
def add_route(action, controller,
|
|
2195
|
+
def add_route(action, controller, as, options_action, _path, to, via, formatted, anchor, options_constraints, internal, options_mapping)
|
|
2010
2196
|
path = path_for_action(action, _path)
|
|
2011
2197
|
raise ArgumentError, "path is required" if path.blank?
|
|
2012
2198
|
|
|
2013
2199
|
action = action.to_s
|
|
2014
2200
|
|
|
2015
|
-
default_action =
|
|
2201
|
+
default_action = options_action || @scope[:action]
|
|
2016
2202
|
|
|
2017
2203
|
if /^[\w\-\/]+$/.match?(action)
|
|
2018
2204
|
default_action ||= action.tr("-", "_") unless action.include?("/")
|
|
@@ -2020,102 +2206,94 @@ module ActionDispatch
|
|
|
2020
2206
|
action = nil
|
|
2021
2207
|
end
|
|
2022
2208
|
|
|
2023
|
-
as
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
name_for_action(options.delete(:as), action)
|
|
2027
|
-
end
|
|
2028
|
-
|
|
2029
|
-
path = Mapping.normalize_path RFC2396_PARSER.escape(path), formatted
|
|
2030
|
-
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
|
|
2031
2212
|
|
|
2032
|
-
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)
|
|
2033
2214
|
@set.add_route(mapping, as)
|
|
2034
2215
|
end
|
|
2035
2216
|
|
|
2036
2217
|
def match_root_route(options)
|
|
2037
|
-
|
|
2038
|
-
match(*args)
|
|
2218
|
+
match("/", as: :root, via: :get, **options)
|
|
2039
2219
|
end
|
|
2040
2220
|
end
|
|
2041
2221
|
|
|
2042
|
-
# Routing Concerns allow you to declare common routes that can be reused
|
|
2043
|
-
#
|
|
2222
|
+
# Routing Concerns allow you to declare common routes that can be reused inside
|
|
2223
|
+
# others resources and routes.
|
|
2044
2224
|
#
|
|
2045
|
-
#
|
|
2046
|
-
#
|
|
2047
|
-
#
|
|
2225
|
+
# concern :commentable do
|
|
2226
|
+
# resources :comments
|
|
2227
|
+
# end
|
|
2048
2228
|
#
|
|
2049
|
-
#
|
|
2050
|
-
#
|
|
2051
|
-
#
|
|
2229
|
+
# concern :image_attachable do
|
|
2230
|
+
# resources :images, only: :index
|
|
2231
|
+
# end
|
|
2052
2232
|
#
|
|
2053
2233
|
# These concerns are used in Resources routing:
|
|
2054
2234
|
#
|
|
2055
|
-
#
|
|
2235
|
+
# resources :messages, concerns: [:commentable, :image_attachable]
|
|
2056
2236
|
#
|
|
2057
2237
|
# or in a scope or namespace:
|
|
2058
2238
|
#
|
|
2059
|
-
#
|
|
2060
|
-
#
|
|
2061
|
-
#
|
|
2239
|
+
# namespace :posts do
|
|
2240
|
+
# concerns :commentable
|
|
2241
|
+
# end
|
|
2062
2242
|
module Concerns
|
|
2063
2243
|
# Define a routing concern using a name.
|
|
2064
2244
|
#
|
|
2065
|
-
# Concerns may be defined inline, using a block, or handled by
|
|
2066
|
-
#
|
|
2245
|
+
# Concerns may be defined inline, using a block, or handled by another object,
|
|
2246
|
+
# by passing that object as the second parameter.
|
|
2067
2247
|
#
|
|
2068
|
-
# The concern object, if supplied, should respond to
|
|
2069
|
-
#
|
|
2248
|
+
# The concern object, if supplied, should respond to `call`, which will receive
|
|
2249
|
+
# two parameters:
|
|
2070
2250
|
#
|
|
2071
|
-
#
|
|
2072
|
-
#
|
|
2251
|
+
# * The current mapper
|
|
2252
|
+
# * A hash of options which the concern object may use
|
|
2073
2253
|
#
|
|
2074
|
-
# Options may also be used by concerns defined in a block by accepting
|
|
2075
|
-
#
|
|
2076
|
-
#
|
|
2077
|
-
#
|
|
2254
|
+
# Options may also be used by concerns defined in a block by accepting a block
|
|
2255
|
+
# parameter. So, using a block, you might do something as simple as limit the
|
|
2256
|
+
# actions available on certain resources, passing standard resource options
|
|
2257
|
+
# through the concern:
|
|
2078
2258
|
#
|
|
2079
|
-
#
|
|
2080
|
-
#
|
|
2081
|
-
#
|
|
2259
|
+
# concern :commentable do |options|
|
|
2260
|
+
# resources :comments, options
|
|
2261
|
+
# end
|
|
2082
2262
|
#
|
|
2083
|
-
#
|
|
2084
|
-
#
|
|
2085
|
-
#
|
|
2086
|
-
#
|
|
2087
|
-
#
|
|
2263
|
+
# resources :posts, concerns: :commentable
|
|
2264
|
+
# resources :archived_posts do
|
|
2265
|
+
# # Don't allow comments on archived posts
|
|
2266
|
+
# concerns :commentable, only: [:index, :show]
|
|
2267
|
+
# end
|
|
2088
2268
|
#
|
|
2089
|
-
# Or, using a callable object, you might implement something more
|
|
2090
|
-
#
|
|
2091
|
-
# routes file.
|
|
2269
|
+
# Or, using a callable object, you might implement something more specific to
|
|
2270
|
+
# your application, which would be out of place in your routes file.
|
|
2092
2271
|
#
|
|
2093
|
-
#
|
|
2094
|
-
#
|
|
2095
|
-
#
|
|
2096
|
-
#
|
|
2097
|
-
#
|
|
2272
|
+
# # purchasable.rb
|
|
2273
|
+
# class Purchasable
|
|
2274
|
+
# def initialize(defaults = {})
|
|
2275
|
+
# @defaults = defaults
|
|
2276
|
+
# end
|
|
2098
2277
|
#
|
|
2099
|
-
#
|
|
2100
|
-
#
|
|
2101
|
-
#
|
|
2102
|
-
#
|
|
2103
|
-
#
|
|
2278
|
+
# def call(mapper, options = {})
|
|
2279
|
+
# options = @defaults.merge(options)
|
|
2280
|
+
# mapper.resources :purchases
|
|
2281
|
+
# mapper.resources :receipts
|
|
2282
|
+
# mapper.resources :returns if options[:returnable]
|
|
2283
|
+
# end
|
|
2104
2284
|
# end
|
|
2105
|
-
# end
|
|
2106
2285
|
#
|
|
2107
|
-
#
|
|
2108
|
-
#
|
|
2286
|
+
# # routes.rb
|
|
2287
|
+
# concern :purchasable, Purchasable.new(returnable: true)
|
|
2109
2288
|
#
|
|
2110
|
-
#
|
|
2111
|
-
#
|
|
2112
|
-
#
|
|
2113
|
-
#
|
|
2114
|
-
#
|
|
2289
|
+
# resources :toys, concerns: :purchasable
|
|
2290
|
+
# resources :electronics, concerns: :purchasable
|
|
2291
|
+
# resources :pets do
|
|
2292
|
+
# concerns :purchasable, returnable: false
|
|
2293
|
+
# end
|
|
2115
2294
|
#
|
|
2116
|
-
# Any routing helpers can be used inside a concern. If using a
|
|
2117
|
-
#
|
|
2118
|
-
# <tt>call</tt>.
|
|
2295
|
+
# Any routing helpers can be used inside a concern. If using a callable, they're
|
|
2296
|
+
# accessible from the Mapper that's passed to `call`.
|
|
2119
2297
|
def concern(name, callable = nil, &block)
|
|
2120
2298
|
callable ||= lambda { |mapper, options| mapper.instance_exec(options, &block) }
|
|
2121
2299
|
@concerns[name] = callable
|
|
@@ -2123,17 +2301,16 @@ module ActionDispatch
|
|
|
2123
2301
|
|
|
2124
2302
|
# Use the named concerns
|
|
2125
2303
|
#
|
|
2126
|
-
#
|
|
2127
|
-
#
|
|
2128
|
-
#
|
|
2304
|
+
# resources :posts do
|
|
2305
|
+
# concerns :commentable
|
|
2306
|
+
# end
|
|
2129
2307
|
#
|
|
2130
2308
|
# Concerns also work in any routes helper that you want to use:
|
|
2131
2309
|
#
|
|
2132
|
-
#
|
|
2133
|
-
#
|
|
2134
|
-
#
|
|
2135
|
-
def concerns(*args)
|
|
2136
|
-
options = args.extract_options!
|
|
2310
|
+
# namespace :posts do
|
|
2311
|
+
# concerns :commentable
|
|
2312
|
+
# end
|
|
2313
|
+
def concerns(*args, **options)
|
|
2137
2314
|
args.flatten.each do |name|
|
|
2138
2315
|
if concern = @concerns[name]
|
|
2139
2316
|
concern.call(self, options)
|
|
@@ -2145,53 +2322,55 @@ module ActionDispatch
|
|
|
2145
2322
|
end
|
|
2146
2323
|
|
|
2147
2324
|
module CustomUrls
|
|
2148
|
-
# Define custom URL helpers that will be added to the application's
|
|
2149
|
-
#
|
|
2150
|
-
#
|
|
2325
|
+
# Define custom URL helpers that will be added to the application's routes. This
|
|
2326
|
+
# allows you to override and/or replace the default behavior of routing helpers,
|
|
2327
|
+
# e.g:
|
|
2151
2328
|
#
|
|
2152
|
-
#
|
|
2153
|
-
#
|
|
2154
|
-
#
|
|
2329
|
+
# direct :homepage do
|
|
2330
|
+
# "https://rubyonrails.org"
|
|
2331
|
+
# end
|
|
2155
2332
|
#
|
|
2156
|
-
#
|
|
2157
|
-
#
|
|
2158
|
-
#
|
|
2333
|
+
# direct :commentable do |model|
|
|
2334
|
+
# [ model, anchor: model.dom_id ]
|
|
2335
|
+
# end
|
|
2159
2336
|
#
|
|
2160
|
-
#
|
|
2161
|
-
#
|
|
2162
|
-
#
|
|
2337
|
+
# direct :main do
|
|
2338
|
+
# { controller: "pages", action: "index", subdomain: "www" }
|
|
2339
|
+
# end
|
|
2163
2340
|
#
|
|
2164
|
-
# The return value from the block passed to
|
|
2165
|
-
# arguments for
|
|
2166
|
-
#
|
|
2341
|
+
# The return value from the block passed to `direct` must be a valid set of
|
|
2342
|
+
# arguments for `url_for` which will actually build the URL string. This can be
|
|
2343
|
+
# one of the following:
|
|
2167
2344
|
#
|
|
2168
|
-
# *
|
|
2169
|
-
# *
|
|
2170
|
-
# *
|
|
2171
|
-
# *
|
|
2172
|
-
# *
|
|
2345
|
+
# * A string, which is treated as a generated URL
|
|
2346
|
+
# * A hash, e.g. `{ controller: "pages", action: "index" }`
|
|
2347
|
+
# * An array, which is passed to `polymorphic_url`
|
|
2348
|
+
# * An Active Model instance
|
|
2349
|
+
# * An Active Model class
|
|
2173
2350
|
#
|
|
2174
|
-
# NOTE: Other URL helpers can be called in the block but be careful not to invoke
|
|
2175
|
-
# your custom URL helper again otherwise it will result in a stack overflow error.
|
|
2176
2351
|
#
|
|
2177
|
-
#
|
|
2178
|
-
# your URL helper
|
|
2352
|
+
# NOTE: Other URL helpers can be called in the block but be careful not to
|
|
2353
|
+
# invoke your custom URL helper again otherwise it will result in a stack
|
|
2354
|
+
# overflow error.
|
|
2179
2355
|
#
|
|
2180
|
-
#
|
|
2181
|
-
#
|
|
2182
|
-
# end
|
|
2356
|
+
# You can also specify default options that will be passed through to your URL
|
|
2357
|
+
# helper definition, e.g:
|
|
2183
2358
|
#
|
|
2184
|
-
#
|
|
2185
|
-
#
|
|
2186
|
-
#
|
|
2359
|
+
# direct :browse, page: 1, size: 10 do |options|
|
|
2360
|
+
# [ :products, options.merge(params.permit(:page, :size).to_h.symbolize_keys) ]
|
|
2361
|
+
# end
|
|
2362
|
+
#
|
|
2363
|
+
# In this instance the `params` object comes from the context in which the block
|
|
2364
|
+
# is executed, e.g. generating a URL inside a controller action or a view. If
|
|
2365
|
+
# the block is executed where there isn't a `params` object such as this:
|
|
2187
2366
|
#
|
|
2188
|
-
#
|
|
2367
|
+
# Rails.application.routes.url_helpers.browse_path
|
|
2189
2368
|
#
|
|
2190
|
-
# then it will raise a
|
|
2369
|
+
# then it will raise a `NameError`. Because of this you need to be aware of the
|
|
2191
2370
|
# context in which you will use your custom URL helper when defining it.
|
|
2192
2371
|
#
|
|
2193
|
-
# NOTE: The
|
|
2194
|
-
#
|
|
2372
|
+
# NOTE: The `direct` method can't be used inside of a scope block such as
|
|
2373
|
+
# `namespace` or `scope` and will raise an error if it detects that it is.
|
|
2195
2374
|
def direct(name, options = {}, &block)
|
|
2196
2375
|
unless @scope.root?
|
|
2197
2376
|
raise RuntimeError, "The direct method can't be used inside a routes scope block"
|
|
@@ -2200,50 +2379,50 @@ module ActionDispatch
|
|
|
2200
2379
|
@set.add_url_helper(name, options, &block)
|
|
2201
2380
|
end
|
|
2202
2381
|
|
|
2203
|
-
# Define custom polymorphic mappings of models to URLs. This alters the
|
|
2204
|
-
#
|
|
2205
|
-
#
|
|
2382
|
+
# Define custom polymorphic mappings of models to URLs. This alters the behavior
|
|
2383
|
+
# of `polymorphic_url` and consequently the behavior of `link_to`, `form_with`
|
|
2384
|
+
# and `form_for` when passed a model instance, e.g:
|
|
2206
2385
|
#
|
|
2207
|
-
#
|
|
2386
|
+
# resource :basket
|
|
2208
2387
|
#
|
|
2209
|
-
#
|
|
2210
|
-
#
|
|
2211
|
-
#
|
|
2388
|
+
# resolve "Basket" do
|
|
2389
|
+
# [:basket]
|
|
2390
|
+
# end
|
|
2212
2391
|
#
|
|
2213
|
-
# This will now generate "/basket" when a
|
|
2214
|
-
#
|
|
2392
|
+
# This will now generate "/basket" when a `Basket` instance is passed to
|
|
2393
|
+
# `link_to`, `form_with` or `form_for` instead of the standard "/baskets/:id".
|
|
2215
2394
|
#
|
|
2216
|
-
# NOTE: This custom behavior only applies to simple polymorphic URLs where
|
|
2217
|
-
#
|
|
2395
|
+
# NOTE: This custom behavior only applies to simple polymorphic URLs where a
|
|
2396
|
+
# single model instance is passed and not more complicated forms, e.g:
|
|
2218
2397
|
#
|
|
2219
|
-
#
|
|
2220
|
-
#
|
|
2221
|
-
#
|
|
2222
|
-
#
|
|
2223
|
-
#
|
|
2398
|
+
# # config/routes.rb
|
|
2399
|
+
# resource :profile
|
|
2400
|
+
# namespace :admin do
|
|
2401
|
+
# resources :users
|
|
2402
|
+
# end
|
|
2224
2403
|
#
|
|
2225
|
-
#
|
|
2404
|
+
# resolve("User") { [:profile] }
|
|
2226
2405
|
#
|
|
2227
|
-
#
|
|
2228
|
-
#
|
|
2229
|
-
#
|
|
2406
|
+
# # app/views/application/_menu.html.erb
|
|
2407
|
+
# link_to "Profile", @current_user
|
|
2408
|
+
# link_to "Profile", [:admin, @current_user]
|
|
2230
2409
|
#
|
|
2231
|
-
# The first
|
|
2232
|
-
#
|
|
2410
|
+
# The first `link_to` will generate "/profile" but the second will generate the
|
|
2411
|
+
# standard polymorphic URL of "/admin/users/1".
|
|
2233
2412
|
#
|
|
2234
|
-
# You can pass options to a polymorphic mapping - the arity for the block
|
|
2235
|
-
#
|
|
2413
|
+
# You can pass options to a polymorphic mapping - the arity for the block needs
|
|
2414
|
+
# to be two as the instance is passed as the first argument, e.g:
|
|
2236
2415
|
#
|
|
2237
|
-
#
|
|
2238
|
-
#
|
|
2239
|
-
#
|
|
2416
|
+
# resolve "Basket", anchor: "items" do |basket, options|
|
|
2417
|
+
# [:basket, options]
|
|
2418
|
+
# end
|
|
2240
2419
|
#
|
|
2241
|
-
# This generates the URL "/basket#items" because when the last item in an
|
|
2242
|
-
#
|
|
2243
|
-
#
|
|
2420
|
+
# This generates the URL "/basket#items" because when the last item in an array
|
|
2421
|
+
# passed to `polymorphic_url` is a hash then it's treated as options to the URL
|
|
2422
|
+
# helper that gets called.
|
|
2244
2423
|
#
|
|
2245
|
-
# NOTE: The
|
|
2246
|
-
#
|
|
2424
|
+
# NOTE: The `resolve` method can't be used inside of a scope block such as
|
|
2425
|
+
# `namespace` or `scope` and will raise an error if it detects that it is.
|
|
2247
2426
|
def resolve(*args, &block)
|
|
2248
2427
|
unless @scope.root?
|
|
2249
2428
|
raise RuntimeError, "The resolve method can't be used inside a routes scope block"
|
|
@@ -2268,9 +2447,9 @@ module ActionDispatch
|
|
|
2268
2447
|
|
|
2269
2448
|
attr_reader :parent, :scope_level
|
|
2270
2449
|
|
|
2271
|
-
def initialize(hash, parent =
|
|
2272
|
-
@hash = hash
|
|
2450
|
+
def initialize(hash, parent = ROOT, scope_level = nil)
|
|
2273
2451
|
@parent = parent
|
|
2452
|
+
@hash = parent ? parent.frame.merge(hash) : hash
|
|
2274
2453
|
@scope_level = scope_level
|
|
2275
2454
|
end
|
|
2276
2455
|
|
|
@@ -2283,7 +2462,7 @@ module ActionDispatch
|
|
|
2283
2462
|
end
|
|
2284
2463
|
|
|
2285
2464
|
def root?
|
|
2286
|
-
@parent
|
|
2465
|
+
@parent == ROOT
|
|
2287
2466
|
end
|
|
2288
2467
|
|
|
2289
2468
|
def resources?
|
|
@@ -2328,25 +2507,26 @@ module ActionDispatch
|
|
|
2328
2507
|
end
|
|
2329
2508
|
|
|
2330
2509
|
def [](key)
|
|
2331
|
-
|
|
2332
|
-
scope && scope.frame[key]
|
|
2510
|
+
frame[key]
|
|
2333
2511
|
end
|
|
2334
2512
|
|
|
2513
|
+
def frame; @hash; end
|
|
2514
|
+
|
|
2335
2515
|
include Enumerable
|
|
2336
2516
|
|
|
2337
2517
|
def each
|
|
2338
2518
|
node = self
|
|
2339
|
-
until node.equal?
|
|
2519
|
+
until node.equal? ROOT
|
|
2340
2520
|
yield node
|
|
2341
2521
|
node = node.parent
|
|
2342
2522
|
end
|
|
2343
2523
|
end
|
|
2344
2524
|
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
NULL = Scope.new(nil, nil)
|
|
2525
|
+
ROOT = Scope.new({}, nil)
|
|
2348
2526
|
end
|
|
2349
2527
|
|
|
2528
|
+
DEFAULT = Object.new # :nodoc:
|
|
2529
|
+
|
|
2350
2530
|
def initialize(set) # :nodoc:
|
|
2351
2531
|
@set = set
|
|
2352
2532
|
@draw_paths = set.draw_paths
|