actionpack 7.1.3 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +121 -465
- data/README.rdoc +1 -1
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +93 -100
- data/lib/abstract_controller/caching/fragments.rb +50 -53
- data/lib/abstract_controller/caching.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +66 -64
- 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 +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +13 -12
- data/lib/abstract_controller/translation.rb +15 -7
- 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 +75 -72
- data/lib/action_controller/base.rb +198 -126
- data/lib/action_controller/caching.rb +15 -12
- data/lib/action_controller/deprecator.rb +2 -0
- data/lib/action_controller/form_builder.rb +20 -17
- data/lib/action_controller/log_subscriber.rb +3 -1
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +187 -174
- 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 +64 -55
- 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 +11 -9
- data/lib/action_controller/metal/flash.rb +12 -10
- data/lib/action_controller/metal/head.rb +12 -10
- data/lib/action_controller/metal/helpers.rb +63 -55
- data/lib/action_controller/metal/http_authentication.rb +211 -206
- data/lib/action_controller/metal/implicit_render.rb +17 -15
- data/lib/action_controller/metal/instrumentation.rb +15 -12
- data/lib/action_controller/metal/live.rb +117 -108
- 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 +13 -12
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +112 -85
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +104 -76
- data/lib/action_controller/metal/request_forgery_protection.rb +165 -134
- data/lib/action_controller/metal/rescue.rb +11 -9
- data/lib/action_controller/metal/streaming.rb +138 -136
- data/lib/action_controller/metal/strong_parameters.rb +525 -483
- 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 +3 -0
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +41 -36
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +148 -129
- data/lib/action_controller.rb +10 -3
- data/lib/action_dispatch/constants.rb +8 -0
- data/lib/action_dispatch/deprecator.rb +2 -0
- data/lib/action_dispatch/http/cache.rb +27 -26
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +61 -42
- data/lib/action_dispatch/http/filter_parameters.rb +18 -9
- 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 +35 -41
- data/lib/action_dispatch/http/mime_type.rb +31 -24
- data/lib/action_dispatch/http/mime_types.rb +2 -0
- data/lib/action_dispatch/http/parameters.rb +11 -9
- data/lib/action_dispatch/http/permissions_policy.rb +20 -44
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +96 -76
- data/lib/action_dispatch/http/response.rb +88 -62
- data/lib/action_dispatch/http/upload.rb +18 -16
- data/lib/action_dispatch/http/url.rb +77 -75
- 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 +2 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +14 -12
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +6 -5
- data/lib/action_dispatch/journey/parser.rb +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +2 -0
- data/lib/action_dispatch/journey/path/pattern.rb +4 -1
- data/lib/action_dispatch/journey/route.rb +9 -7
- data/lib/action_dispatch/journey/router/utils.rb +16 -15
- data/lib/action_dispatch/journey/router.rb +4 -2
- data/lib/action_dispatch/journey/routes.rb +4 -2
- data/lib/action_dispatch/journey/scanner.rb +4 -2
- data/lib/action_dispatch/journey/visitors.rb +2 -0
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +2 -0
- 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 +119 -104
- data/lib/action_dispatch/middleware/debug_exceptions.rb +16 -6
- data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
- data/lib/action_dispatch/middleware/debug_view.rb +2 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +9 -14
- data/lib/action_dispatch/middleware/executor.rb +13 -2
- 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 +13 -7
- data/lib/action_dispatch/middleware/reloader.rb +5 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +76 -72
- data/lib/action_dispatch/middleware/request_id.rb +14 -9
- 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 +13 -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 +31 -21
- data/lib/action_dispatch/middleware/ssl.rb +43 -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/_source.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
- data/lib/action_dispatch/railtie.rb +2 -4
- data/lib/action_dispatch/request/session.rb +23 -21
- data/lib/action_dispatch/request/utils.rb +2 -0
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +6 -4
- data/lib/action_dispatch/routing/mapper.rb +673 -637
- data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
- data/lib/action_dispatch/routing/redirection.rb +37 -32
- data/lib/action_dispatch/routing/route_set.rb +60 -46
- data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
- data/lib/action_dispatch/routing/url_for.rb +130 -125
- data/lib/action_dispatch/routing.rb +150 -148
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +10 -3
- data/lib/action_dispatch/system_testing/driver.rb +3 -1
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
- 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 +26 -23
- data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
- data/lib/action_dispatch/testing/assertions.rb +2 -0
- data/lib/action_dispatch/testing/integration.rb +222 -222
- 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 +25 -27
- data/lib/action_pack/gem_version.rb +4 -2
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +17 -16
- metadata +50 -16
|
@@ -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"
|
|
@@ -10,10 +12,19 @@ require "action_dispatch/routing/endpoint"
|
|
|
10
12
|
module ActionDispatch
|
|
11
13
|
module Routing
|
|
12
14
|
class Mapper
|
|
15
|
+
class BacktraceCleaner < ActiveSupport::BacktraceCleaner # :nodoc:
|
|
16
|
+
def initialize
|
|
17
|
+
super
|
|
18
|
+
remove_silencers!
|
|
19
|
+
add_core_silencer
|
|
20
|
+
add_stdlib_silencer
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
13
24
|
URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
|
|
14
25
|
|
|
15
26
|
cattr_accessor :route_source_locations, instance_accessor: false, default: false
|
|
16
|
-
cattr_accessor :backtrace_cleaner, instance_accessor: false, default:
|
|
27
|
+
cattr_accessor :backtrace_cleaner, instance_accessor: false, default: BacktraceCleaner.new
|
|
17
28
|
|
|
18
29
|
class Constraints < Routing::Endpoint # :nodoc:
|
|
19
30
|
attr_reader :app, :constraints
|
|
@@ -22,10 +33,10 @@ module ActionDispatch
|
|
|
22
33
|
CALL = ->(app, req) { app.call req.env }
|
|
23
34
|
|
|
24
35
|
def initialize(app, constraints, strategy)
|
|
25
|
-
# Unwrap Constraints objects. I don't actually think it's possible
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
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.
|
|
29
40
|
if app.is_a?(self.class)
|
|
30
41
|
constraints += app.constraints
|
|
31
42
|
app = app.app
|
|
@@ -210,7 +221,7 @@ module ActionDispatch
|
|
|
210
221
|
# Add a default constraint for :controller path segments that matches namespaced
|
|
211
222
|
# controllers with default routes like :controller/:action/:id(.:format), e.g:
|
|
212
223
|
# GET /admin/products/show/1
|
|
213
|
-
#
|
|
224
|
+
# # > { controller: 'admin/products', action: 'show', id: '1' }
|
|
214
225
|
options[:controller] ||= /.+?/
|
|
215
226
|
end
|
|
216
227
|
|
|
@@ -220,12 +231,17 @@ module ActionDispatch
|
|
|
220
231
|
if to.nil?
|
|
221
232
|
controller = default_controller
|
|
222
233
|
action = default_action
|
|
223
|
-
elsif to.is_a?(String)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
234
|
+
elsif to.is_a?(String)
|
|
235
|
+
if to.include?("#")
|
|
236
|
+
to_endpoint = to.split("#").map!(&:-@)
|
|
237
|
+
controller = to_endpoint[0]
|
|
238
|
+
action = to_endpoint[1]
|
|
239
|
+
else
|
|
240
|
+
controller = default_controller
|
|
241
|
+
action = to
|
|
242
|
+
end
|
|
227
243
|
else
|
|
228
|
-
raise ArgumentError, ":to must respond to `action` or `call`, or it must be a String that includes '#'"
|
|
244
|
+
raise ArgumentError, ":to must respond to `action` or `call`, or it must be a String that includes '#', or the controller should be implicit"
|
|
229
245
|
end
|
|
230
246
|
|
|
231
247
|
controller = add_controller_module(controller, modyoule)
|
|
@@ -359,32 +375,53 @@ module ActionDispatch
|
|
|
359
375
|
Routing::RouteSet::Dispatcher.new raise_on_name_error
|
|
360
376
|
end
|
|
361
377
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
378
|
+
if Thread.respond_to?(:each_caller_location)
|
|
379
|
+
def route_source_location
|
|
380
|
+
if Mapper.route_source_locations
|
|
381
|
+
action_dispatch_dir = File.expand_path("..", __dir__)
|
|
382
|
+
Thread.each_caller_location do |location|
|
|
383
|
+
next if location.path.start_with?(action_dispatch_dir)
|
|
384
|
+
|
|
385
|
+
cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
|
|
386
|
+
next if cleaned_path.nil?
|
|
387
|
+
|
|
388
|
+
return "#{cleaned_path}:#{location.lineno}"
|
|
389
|
+
end
|
|
390
|
+
nil
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
else
|
|
394
|
+
def route_source_location
|
|
395
|
+
if Mapper.route_source_locations
|
|
396
|
+
action_dispatch_dir = File.expand_path("..", __dir__)
|
|
397
|
+
caller_locations.each do |location|
|
|
398
|
+
next if location.path.start_with?(action_dispatch_dir)
|
|
399
|
+
|
|
400
|
+
cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
|
|
401
|
+
next if cleaned_path.nil?
|
|
402
|
+
|
|
403
|
+
return "#{cleaned_path}:#{location.lineno}"
|
|
404
|
+
end
|
|
405
|
+
nil
|
|
406
|
+
end
|
|
368
407
|
end
|
|
369
408
|
end
|
|
370
409
|
end
|
|
371
410
|
|
|
372
|
-
# Invokes Journey::Router::Utils.normalize_path, then ensures that
|
|
373
|
-
#
|
|
374
|
-
#
|
|
411
|
+
# Invokes Journey::Router::Utils.normalize_path, then ensures that /(:locale)
|
|
412
|
+
# becomes (/:locale). Except for root cases, where the former is the correct
|
|
413
|
+
# one.
|
|
375
414
|
def self.normalize_path(path)
|
|
376
415
|
path = Journey::Router::Utils.normalize_path(path)
|
|
377
416
|
|
|
378
417
|
# the path for a root URL at this point can be something like
|
|
379
418
|
# "/(/:locale)(/:platform)/(:browser)", and we would want
|
|
380
|
-
# "/(:locale)(/:platform)(/:browser)"
|
|
381
|
-
|
|
382
|
-
# reverse "/(", "/((" etc to "(/", "((/" etc
|
|
419
|
+
# "/(:locale)(/:platform)(/:browser)" reverse "/(", "/((" etc to "(/", "((/" etc
|
|
383
420
|
path.gsub!(%r{/(\(+)/?}, '\1/')
|
|
384
|
-
# if a path is all optional segments, change the leading "(/" back to
|
|
385
|
-
#
|
|
386
|
-
#
|
|
387
|
-
#
|
|
421
|
+
# if a path is all optional segments, change the leading "(/" back to "/(" so it
|
|
422
|
+
# evaluates to "/" when interpreted with no options. Unless, however, at least
|
|
423
|
+
# one secondary segment consists of a static part, ex.
|
|
424
|
+
# "(/:locale)(/pages/:page)"
|
|
388
425
|
path.sub!(%r{^(\(+)/}, '/\1') if %r{^(\(+[^)]+\))(\(+/:[^)]+\))*$}.match?(path)
|
|
389
426
|
path
|
|
390
427
|
end
|
|
@@ -396,206 +433,202 @@ module ActionDispatch
|
|
|
396
433
|
module Base
|
|
397
434
|
# Matches a URL pattern to one or more routes.
|
|
398
435
|
#
|
|
399
|
-
# You should not use the
|
|
400
|
-
#
|
|
436
|
+
# You should not use the `match` method in your router without specifying an
|
|
437
|
+
# HTTP method.
|
|
401
438
|
#
|
|
402
439
|
# If you want to expose your action to both GET and POST, use:
|
|
403
440
|
#
|
|
404
|
-
#
|
|
405
|
-
#
|
|
441
|
+
# # sets :controller, :action, and :id in params
|
|
442
|
+
# match ':controller/:action/:id', via: [:get, :post]
|
|
406
443
|
#
|
|
407
|
-
# Note that
|
|
408
|
-
#
|
|
444
|
+
# Note that `:controller`, `:action`, and `:id` are interpreted as URL query
|
|
445
|
+
# parameters and thus available through `params` in an action.
|
|
409
446
|
#
|
|
410
|
-
# If you want to expose your action to GET, use
|
|
447
|
+
# If you want to expose your action to GET, use `get` in the router:
|
|
411
448
|
#
|
|
412
449
|
# Instead of:
|
|
413
450
|
#
|
|
414
|
-
#
|
|
451
|
+
# match ":controller/:action/:id"
|
|
415
452
|
#
|
|
416
453
|
# Do:
|
|
417
454
|
#
|
|
418
|
-
#
|
|
455
|
+
# get ":controller/:action/:id"
|
|
419
456
|
#
|
|
420
|
-
# Two of these symbols are special,
|
|
421
|
-
#
|
|
422
|
-
#
|
|
457
|
+
# Two of these symbols are special, `:controller` maps to the controller and
|
|
458
|
+
# `:action` to the controller's action. A pattern can also map wildcard segments
|
|
459
|
+
# (globs) to params:
|
|
423
460
|
#
|
|
424
|
-
#
|
|
461
|
+
# get 'songs/*category/:title', to: 'songs#show'
|
|
425
462
|
#
|
|
426
|
-
#
|
|
427
|
-
#
|
|
428
|
-
#
|
|
463
|
+
# # 'songs/rock/classic/stairway-to-heaven' sets
|
|
464
|
+
# # params[:category] = 'rock/classic'
|
|
465
|
+
# # params[:title] = 'stairway-to-heaven'
|
|
429
466
|
#
|
|
430
|
-
# To match a wildcard parameter, it must have a name assigned to it.
|
|
431
|
-
#
|
|
432
|
-
# can't be parsed.
|
|
467
|
+
# To match a wildcard parameter, it must have a name assigned to it. Without a
|
|
468
|
+
# variable name to attach the glob parameter to, the route can't be parsed.
|
|
433
469
|
#
|
|
434
|
-
# When a pattern points to an internal route, the route's
|
|
435
|
-
#
|
|
470
|
+
# When a pattern points to an internal route, the route's `:action` and
|
|
471
|
+
# `:controller` should be set in options or hash shorthand. Examples:
|
|
436
472
|
#
|
|
437
|
-
#
|
|
438
|
-
#
|
|
439
|
-
#
|
|
473
|
+
# match 'photos/:id' => 'photos#show', via: :get
|
|
474
|
+
# match 'photos/:id', to: 'photos#show', via: :get
|
|
475
|
+
# match 'photos/:id', controller: 'photos', action: 'show', via: :get
|
|
440
476
|
#
|
|
441
|
-
# A pattern can also point to a
|
|
442
|
-
#
|
|
477
|
+
# A pattern can also point to a `Rack` endpoint i.e. anything that responds to
|
|
478
|
+
# `call`:
|
|
443
479
|
#
|
|
444
|
-
#
|
|
445
|
-
#
|
|
446
|
-
#
|
|
447
|
-
#
|
|
480
|
+
# match 'photos/:id', to: -> (hash) { [200, {}, ["Coming soon"]] }, via: :get
|
|
481
|
+
# match 'photos/:id', to: PhotoRackApp, via: :get
|
|
482
|
+
# # Yes, controller actions are just rack endpoints
|
|
483
|
+
# match 'photos/:id', to: PhotosController.action(:show), via: :get
|
|
448
484
|
#
|
|
449
485
|
# Because requesting various HTTP verbs with a single action has security
|
|
450
|
-
# implications, you must either specify the actions in
|
|
451
|
-
#
|
|
452
|
-
# instead +match+
|
|
486
|
+
# implications, you must either specify the actions in the via options or use
|
|
487
|
+
# one of the [HttpHelpers](rdoc-ref:HttpHelpers) instead `match`
|
|
453
488
|
#
|
|
454
|
-
#
|
|
489
|
+
# ### Options
|
|
455
490
|
#
|
|
456
491
|
# Any options not seen here are passed on as params with the URL.
|
|
457
492
|
#
|
|
458
|
-
#
|
|
459
|
-
# The route's controller.
|
|
493
|
+
# :controller
|
|
494
|
+
# : The route's controller.
|
|
460
495
|
#
|
|
461
|
-
#
|
|
462
|
-
# The route's action.
|
|
496
|
+
# :action
|
|
497
|
+
# : The route's action.
|
|
463
498
|
#
|
|
464
|
-
#
|
|
465
|
-
# Overrides the default resource identifier
|
|
466
|
-
#
|
|
467
|
-
#
|
|
468
|
-
# <tt>params[<:param>]</tt>.
|
|
469
|
-
# In your router:
|
|
499
|
+
# :param
|
|
500
|
+
# : Overrides the default resource identifier `:id` (name of the dynamic
|
|
501
|
+
# segment used to generate the routes). You can access that segment from
|
|
502
|
+
# your controller using `params[<:param>]`. In your router:
|
|
470
503
|
#
|
|
471
|
-
#
|
|
504
|
+
# resources :users, param: :name
|
|
472
505
|
#
|
|
473
|
-
#
|
|
506
|
+
# The `users` resource here will have the following routes generated for it:
|
|
474
507
|
#
|
|
475
|
-
#
|
|
476
|
-
#
|
|
477
|
-
#
|
|
478
|
-
#
|
|
479
|
-
#
|
|
480
|
-
#
|
|
481
|
-
#
|
|
508
|
+
# GET /users(.:format)
|
|
509
|
+
# POST /users(.:format)
|
|
510
|
+
# GET /users/new(.:format)
|
|
511
|
+
# GET /users/:name/edit(.:format)
|
|
512
|
+
# GET /users/:name(.:format)
|
|
513
|
+
# PATCH/PUT /users/:name(.:format)
|
|
514
|
+
# DELETE /users/:name(.:format)
|
|
482
515
|
#
|
|
483
|
-
#
|
|
484
|
-
#
|
|
516
|
+
# You can override `ActiveRecord::Base#to_param` of a related model to
|
|
517
|
+
# construct a URL:
|
|
485
518
|
#
|
|
486
|
-
#
|
|
487
|
-
#
|
|
488
|
-
#
|
|
489
|
-
#
|
|
490
|
-
#
|
|
519
|
+
# class User < ActiveRecord::Base
|
|
520
|
+
# def to_param
|
|
521
|
+
# name
|
|
522
|
+
# end
|
|
523
|
+
# end
|
|
491
524
|
#
|
|
492
|
-
#
|
|
493
|
-
#
|
|
525
|
+
# user = User.find_by(name: 'Phusion')
|
|
526
|
+
# user_path(user) # => "/users/Phusion"
|
|
494
527
|
#
|
|
495
|
-
#
|
|
496
|
-
# The path prefix for the routes.
|
|
528
|
+
# :path
|
|
529
|
+
# : The path prefix for the routes.
|
|
497
530
|
#
|
|
498
|
-
#
|
|
499
|
-
# The namespace for :controller.
|
|
531
|
+
# :module
|
|
532
|
+
# : The namespace for :controller.
|
|
500
533
|
#
|
|
501
|
-
#
|
|
502
|
-
#
|
|
534
|
+
# match 'path', to: 'c#a', module: 'sekret', controller: 'posts', via: :get
|
|
535
|
+
# # => Sekret::PostsController
|
|
503
536
|
#
|
|
504
|
-
#
|
|
537
|
+
# See `Scoping#namespace` for its scope equivalent.
|
|
505
538
|
#
|
|
506
|
-
#
|
|
507
|
-
# The name used to generate routing helpers.
|
|
539
|
+
# :as
|
|
540
|
+
# : The name used to generate routing helpers.
|
|
508
541
|
#
|
|
509
|
-
#
|
|
510
|
-
# Allowed HTTP verb(s) for route.
|
|
542
|
+
# :via
|
|
543
|
+
# : Allowed HTTP verb(s) for route.
|
|
511
544
|
#
|
|
512
|
-
#
|
|
513
|
-
#
|
|
514
|
-
#
|
|
545
|
+
# match 'path', to: 'c#a', via: :get
|
|
546
|
+
# match 'path', to: 'c#a', via: [:get, :post]
|
|
547
|
+
# match 'path', to: 'c#a', via: :all
|
|
515
548
|
#
|
|
516
|
-
#
|
|
517
|
-
# Points to a
|
|
518
|
-
#
|
|
549
|
+
# :to
|
|
550
|
+
# : Points to a `Rack` endpoint. Can be an object that responds to `call` or a
|
|
551
|
+
# string representing a controller's action.
|
|
519
552
|
#
|
|
520
|
-
#
|
|
521
|
-
#
|
|
522
|
-
#
|
|
553
|
+
# match 'path', to: 'controller#action', via: :get
|
|
554
|
+
# match 'path', to: -> (env) { [200, {}, ["Success!"]] }, via: :get
|
|
555
|
+
# match 'path', to: RackApp, via: :get
|
|
523
556
|
#
|
|
524
|
-
#
|
|
525
|
-
# Shorthand for wrapping routes in a specific RESTful context. Valid
|
|
526
|
-
#
|
|
527
|
-
#
|
|
557
|
+
# :on
|
|
558
|
+
# : Shorthand for wrapping routes in a specific RESTful context. Valid values
|
|
559
|
+
# are `:member`, `:collection`, and `:new`. Only use within `resource(s)`
|
|
560
|
+
# block. For example:
|
|
528
561
|
#
|
|
529
|
-
#
|
|
530
|
-
#
|
|
531
|
-
#
|
|
562
|
+
# resource :bar do
|
|
563
|
+
# match 'foo', to: 'c#a', on: :member, via: [:get, :post]
|
|
564
|
+
# end
|
|
532
565
|
#
|
|
533
|
-
#
|
|
566
|
+
# Is equivalent to:
|
|
534
567
|
#
|
|
535
|
-
#
|
|
536
|
-
#
|
|
537
|
-
#
|
|
538
|
-
#
|
|
539
|
-
#
|
|
568
|
+
# resource :bar do
|
|
569
|
+
# member do
|
|
570
|
+
# match 'foo', to: 'c#a', via: [:get, :post]
|
|
571
|
+
# end
|
|
572
|
+
# end
|
|
540
573
|
#
|
|
541
|
-
#
|
|
542
|
-
# Constrains parameters with a hash of regular expressions
|
|
543
|
-
#
|
|
544
|
-
#
|
|
545
|
-
#
|
|
574
|
+
# :constraints
|
|
575
|
+
# : Constrains parameters with a hash of regular expressions or an object that
|
|
576
|
+
# responds to `matches?`. In addition, constraints other than path can also
|
|
577
|
+
# be specified with any object that responds to `===` (e.g. String, Array,
|
|
578
|
+
# Range, etc.).
|
|
546
579
|
#
|
|
547
|
-
#
|
|
580
|
+
# match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }, via: :get
|
|
548
581
|
#
|
|
549
|
-
#
|
|
582
|
+
# match 'json_only', constraints: { format: 'json' }, via: :get
|
|
550
583
|
#
|
|
551
|
-
#
|
|
552
|
-
#
|
|
553
|
-
#
|
|
554
|
-
#
|
|
584
|
+
# class PermitList
|
|
585
|
+
# def matches?(request) request.remote_ip == '1.2.3.4' end
|
|
586
|
+
# end
|
|
587
|
+
# match 'path', to: 'c#a', constraints: PermitList.new, via: :get
|
|
555
588
|
#
|
|
556
|
-
#
|
|
557
|
-
# equivalent.
|
|
589
|
+
# See `Scoping#constraints` for more examples with its scope equivalent.
|
|
558
590
|
#
|
|
559
|
-
#
|
|
560
|
-
# Sets defaults for parameters
|
|
591
|
+
# :defaults
|
|
592
|
+
# : Sets defaults for parameters
|
|
561
593
|
#
|
|
562
|
-
#
|
|
563
|
-
#
|
|
594
|
+
# # Sets params[:format] to 'jpg' by default
|
|
595
|
+
# match 'path', to: 'c#a', defaults: { format: 'jpg' }, via: :get
|
|
564
596
|
#
|
|
565
|
-
#
|
|
597
|
+
# See `Scoping#defaults` for its scope equivalent.
|
|
566
598
|
#
|
|
567
|
-
#
|
|
568
|
-
# Boolean to anchor a
|
|
569
|
-
#
|
|
599
|
+
# :anchor
|
|
600
|
+
# : Boolean to anchor a `match` pattern. Default is true. When set to false,
|
|
601
|
+
# the pattern matches any request prefixed with the given path.
|
|
570
602
|
#
|
|
571
|
-
#
|
|
572
|
-
#
|
|
603
|
+
# # Matches any request starting with 'path'
|
|
604
|
+
# match 'path', to: 'c#a', anchor: false, via: :get
|
|
605
|
+
#
|
|
606
|
+
# :format
|
|
607
|
+
# : Allows you to specify the default value for optional `format` segment or
|
|
608
|
+
# disable it by supplying `false`.
|
|
573
609
|
#
|
|
574
|
-
# [:format]
|
|
575
|
-
# Allows you to specify the default value for optional +format+
|
|
576
|
-
# segment or disable it by supplying +false+.
|
|
577
610
|
def match(path, options = nil)
|
|
578
611
|
end
|
|
579
612
|
|
|
580
613
|
# Mount a Rack-based application to be used within the application.
|
|
581
614
|
#
|
|
582
|
-
#
|
|
615
|
+
# mount SomeRackApp, at: "some_route"
|
|
583
616
|
#
|
|
584
617
|
# Alternatively:
|
|
585
618
|
#
|
|
586
|
-
#
|
|
619
|
+
# mount(SomeRackApp => "some_route")
|
|
587
620
|
#
|
|
588
|
-
# For options, see
|
|
621
|
+
# For options, see `match`, as `mount` uses it internally.
|
|
589
622
|
#
|
|
590
|
-
# All mounted applications come with routing helpers to access them.
|
|
591
|
-
#
|
|
592
|
-
#
|
|
593
|
-
#
|
|
623
|
+
# All mounted applications come with routing helpers to access them. These are
|
|
624
|
+
# named after the class specified, so for the above example the helper is either
|
|
625
|
+
# `some_rack_app_path` or `some_rack_app_url`. To customize this helper's name,
|
|
626
|
+
# use the `:as` option:
|
|
594
627
|
#
|
|
595
|
-
#
|
|
628
|
+
# mount(SomeRackApp => "some_route", as: "exciting")
|
|
596
629
|
#
|
|
597
|
-
# This will generate the
|
|
598
|
-
#
|
|
630
|
+
# This will generate the `exciting_path` and `exciting_url` helpers which can be
|
|
631
|
+
# used to navigate to this mounted app.
|
|
599
632
|
def mount(app, options = nil)
|
|
600
633
|
if options
|
|
601
634
|
path = options.delete(:at)
|
|
@@ -669,7 +702,8 @@ module ActionDispatch
|
|
|
669
702
|
prefix_options.reverse_merge!(options[:_recall].slice(*_route.segment_keys))
|
|
670
703
|
end
|
|
671
704
|
|
|
672
|
-
# We must actually delete prefix segment keys to avoid passing them to next
|
|
705
|
+
# We must actually delete prefix segment keys to avoid passing them to next
|
|
706
|
+
# url_for.
|
|
673
707
|
_route.segment_keys.each { |k| options.delete(k) }
|
|
674
708
|
_url_helpers.public_send("#{name}_path", prefix_options)
|
|
675
709
|
end
|
|
@@ -680,7 +714,7 @@ module ActionDispatch
|
|
|
680
714
|
def optimize_routes_generation?; false; end
|
|
681
715
|
|
|
682
716
|
define_method :find_script_name do |options|
|
|
683
|
-
if options.key? :script_name
|
|
717
|
+
if options.key?(:script_name) && options[:script_name].present?
|
|
684
718
|
super(options)
|
|
685
719
|
else
|
|
686
720
|
script_namer.call(options)
|
|
@@ -691,50 +725,50 @@ module ActionDispatch
|
|
|
691
725
|
end
|
|
692
726
|
|
|
693
727
|
module HttpHelpers
|
|
694
|
-
# Define a route that only recognizes HTTP GET.
|
|
695
|
-
#
|
|
728
|
+
# Define a route that only recognizes HTTP GET. For supported arguments, see
|
|
729
|
+
# [match](rdoc-ref:Base#match)
|
|
696
730
|
#
|
|
697
|
-
#
|
|
731
|
+
# get 'bacon', to: 'food#bacon'
|
|
698
732
|
def get(*args, &block)
|
|
699
733
|
map_method(:get, args, &block)
|
|
700
734
|
end
|
|
701
735
|
|
|
702
|
-
# Define a route that only recognizes HTTP POST.
|
|
703
|
-
#
|
|
736
|
+
# Define a route that only recognizes HTTP POST. For supported arguments, see
|
|
737
|
+
# [match](rdoc-ref:Base#match)
|
|
704
738
|
#
|
|
705
|
-
#
|
|
739
|
+
# post 'bacon', to: 'food#bacon'
|
|
706
740
|
def post(*args, &block)
|
|
707
741
|
map_method(:post, args, &block)
|
|
708
742
|
end
|
|
709
743
|
|
|
710
|
-
# Define a route that only recognizes HTTP PATCH.
|
|
711
|
-
#
|
|
744
|
+
# Define a route that only recognizes HTTP PATCH. For supported arguments, see
|
|
745
|
+
# [match](rdoc-ref:Base#match)
|
|
712
746
|
#
|
|
713
|
-
#
|
|
747
|
+
# patch 'bacon', to: 'food#bacon'
|
|
714
748
|
def patch(*args, &block)
|
|
715
749
|
map_method(:patch, args, &block)
|
|
716
750
|
end
|
|
717
751
|
|
|
718
|
-
# Define a route that only recognizes HTTP PUT.
|
|
719
|
-
#
|
|
752
|
+
# Define a route that only recognizes HTTP PUT. For supported arguments, see
|
|
753
|
+
# [match](rdoc-ref:Base#match)
|
|
720
754
|
#
|
|
721
|
-
#
|
|
755
|
+
# put 'bacon', to: 'food#bacon'
|
|
722
756
|
def put(*args, &block)
|
|
723
757
|
map_method(:put, args, &block)
|
|
724
758
|
end
|
|
725
759
|
|
|
726
|
-
# Define a route that only recognizes HTTP DELETE.
|
|
727
|
-
#
|
|
760
|
+
# Define a route that only recognizes HTTP DELETE. For supported arguments, see
|
|
761
|
+
# [match](rdoc-ref:Base#match)
|
|
728
762
|
#
|
|
729
|
-
#
|
|
763
|
+
# delete 'broccoli', to: 'food#broccoli'
|
|
730
764
|
def delete(*args, &block)
|
|
731
765
|
map_method(:delete, args, &block)
|
|
732
766
|
end
|
|
733
767
|
|
|
734
|
-
# Define a route that only recognizes HTTP OPTIONS.
|
|
735
|
-
#
|
|
768
|
+
# Define a route that only recognizes HTTP OPTIONS. For supported arguments, see
|
|
769
|
+
# [match](rdoc-ref:Base#match)
|
|
736
770
|
#
|
|
737
|
-
#
|
|
771
|
+
# options 'carrots', to: 'food#carrots'
|
|
738
772
|
def options(*args, &block)
|
|
739
773
|
map_method(:options, args, &block)
|
|
740
774
|
end
|
|
@@ -748,91 +782,90 @@ module ActionDispatch
|
|
|
748
782
|
end
|
|
749
783
|
end
|
|
750
784
|
|
|
751
|
-
# You may wish to organize groups of controllers under a namespace.
|
|
752
|
-
#
|
|
753
|
-
#
|
|
754
|
-
#
|
|
755
|
-
#
|
|
785
|
+
# You may wish to organize groups of controllers under a namespace. Most
|
|
786
|
+
# commonly, you might group a number of administrative controllers under an
|
|
787
|
+
# `admin` namespace. You would place these controllers under the
|
|
788
|
+
# `app/controllers/admin` directory, and you can group them together in your
|
|
789
|
+
# router:
|
|
756
790
|
#
|
|
757
|
-
#
|
|
758
|
-
#
|
|
759
|
-
#
|
|
791
|
+
# namespace "admin" do
|
|
792
|
+
# resources :posts, :comments
|
|
793
|
+
# end
|
|
760
794
|
#
|
|
761
795
|
# This will create a number of routes for each of the posts and comments
|
|
762
|
-
# controller. For
|
|
796
|
+
# controller. For `Admin::PostsController`, Rails will create:
|
|
763
797
|
#
|
|
764
|
-
#
|
|
765
|
-
#
|
|
766
|
-
#
|
|
767
|
-
#
|
|
768
|
-
#
|
|
769
|
-
#
|
|
770
|
-
#
|
|
798
|
+
# GET /admin/posts
|
|
799
|
+
# GET /admin/posts/new
|
|
800
|
+
# POST /admin/posts
|
|
801
|
+
# GET /admin/posts/1
|
|
802
|
+
# GET /admin/posts/1/edit
|
|
803
|
+
# PATCH/PUT /admin/posts/1
|
|
804
|
+
# DELETE /admin/posts/1
|
|
771
805
|
#
|
|
772
806
|
# If you want to route /posts (without the prefix /admin) to
|
|
773
|
-
#
|
|
807
|
+
# `Admin::PostsController`, you could use
|
|
774
808
|
#
|
|
775
|
-
#
|
|
776
|
-
#
|
|
777
|
-
#
|
|
809
|
+
# scope module: "admin" do
|
|
810
|
+
# resources :posts
|
|
811
|
+
# end
|
|
778
812
|
#
|
|
779
813
|
# or, for a single case
|
|
780
814
|
#
|
|
781
|
-
#
|
|
815
|
+
# resources :posts, module: "admin"
|
|
782
816
|
#
|
|
783
|
-
# If you want to route /admin/posts to
|
|
784
|
-
#
|
|
817
|
+
# If you want to route /admin/posts to `PostsController` (without the `Admin::`
|
|
818
|
+
# module prefix), you could use
|
|
785
819
|
#
|
|
786
|
-
#
|
|
787
|
-
#
|
|
788
|
-
#
|
|
820
|
+
# scope "/admin" do
|
|
821
|
+
# resources :posts
|
|
822
|
+
# end
|
|
789
823
|
#
|
|
790
824
|
# or, for a single case
|
|
791
825
|
#
|
|
792
|
-
#
|
|
826
|
+
# resources :posts, path: "/admin/posts"
|
|
793
827
|
#
|
|
794
|
-
# In each of these cases, the named routes remain the same as if you did
|
|
795
|
-
#
|
|
796
|
-
# +PostsController+:
|
|
828
|
+
# In each of these cases, the named routes remain the same as if you did not use
|
|
829
|
+
# scope. In the last case, the following paths map to `PostsController`:
|
|
797
830
|
#
|
|
798
|
-
#
|
|
799
|
-
#
|
|
800
|
-
#
|
|
801
|
-
#
|
|
802
|
-
#
|
|
803
|
-
#
|
|
804
|
-
#
|
|
831
|
+
# GET /admin/posts
|
|
832
|
+
# GET /admin/posts/new
|
|
833
|
+
# POST /admin/posts
|
|
834
|
+
# GET /admin/posts/1
|
|
835
|
+
# GET /admin/posts/1/edit
|
|
836
|
+
# PATCH/PUT /admin/posts/1
|
|
837
|
+
# DELETE /admin/posts/1
|
|
805
838
|
module Scoping
|
|
806
839
|
# Scopes a set of routes to the given default options.
|
|
807
840
|
#
|
|
808
841
|
# Take the following route definition as an example:
|
|
809
842
|
#
|
|
810
|
-
#
|
|
811
|
-
#
|
|
812
|
-
#
|
|
843
|
+
# scope path: ":account_id", as: "account" do
|
|
844
|
+
# resources :projects
|
|
845
|
+
# end
|
|
813
846
|
#
|
|
814
|
-
# This generates helpers such as
|
|
815
|
-
# The difference here being that the routes generated are like
|
|
816
|
-
# rather than /accounts/:account_id/projects.
|
|
847
|
+
# This generates helpers such as `account_projects_path`, just like `resources`
|
|
848
|
+
# does. The difference here being that the routes generated are like
|
|
849
|
+
# /:account_id/projects, rather than /accounts/:account_id/projects.
|
|
817
850
|
#
|
|
818
|
-
#
|
|
851
|
+
# ### Options
|
|
819
852
|
#
|
|
820
|
-
# Takes same options as
|
|
853
|
+
# Takes same options as `Base#match` and `Resources#resources`.
|
|
821
854
|
#
|
|
822
|
-
#
|
|
823
|
-
#
|
|
824
|
-
#
|
|
825
|
-
#
|
|
855
|
+
# # route /posts (without the prefix /admin) to Admin::PostsController
|
|
856
|
+
# scope module: "admin" do
|
|
857
|
+
# resources :posts
|
|
858
|
+
# end
|
|
826
859
|
#
|
|
827
|
-
#
|
|
828
|
-
#
|
|
829
|
-
#
|
|
830
|
-
#
|
|
860
|
+
# # prefix the posts resource's requests with '/admin'
|
|
861
|
+
# scope path: "/admin" do
|
|
862
|
+
# resources :posts
|
|
863
|
+
# end
|
|
831
864
|
#
|
|
832
|
-
#
|
|
833
|
-
#
|
|
834
|
-
#
|
|
835
|
-
#
|
|
865
|
+
# # prefix the routing helper name: sekret_posts_path instead of posts_path
|
|
866
|
+
# scope as: "sekret" do
|
|
867
|
+
# resources :posts
|
|
868
|
+
# end
|
|
836
869
|
def scope(*args)
|
|
837
870
|
options = args.extract_options!.dup
|
|
838
871
|
scope = {}
|
|
@@ -889,9 +922,9 @@ module ActionDispatch
|
|
|
889
922
|
|
|
890
923
|
# Scopes routes to a specific controller
|
|
891
924
|
#
|
|
892
|
-
#
|
|
893
|
-
#
|
|
894
|
-
#
|
|
925
|
+
# controller "food" do
|
|
926
|
+
# match "bacon", action: :bacon, via: :get
|
|
927
|
+
# end
|
|
895
928
|
def controller(controller)
|
|
896
929
|
@scope = @scope.new(controller: controller)
|
|
897
930
|
yield
|
|
@@ -901,42 +934,42 @@ module ActionDispatch
|
|
|
901
934
|
|
|
902
935
|
# Scopes routes to a specific namespace. For example:
|
|
903
936
|
#
|
|
904
|
-
#
|
|
905
|
-
#
|
|
906
|
-
#
|
|
937
|
+
# namespace :admin do
|
|
938
|
+
# resources :posts
|
|
939
|
+
# end
|
|
907
940
|
#
|
|
908
941
|
# This generates the following routes:
|
|
909
942
|
#
|
|
910
|
-
#
|
|
911
|
-
#
|
|
912
|
-
#
|
|
913
|
-
#
|
|
914
|
-
#
|
|
915
|
-
#
|
|
916
|
-
#
|
|
943
|
+
# admin_posts GET /admin/posts(.:format) admin/posts#index
|
|
944
|
+
# admin_posts POST /admin/posts(.:format) admin/posts#create
|
|
945
|
+
# new_admin_post GET /admin/posts/new(.:format) admin/posts#new
|
|
946
|
+
# edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit
|
|
947
|
+
# admin_post GET /admin/posts/:id(.:format) admin/posts#show
|
|
948
|
+
# admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update
|
|
949
|
+
# admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy
|
|
917
950
|
#
|
|
918
|
-
#
|
|
951
|
+
# ### Options
|
|
919
952
|
#
|
|
920
|
-
# The
|
|
921
|
-
#
|
|
953
|
+
# The `:path`, `:as`, `:module`, `:shallow_path`, and `:shallow_prefix` options
|
|
954
|
+
# all default to the name of the namespace.
|
|
922
955
|
#
|
|
923
|
-
# For options, see
|
|
924
|
-
#
|
|
956
|
+
# For options, see `Base#match`. For `:shallow_path` option, see
|
|
957
|
+
# `Resources#resources`.
|
|
925
958
|
#
|
|
926
|
-
#
|
|
927
|
-
#
|
|
928
|
-
#
|
|
929
|
-
#
|
|
959
|
+
# # accessible through /sekret/posts rather than /admin/posts
|
|
960
|
+
# namespace :admin, path: "sekret" do
|
|
961
|
+
# resources :posts
|
|
962
|
+
# end
|
|
930
963
|
#
|
|
931
|
-
#
|
|
932
|
-
#
|
|
933
|
-
#
|
|
934
|
-
#
|
|
964
|
+
# # maps to Sekret::PostsController rather than Admin::PostsController
|
|
965
|
+
# namespace :admin, module: "sekret" do
|
|
966
|
+
# resources :posts
|
|
967
|
+
# end
|
|
935
968
|
#
|
|
936
|
-
#
|
|
937
|
-
#
|
|
938
|
-
#
|
|
939
|
-
#
|
|
969
|
+
# # generates sekret_posts_path rather than admin_posts_path
|
|
970
|
+
# namespace :admin, as: "sekret" do
|
|
971
|
+
# resources :posts
|
|
972
|
+
# end
|
|
940
973
|
def namespace(path, options = {}, &block)
|
|
941
974
|
path = path.to_s
|
|
942
975
|
|
|
@@ -952,70 +985,75 @@ module ActionDispatch
|
|
|
952
985
|
end
|
|
953
986
|
end
|
|
954
987
|
|
|
955
|
-
#
|
|
956
|
-
# Allows you to constrain the nested routes based on a set of rules.
|
|
957
|
-
#
|
|
988
|
+
# ### Parameter Restriction
|
|
989
|
+
# Allows you to constrain the nested routes based on a set of rules. For
|
|
990
|
+
# instance, in order to change the routes to allow for a dot character in the
|
|
991
|
+
# `id` parameter:
|
|
958
992
|
#
|
|
959
|
-
#
|
|
960
|
-
#
|
|
961
|
-
#
|
|
993
|
+
# constraints(id: /\d+\.\d+/) do
|
|
994
|
+
# resources :posts
|
|
995
|
+
# end
|
|
962
996
|
#
|
|
963
|
-
# Now routes such as
|
|
964
|
-
# The
|
|
997
|
+
# Now routes such as `/posts/1` will no longer be valid, but `/posts/1.1` will
|
|
998
|
+
# be. The `id` parameter must match the constraint passed in for this example.
|
|
965
999
|
#
|
|
966
1000
|
# You may use this to also restrict other parameters:
|
|
967
1001
|
#
|
|
968
|
-
#
|
|
969
|
-
#
|
|
970
|
-
#
|
|
1002
|
+
# resources :posts do
|
|
1003
|
+
# constraints(post_id: /\d+\.\d+/) do
|
|
1004
|
+
# resources :comments
|
|
1005
|
+
# end
|
|
971
1006
|
# end
|
|
972
|
-
# end
|
|
973
1007
|
#
|
|
974
|
-
#
|
|
1008
|
+
# ### Restricting based on IP
|
|
975
1009
|
#
|
|
976
1010
|
# Routes can also be constrained to an IP or a certain range of IP addresses:
|
|
977
1011
|
#
|
|
978
|
-
#
|
|
979
|
-
#
|
|
980
|
-
#
|
|
1012
|
+
# constraints(ip: /192\.168\.\d+\.\d+/) do
|
|
1013
|
+
# resources :posts
|
|
1014
|
+
# end
|
|
981
1015
|
#
|
|
982
|
-
# Any user connecting from the 192.168.* range will be able to see this
|
|
983
|
-
# where as any user connecting outside of this range will be told
|
|
1016
|
+
# Any user connecting from the 192.168.* range will be able to see this
|
|
1017
|
+
# resource, where as any user connecting outside of this range will be told
|
|
1018
|
+
# there is no such route.
|
|
984
1019
|
#
|
|
985
|
-
#
|
|
1020
|
+
# ### Dynamic request matching
|
|
986
1021
|
#
|
|
987
1022
|
# Requests to routes can be constrained based on specific criteria:
|
|
988
1023
|
#
|
|
989
|
-
#
|
|
990
|
-
#
|
|
991
|
-
#
|
|
1024
|
+
# constraints(-> (req) { /iPhone/.match?(req.env["HTTP_USER_AGENT"]) }) do
|
|
1025
|
+
# resources :iphones
|
|
1026
|
+
# end
|
|
992
1027
|
#
|
|
993
|
-
# You are able to move this logic out into a class if it is too complex for
|
|
994
|
-
# This class must have a
|
|
995
|
-
# if the user should be given access to that route, or
|
|
1028
|
+
# You are able to move this logic out into a class if it is too complex for
|
|
1029
|
+
# routes. This class must have a `matches?` method defined on it which either
|
|
1030
|
+
# returns `true` if the user should be given access to that route, or `false` if
|
|
1031
|
+
# the user should not.
|
|
996
1032
|
#
|
|
997
|
-
#
|
|
998
|
-
#
|
|
999
|
-
#
|
|
1000
|
-
#
|
|
1001
|
-
#
|
|
1033
|
+
# class Iphone
|
|
1034
|
+
# def self.matches?(request)
|
|
1035
|
+
# /iPhone/.match?(request.env["HTTP_USER_AGENT"])
|
|
1036
|
+
# end
|
|
1037
|
+
# end
|
|
1002
1038
|
#
|
|
1003
|
-
# An expected place for this code would be
|
|
1039
|
+
# An expected place for this code would be `lib/constraints`.
|
|
1004
1040
|
#
|
|
1005
1041
|
# This class is then used like this:
|
|
1006
1042
|
#
|
|
1007
|
-
#
|
|
1008
|
-
#
|
|
1009
|
-
#
|
|
1043
|
+
# constraints(Iphone) do
|
|
1044
|
+
# resources :iphones
|
|
1045
|
+
# end
|
|
1010
1046
|
def constraints(constraints = {}, &block)
|
|
1011
1047
|
scope(constraints: constraints, &block)
|
|
1012
1048
|
end
|
|
1013
1049
|
|
|
1014
1050
|
# Allows you to set default parameters for a route, such as this:
|
|
1015
|
-
#
|
|
1016
|
-
#
|
|
1017
|
-
#
|
|
1018
|
-
#
|
|
1051
|
+
#
|
|
1052
|
+
# defaults id: 'home' do
|
|
1053
|
+
# match 'scoped_pages/(:id)', to: 'pages#show'
|
|
1054
|
+
# end
|
|
1055
|
+
#
|
|
1056
|
+
# Using this, the `:id` parameter here will default to 'home'.
|
|
1019
1057
|
def defaults(defaults = {})
|
|
1020
1058
|
@scope = @scope.new(defaults: merge_defaults_scope(@scope[:defaults], defaults))
|
|
1021
1059
|
yield
|
|
@@ -1091,48 +1129,46 @@ module ActionDispatch
|
|
|
1091
1129
|
end
|
|
1092
1130
|
end
|
|
1093
1131
|
|
|
1094
|
-
# Resource routing allows you to quickly declare all of the common routes
|
|
1095
|
-
#
|
|
1096
|
-
#
|
|
1097
|
-
#
|
|
1132
|
+
# Resource routing allows you to quickly declare all of the common routes for a
|
|
1133
|
+
# given resourceful controller. Instead of declaring separate routes for your
|
|
1134
|
+
# `index`, `show`, `new`, `edit`, `create`, `update`, and `destroy` actions, a
|
|
1135
|
+
# resourceful route declares them in a single line of code:
|
|
1098
1136
|
#
|
|
1099
|
-
#
|
|
1137
|
+
# resources :photos
|
|
1100
1138
|
#
|
|
1101
|
-
# Sometimes, you have a resource that clients always look up without
|
|
1102
|
-
#
|
|
1103
|
-
#
|
|
1104
|
-
#
|
|
1139
|
+
# Sometimes, you have a resource that clients always look up without referencing
|
|
1140
|
+
# an ID. A common example, /profile always shows the profile of the currently
|
|
1141
|
+
# logged in user. In this case, you can use a singular resource to map /profile
|
|
1142
|
+
# (rather than /profile/:id) to the show action.
|
|
1105
1143
|
#
|
|
1106
|
-
#
|
|
1144
|
+
# resource :profile
|
|
1107
1145
|
#
|
|
1108
|
-
# It's common to have resources that are logically children of other
|
|
1109
|
-
# resources:
|
|
1146
|
+
# It's common to have resources that are logically children of other resources:
|
|
1110
1147
|
#
|
|
1111
|
-
#
|
|
1112
|
-
#
|
|
1113
|
-
#
|
|
1148
|
+
# resources :magazines do
|
|
1149
|
+
# resources :ads
|
|
1150
|
+
# end
|
|
1114
1151
|
#
|
|
1115
1152
|
# You may wish to organize groups of controllers under a namespace. Most
|
|
1116
|
-
# commonly, you might group a number of administrative controllers under
|
|
1117
|
-
#
|
|
1118
|
-
#
|
|
1119
|
-
#
|
|
1120
|
-
#
|
|
1121
|
-
# namespace "admin" do
|
|
1122
|
-
# resources :posts, :comments
|
|
1123
|
-
# end
|
|
1153
|
+
# commonly, you might group a number of administrative controllers under an
|
|
1154
|
+
# `admin` namespace. You would place these controllers under the
|
|
1155
|
+
# `app/controllers/admin` directory, and you can group them together in your
|
|
1156
|
+
# router:
|
|
1124
1157
|
#
|
|
1125
|
-
#
|
|
1126
|
-
#
|
|
1127
|
-
#
|
|
1158
|
+
# namespace "admin" do
|
|
1159
|
+
# resources :posts, :comments
|
|
1160
|
+
# end
|
|
1128
1161
|
#
|
|
1129
|
-
#
|
|
1162
|
+
# By default the `:id` parameter doesn't accept dots. If you need to use dots as
|
|
1163
|
+
# part of the `:id` parameter add a constraint which overrides this restriction,
|
|
1164
|
+
# e.g:
|
|
1130
1165
|
#
|
|
1131
|
-
#
|
|
1166
|
+
# resources :articles, id: /[^\/]+/
|
|
1132
1167
|
#
|
|
1168
|
+
# This allows any character other than a slash as part of your `:id`.
|
|
1133
1169
|
module Resources
|
|
1134
|
-
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
|
|
1135
|
-
#
|
|
1170
|
+
# CANONICAL_ACTIONS holds all actions that does not need a prefix or a path
|
|
1171
|
+
# appended since they fit properly in their scope level.
|
|
1136
1172
|
VALID_ON_OPTIONS = [:new, :collection, :member]
|
|
1137
1173
|
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
|
|
1138
1174
|
CANONICAL_ACTIONS = %w(index create new show update destroy)
|
|
@@ -1195,8 +1231,8 @@ module ActionDispatch
|
|
|
1195
1231
|
|
|
1196
1232
|
alias :member_name :singular
|
|
1197
1233
|
|
|
1198
|
-
# Checks for uncountable plurals, and appends "_index" if the plural
|
|
1199
|
-
#
|
|
1234
|
+
# Checks for uncountable plurals, and appends "_index" if the plural and
|
|
1235
|
+
# singular form are the same.
|
|
1200
1236
|
def collection_name
|
|
1201
1237
|
singular == plural ? "#{plural}_index" : plural
|
|
1202
1238
|
end
|
|
@@ -1269,37 +1305,35 @@ module ActionDispatch
|
|
|
1269
1305
|
@scope[:path_names].merge!(options)
|
|
1270
1306
|
end
|
|
1271
1307
|
|
|
1272
|
-
# Sometimes, you have a resource that clients always look up without
|
|
1273
|
-
#
|
|
1274
|
-
#
|
|
1275
|
-
#
|
|
1276
|
-
# the show action:
|
|
1308
|
+
# Sometimes, you have a resource that clients always look up without referencing
|
|
1309
|
+
# an ID. A common example, /profile always shows the profile of the currently
|
|
1310
|
+
# logged in user. In this case, you can use a singular resource to map /profile
|
|
1311
|
+
# (rather than /profile/:id) to the show action:
|
|
1277
1312
|
#
|
|
1278
|
-
#
|
|
1313
|
+
# resource :profile
|
|
1279
1314
|
#
|
|
1280
|
-
# This creates six different routes in your application, all mapping to
|
|
1281
|
-
#
|
|
1282
|
-
# the plural):
|
|
1315
|
+
# This creates six different routes in your application, all mapping to the
|
|
1316
|
+
# `Profiles` controller (note that the controller is named after the plural):
|
|
1283
1317
|
#
|
|
1284
|
-
#
|
|
1285
|
-
#
|
|
1286
|
-
#
|
|
1287
|
-
#
|
|
1288
|
-
#
|
|
1289
|
-
#
|
|
1318
|
+
# GET /profile/new
|
|
1319
|
+
# GET /profile
|
|
1320
|
+
# GET /profile/edit
|
|
1321
|
+
# PATCH/PUT /profile
|
|
1322
|
+
# DELETE /profile
|
|
1323
|
+
# POST /profile
|
|
1290
1324
|
#
|
|
1291
|
-
# If you want instances of a model to work with this resource via
|
|
1292
|
-
#
|
|
1293
|
-
#
|
|
1325
|
+
# If you want instances of a model to work with this resource via record
|
|
1326
|
+
# identification (e.g. in `form_with` or `redirect_to`), you will need to call
|
|
1327
|
+
# [resolve](rdoc-ref:CustomUrls#resolve):
|
|
1294
1328
|
#
|
|
1295
|
-
#
|
|
1296
|
-
#
|
|
1329
|
+
# resource :profile
|
|
1330
|
+
# resolve('Profile') { [:profile] }
|
|
1297
1331
|
#
|
|
1298
|
-
#
|
|
1299
|
-
#
|
|
1332
|
+
# # Enables this to work with singular routes:
|
|
1333
|
+
# form_with(model: @profile) {}
|
|
1300
1334
|
#
|
|
1301
|
-
#
|
|
1302
|
-
# Takes same options as resources
|
|
1335
|
+
# ### Options
|
|
1336
|
+
# Takes same options as [resources](rdoc-ref:#resources)
|
|
1303
1337
|
def resource(*resources, &block)
|
|
1304
1338
|
options = resources.extract_options!.dup
|
|
1305
1339
|
|
|
@@ -1329,143 +1363,147 @@ module ActionDispatch
|
|
|
1329
1363
|
self
|
|
1330
1364
|
end
|
|
1331
1365
|
|
|
1332
|
-
# In
|
|
1333
|
-
# and
|
|
1334
|
-
#
|
|
1335
|
-
# routing file, such as
|
|
1366
|
+
# In Rails, a resourceful route provides a mapping between HTTP verbs and URLs
|
|
1367
|
+
# and controller actions. By convention, each action also maps to particular
|
|
1368
|
+
# CRUD operations in a database. A single entry in the routing file, such as
|
|
1336
1369
|
#
|
|
1337
|
-
#
|
|
1370
|
+
# resources :photos
|
|
1338
1371
|
#
|
|
1339
|
-
# creates seven different routes in your application, all mapping to
|
|
1340
|
-
#
|
|
1372
|
+
# creates seven different routes in your application, all mapping to the
|
|
1373
|
+
# `Photos` controller:
|
|
1341
1374
|
#
|
|
1342
|
-
#
|
|
1343
|
-
#
|
|
1344
|
-
#
|
|
1345
|
-
#
|
|
1346
|
-
#
|
|
1347
|
-
#
|
|
1348
|
-
#
|
|
1375
|
+
# GET /photos
|
|
1376
|
+
# GET /photos/new
|
|
1377
|
+
# POST /photos
|
|
1378
|
+
# GET /photos/:id
|
|
1379
|
+
# GET /photos/:id/edit
|
|
1380
|
+
# PATCH/PUT /photos/:id
|
|
1381
|
+
# DELETE /photos/:id
|
|
1349
1382
|
#
|
|
1350
1383
|
# Resources can also be nested infinitely by using this block syntax:
|
|
1351
1384
|
#
|
|
1352
|
-
#
|
|
1353
|
-
#
|
|
1354
|
-
#
|
|
1385
|
+
# resources :photos do
|
|
1386
|
+
# resources :comments
|
|
1387
|
+
# end
|
|
1355
1388
|
#
|
|
1356
1389
|
# This generates the following comments routes:
|
|
1357
1390
|
#
|
|
1358
|
-
#
|
|
1359
|
-
#
|
|
1360
|
-
#
|
|
1361
|
-
#
|
|
1362
|
-
#
|
|
1363
|
-
#
|
|
1364
|
-
#
|
|
1391
|
+
# GET /photos/:photo_id/comments
|
|
1392
|
+
# GET /photos/:photo_id/comments/new
|
|
1393
|
+
# POST /photos/:photo_id/comments
|
|
1394
|
+
# GET /photos/:photo_id/comments/:id
|
|
1395
|
+
# GET /photos/:photo_id/comments/:id/edit
|
|
1396
|
+
# PATCH/PUT /photos/:photo_id/comments/:id
|
|
1397
|
+
# DELETE /photos/:photo_id/comments/:id
|
|
1365
1398
|
#
|
|
1366
|
-
#
|
|
1367
|
-
# Takes same options as match
|
|
1399
|
+
# ### Options
|
|
1400
|
+
# Takes same options as [match](rdoc-ref:Base#match) as well as:
|
|
1368
1401
|
#
|
|
1369
|
-
#
|
|
1370
|
-
# Allows you to change the segment component of the
|
|
1371
|
-
#
|
|
1402
|
+
# :path_names
|
|
1403
|
+
# : Allows you to change the segment component of the `edit` and `new`
|
|
1404
|
+
# actions. Actions not specified are not changed.
|
|
1372
1405
|
#
|
|
1373
|
-
#
|
|
1406
|
+
# resources :posts, path_names: { new: "brand_new" }
|
|
1374
1407
|
#
|
|
1375
|
-
#
|
|
1408
|
+
# The above example will now change /posts/new to /posts/brand_new.
|
|
1376
1409
|
#
|
|
1377
|
-
#
|
|
1378
|
-
# Allows you to change the path prefix for the resource.
|
|
1410
|
+
# :path
|
|
1411
|
+
# : Allows you to change the path prefix for the resource.
|
|
1379
1412
|
#
|
|
1380
|
-
#
|
|
1413
|
+
# resources :posts, path: 'postings'
|
|
1381
1414
|
#
|
|
1382
|
-
#
|
|
1415
|
+
# The resource and all segments will now route to /postings instead of
|
|
1416
|
+
# /posts.
|
|
1383
1417
|
#
|
|
1384
|
-
#
|
|
1385
|
-
# Only generate routes for the given actions.
|
|
1418
|
+
# :only
|
|
1419
|
+
# : Only generate routes for the given actions.
|
|
1386
1420
|
#
|
|
1387
|
-
#
|
|
1388
|
-
#
|
|
1421
|
+
# resources :cows, only: :show
|
|
1422
|
+
# resources :cows, only: [:show, :index]
|
|
1389
1423
|
#
|
|
1390
|
-
#
|
|
1391
|
-
# Generate all routes except for the given actions.
|
|
1424
|
+
# :except
|
|
1425
|
+
# : Generate all routes except for the given actions.
|
|
1392
1426
|
#
|
|
1393
|
-
#
|
|
1394
|
-
#
|
|
1427
|
+
# resources :cows, except: :show
|
|
1428
|
+
# resources :cows, except: [:show, :index]
|
|
1395
1429
|
#
|
|
1396
|
-
#
|
|
1397
|
-
# Generates shallow routes for nested resource(s). When placed on a parent
|
|
1398
|
-
#
|
|
1430
|
+
# :shallow
|
|
1431
|
+
# : Generates shallow routes for nested resource(s). When placed on a parent
|
|
1432
|
+
# resource, generates shallow routes for all nested resources.
|
|
1399
1433
|
#
|
|
1400
|
-
#
|
|
1401
|
-
#
|
|
1402
|
-
#
|
|
1434
|
+
# resources :posts, shallow: true do
|
|
1435
|
+
# resources :comments
|
|
1436
|
+
# end
|
|
1403
1437
|
#
|
|
1404
|
-
#
|
|
1438
|
+
# Is the same as:
|
|
1405
1439
|
#
|
|
1406
|
-
#
|
|
1407
|
-
#
|
|
1408
|
-
#
|
|
1409
|
-
#
|
|
1440
|
+
# resources :posts do
|
|
1441
|
+
# resources :comments, except: [:show, :edit, :update, :destroy]
|
|
1442
|
+
# end
|
|
1443
|
+
# resources :comments, only: [:show, :edit, :update, :destroy]
|
|
1410
1444
|
#
|
|
1411
|
-
#
|
|
1412
|
-
#
|
|
1413
|
-
#
|
|
1445
|
+
# This allows URLs for resources that otherwise would be deeply nested such
|
|
1446
|
+
# as a comment on a blog post like `/posts/a-long-permalink/comments/1234`
|
|
1447
|
+
# to be shortened to just `/comments/1234`.
|
|
1414
1448
|
#
|
|
1415
|
-
#
|
|
1449
|
+
# Set `shallow: false` on a child resource to ignore a parent's shallow
|
|
1450
|
+
# parameter.
|
|
1416
1451
|
#
|
|
1417
|
-
#
|
|
1418
|
-
# Prefixes nested shallow routes with the specified path.
|
|
1452
|
+
# :shallow_path
|
|
1453
|
+
# : Prefixes nested shallow routes with the specified path.
|
|
1419
1454
|
#
|
|
1420
|
-
#
|
|
1421
|
-
#
|
|
1422
|
-
#
|
|
1423
|
-
#
|
|
1424
|
-
#
|
|
1455
|
+
# scope shallow_path: "sekret" do
|
|
1456
|
+
# resources :posts do
|
|
1457
|
+
# resources :comments, shallow: true
|
|
1458
|
+
# end
|
|
1459
|
+
# end
|
|
1425
1460
|
#
|
|
1426
|
-
#
|
|
1461
|
+
# The `comments` resource here will have the following routes generated for
|
|
1462
|
+
# it:
|
|
1427
1463
|
#
|
|
1428
|
-
#
|
|
1429
|
-
#
|
|
1430
|
-
#
|
|
1431
|
-
#
|
|
1432
|
-
#
|
|
1433
|
-
#
|
|
1434
|
-
#
|
|
1464
|
+
# post_comments GET /posts/:post_id/comments(.:format)
|
|
1465
|
+
# post_comments POST /posts/:post_id/comments(.:format)
|
|
1466
|
+
# new_post_comment GET /posts/:post_id/comments/new(.:format)
|
|
1467
|
+
# edit_comment GET /sekret/comments/:id/edit(.:format)
|
|
1468
|
+
# comment GET /sekret/comments/:id(.:format)
|
|
1469
|
+
# comment PATCH/PUT /sekret/comments/:id(.:format)
|
|
1470
|
+
# comment DELETE /sekret/comments/:id(.:format)
|
|
1435
1471
|
#
|
|
1436
|
-
#
|
|
1437
|
-
# Prefixes nested shallow route names with specified prefix.
|
|
1472
|
+
# :shallow_prefix
|
|
1473
|
+
# : Prefixes nested shallow route names with specified prefix.
|
|
1438
1474
|
#
|
|
1439
|
-
#
|
|
1440
|
-
#
|
|
1441
|
-
#
|
|
1442
|
-
#
|
|
1443
|
-
#
|
|
1475
|
+
# scope shallow_prefix: "sekret" do
|
|
1476
|
+
# resources :posts do
|
|
1477
|
+
# resources :comments, shallow: true
|
|
1478
|
+
# end
|
|
1479
|
+
# end
|
|
1480
|
+
#
|
|
1481
|
+
# The `comments` resource here will have the following routes generated for
|
|
1482
|
+
# it:
|
|
1444
1483
|
#
|
|
1445
|
-
#
|
|
1484
|
+
# post_comments GET /posts/:post_id/comments(.:format)
|
|
1485
|
+
# post_comments POST /posts/:post_id/comments(.:format)
|
|
1486
|
+
# new_post_comment GET /posts/:post_id/comments/new(.:format)
|
|
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)
|
|
1446
1491
|
#
|
|
1447
|
-
#
|
|
1448
|
-
#
|
|
1449
|
-
#
|
|
1450
|
-
# edit_sekret_comment GET /comments/:id/edit(.:format)
|
|
1451
|
-
# sekret_comment GET /comments/:id(.:format)
|
|
1452
|
-
# sekret_comment PATCH/PUT /comments/:id(.:format)
|
|
1453
|
-
# sekret_comment DELETE /comments/:id(.:format)
|
|
1492
|
+
# :format
|
|
1493
|
+
# : Allows you to specify the default value for optional `format` segment or
|
|
1494
|
+
# disable it by supplying `false`.
|
|
1454
1495
|
#
|
|
1455
|
-
#
|
|
1456
|
-
# Allows you to
|
|
1457
|
-
# segment or disable it by supplying +false+.
|
|
1496
|
+
# :param
|
|
1497
|
+
# : Allows you to override the default param name of `:id` in the URL.
|
|
1458
1498
|
#
|
|
1459
|
-
# [:param]
|
|
1460
|
-
# Allows you to override the default param name of +:id+ in the URL.
|
|
1461
1499
|
#
|
|
1462
|
-
#
|
|
1500
|
+
# ### Examples
|
|
1463
1501
|
#
|
|
1464
|
-
#
|
|
1465
|
-
#
|
|
1502
|
+
# # routes call Admin::PostsController
|
|
1503
|
+
# resources :posts, module: "admin"
|
|
1466
1504
|
#
|
|
1467
|
-
#
|
|
1468
|
-
#
|
|
1505
|
+
# # resource actions are at /admin/posts.
|
|
1506
|
+
# resources :posts, path: "admin/posts"
|
|
1469
1507
|
def resources(*resources, &block)
|
|
1470
1508
|
options = resources.extract_options!.dup
|
|
1471
1509
|
|
|
@@ -1498,16 +1536,15 @@ module ActionDispatch
|
|
|
1498
1536
|
|
|
1499
1537
|
# To add a route to the collection:
|
|
1500
1538
|
#
|
|
1501
|
-
#
|
|
1502
|
-
#
|
|
1503
|
-
#
|
|
1539
|
+
# resources :photos do
|
|
1540
|
+
# collection do
|
|
1541
|
+
# get 'search'
|
|
1542
|
+
# end
|
|
1504
1543
|
# end
|
|
1505
|
-
# end
|
|
1506
1544
|
#
|
|
1507
|
-
# This will enable
|
|
1508
|
-
#
|
|
1509
|
-
#
|
|
1510
|
-
# route helpers.
|
|
1545
|
+
# This will enable Rails to recognize paths such as `/photos/search` with GET,
|
|
1546
|
+
# and route to the search action of `PhotosController`. It will also create the
|
|
1547
|
+
# `search_photos_url` and `search_photos_path` route helpers.
|
|
1511
1548
|
def collection(&block)
|
|
1512
1549
|
unless resource_scope?
|
|
1513
1550
|
raise ArgumentError, "can't use collection outside resource(s) scope"
|
|
@@ -1520,15 +1557,15 @@ module ActionDispatch
|
|
|
1520
1557
|
|
|
1521
1558
|
# To add a member route, add a member block into the resource block:
|
|
1522
1559
|
#
|
|
1523
|
-
#
|
|
1524
|
-
#
|
|
1525
|
-
#
|
|
1560
|
+
# resources :photos do
|
|
1561
|
+
# member do
|
|
1562
|
+
# get 'preview'
|
|
1563
|
+
# end
|
|
1526
1564
|
# end
|
|
1527
|
-
# end
|
|
1528
1565
|
#
|
|
1529
|
-
# This will recognize
|
|
1530
|
-
#
|
|
1531
|
-
#
|
|
1566
|
+
# This will recognize `/photos/1/preview` with GET, and route to the preview
|
|
1567
|
+
# action of `PhotosController`. It will also create the `preview_photo_url` and
|
|
1568
|
+
# `preview_photo_path` helpers.
|
|
1532
1569
|
def member(&block)
|
|
1533
1570
|
unless resource_scope?
|
|
1534
1571
|
raise ArgumentError, "can't use member outside resource(s) scope"
|
|
@@ -1595,29 +1632,28 @@ module ActionDispatch
|
|
|
1595
1632
|
!parent_resource.singleton? && @scope[:shallow]
|
|
1596
1633
|
end
|
|
1597
1634
|
|
|
1598
|
-
# Loads another routes file with the given
|
|
1599
|
-
#
|
|
1600
|
-
#
|
|
1601
|
-
#
|
|
1602
|
-
#
|
|
1603
|
-
#
|
|
1604
|
-
#
|
|
1605
|
-
#
|
|
1606
|
-
#
|
|
1607
|
-
#
|
|
1608
|
-
#
|
|
1609
|
-
#
|
|
1610
|
-
#
|
|
1611
|
-
#
|
|
1612
|
-
#
|
|
1613
|
-
#
|
|
1614
|
-
#
|
|
1615
|
-
#
|
|
1616
|
-
#
|
|
1617
|
-
#
|
|
1618
|
-
#
|
|
1619
|
-
#
|
|
1620
|
-
# developers to have a single routes file.
|
|
1635
|
+
# Loads another routes file with the given `name` located inside the
|
|
1636
|
+
# `config/routes` directory. In that file, you can use the normal routing DSL,
|
|
1637
|
+
# but *do not* surround it with a `Rails.application.routes.draw` block.
|
|
1638
|
+
#
|
|
1639
|
+
# # config/routes.rb
|
|
1640
|
+
# Rails.application.routes.draw do
|
|
1641
|
+
# draw :admin # Loads `config/routes/admin.rb`
|
|
1642
|
+
# draw "third_party/some_gem" # Loads `config/routes/third_party/some_gem.rb`
|
|
1643
|
+
# end
|
|
1644
|
+
#
|
|
1645
|
+
# # config/routes/admin.rb
|
|
1646
|
+
# namespace :admin do
|
|
1647
|
+
# resources :accounts
|
|
1648
|
+
# end
|
|
1649
|
+
#
|
|
1650
|
+
# # config/routes/third_party/some_gem.rb
|
|
1651
|
+
# mount SomeGem::Engine, at: "/some_gem"
|
|
1652
|
+
#
|
|
1653
|
+
# **CAUTION:** Use this feature with care. Having multiple routes files can
|
|
1654
|
+
# negatively impact discoverability and readability. For most applications —
|
|
1655
|
+
# even those with a few hundred routes — it's easier for developers to have a
|
|
1656
|
+
# single routes file.
|
|
1621
1657
|
def draw(name)
|
|
1622
1658
|
path = @draw_paths.find do |_path|
|
|
1623
1659
|
File.exist? "#{_path}/#{name}.rb"
|
|
@@ -1634,12 +1670,12 @@ module ActionDispatch
|
|
|
1634
1670
|
instance_eval(File.read(route_path), route_path.to_s)
|
|
1635
1671
|
end
|
|
1636
1672
|
|
|
1637
|
-
# Matches a URL pattern to one or more routes.
|
|
1638
|
-
#
|
|
1673
|
+
# Matches a URL pattern to one or more routes. For more information, see
|
|
1674
|
+
# [match](rdoc-ref:Base#match).
|
|
1639
1675
|
#
|
|
1640
|
-
#
|
|
1641
|
-
#
|
|
1642
|
-
#
|
|
1676
|
+
# match 'path' => 'controller#action', via: :patch
|
|
1677
|
+
# match 'path', to: 'controller#action', via: :post
|
|
1678
|
+
# match 'path', 'otherpath', on: :member, via: :get
|
|
1643
1679
|
def match(path, *rest, &block)
|
|
1644
1680
|
if rest.empty? && Hash === path
|
|
1645
1681
|
options = path
|
|
@@ -1674,19 +1710,19 @@ module ActionDispatch
|
|
|
1674
1710
|
end
|
|
1675
1711
|
end
|
|
1676
1712
|
|
|
1677
|
-
# You can specify what
|
|
1713
|
+
# You can specify what Rails should route "/" to with the root method:
|
|
1678
1714
|
#
|
|
1679
|
-
#
|
|
1715
|
+
# root to: 'pages#main'
|
|
1680
1716
|
#
|
|
1681
|
-
# For options, see
|
|
1717
|
+
# For options, see `match`, as `root` uses it internally.
|
|
1682
1718
|
#
|
|
1683
1719
|
# You can also pass a string which will expand
|
|
1684
1720
|
#
|
|
1685
|
-
#
|
|
1721
|
+
# root 'pages#main'
|
|
1686
1722
|
#
|
|
1687
|
-
# You should put the root route at the top of
|
|
1688
|
-
#
|
|
1689
|
-
#
|
|
1723
|
+
# You should put the root route at the top of `config/routes.rb`, because this
|
|
1724
|
+
# means it will be matched first. As this is the most popular route of most
|
|
1725
|
+
# Rails applications, this is beneficial.
|
|
1690
1726
|
def root(path, options = {})
|
|
1691
1727
|
if path.is_a?(String)
|
|
1692
1728
|
options[:to] = path
|
|
@@ -1864,9 +1900,9 @@ module ActionDispatch
|
|
|
1864
1900
|
candidate = action_name.select(&:present?).join("_")
|
|
1865
1901
|
|
|
1866
1902
|
unless candidate.empty?
|
|
1867
|
-
# If a name was not explicitly given, we check if it is valid
|
|
1868
|
-
#
|
|
1869
|
-
#
|
|
1903
|
+
# If a name was not explicitly given, we check if it is valid and return nil in
|
|
1904
|
+
# case it isn't. Otherwise, we pass the invalid name forward so the underlying
|
|
1905
|
+
# router engine treats it and raises an exception.
|
|
1870
1906
|
if as.nil?
|
|
1871
1907
|
candidate unless !candidate.match?(/\A[_a-z]/i) || has_named_route?(candidate)
|
|
1872
1908
|
else
|
|
@@ -1989,7 +2025,7 @@ module ActionDispatch
|
|
|
1989
2025
|
name_for_action(options.delete(:as), action)
|
|
1990
2026
|
end
|
|
1991
2027
|
|
|
1992
|
-
path = Mapping.normalize_path
|
|
2028
|
+
path = Mapping.normalize_path RFC2396_PARSER.escape(path), formatted
|
|
1993
2029
|
ast = Journey::Parser.parse path
|
|
1994
2030
|
|
|
1995
2031
|
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
|
|
@@ -2002,83 +2038,81 @@ module ActionDispatch
|
|
|
2002
2038
|
end
|
|
2003
2039
|
end
|
|
2004
2040
|
|
|
2005
|
-
# Routing Concerns allow you to declare common routes that can be reused
|
|
2006
|
-
#
|
|
2041
|
+
# Routing Concerns allow you to declare common routes that can be reused inside
|
|
2042
|
+
# others resources and routes.
|
|
2007
2043
|
#
|
|
2008
|
-
#
|
|
2009
|
-
#
|
|
2010
|
-
#
|
|
2044
|
+
# concern :commentable do
|
|
2045
|
+
# resources :comments
|
|
2046
|
+
# end
|
|
2011
2047
|
#
|
|
2012
|
-
#
|
|
2013
|
-
#
|
|
2014
|
-
#
|
|
2048
|
+
# concern :image_attachable do
|
|
2049
|
+
# resources :images, only: :index
|
|
2050
|
+
# end
|
|
2015
2051
|
#
|
|
2016
2052
|
# These concerns are used in Resources routing:
|
|
2017
2053
|
#
|
|
2018
|
-
#
|
|
2054
|
+
# resources :messages, concerns: [:commentable, :image_attachable]
|
|
2019
2055
|
#
|
|
2020
2056
|
# or in a scope or namespace:
|
|
2021
2057
|
#
|
|
2022
|
-
#
|
|
2023
|
-
#
|
|
2024
|
-
#
|
|
2058
|
+
# namespace :posts do
|
|
2059
|
+
# concerns :commentable
|
|
2060
|
+
# end
|
|
2025
2061
|
module Concerns
|
|
2026
2062
|
# Define a routing concern using a name.
|
|
2027
2063
|
#
|
|
2028
|
-
# Concerns may be defined inline, using a block, or handled by
|
|
2029
|
-
#
|
|
2064
|
+
# Concerns may be defined inline, using a block, or handled by another object,
|
|
2065
|
+
# by passing that object as the second parameter.
|
|
2030
2066
|
#
|
|
2031
|
-
# The concern object, if supplied, should respond to
|
|
2032
|
-
#
|
|
2067
|
+
# The concern object, if supplied, should respond to `call`, which will receive
|
|
2068
|
+
# two parameters:
|
|
2033
2069
|
#
|
|
2034
|
-
#
|
|
2035
|
-
#
|
|
2070
|
+
# * The current mapper
|
|
2071
|
+
# * A hash of options which the concern object may use
|
|
2036
2072
|
#
|
|
2037
|
-
# Options may also be used by concerns defined in a block by accepting
|
|
2038
|
-
#
|
|
2039
|
-
#
|
|
2040
|
-
#
|
|
2073
|
+
# Options may also be used by concerns defined in a block by accepting a block
|
|
2074
|
+
# parameter. So, using a block, you might do something as simple as limit the
|
|
2075
|
+
# actions available on certain resources, passing standard resource options
|
|
2076
|
+
# through the concern:
|
|
2041
2077
|
#
|
|
2042
|
-
#
|
|
2043
|
-
#
|
|
2044
|
-
#
|
|
2078
|
+
# concern :commentable do |options|
|
|
2079
|
+
# resources :comments, options
|
|
2080
|
+
# end
|
|
2045
2081
|
#
|
|
2046
|
-
#
|
|
2047
|
-
#
|
|
2048
|
-
#
|
|
2049
|
-
#
|
|
2050
|
-
#
|
|
2082
|
+
# resources :posts, concerns: :commentable
|
|
2083
|
+
# resources :archived_posts do
|
|
2084
|
+
# # Don't allow comments on archived posts
|
|
2085
|
+
# concerns :commentable, only: [:index, :show]
|
|
2086
|
+
# end
|
|
2051
2087
|
#
|
|
2052
|
-
# Or, using a callable object, you might implement something more
|
|
2053
|
-
#
|
|
2054
|
-
# routes file.
|
|
2088
|
+
# Or, using a callable object, you might implement something more specific to
|
|
2089
|
+
# your application, which would be out of place in your routes file.
|
|
2055
2090
|
#
|
|
2056
|
-
#
|
|
2057
|
-
#
|
|
2058
|
-
#
|
|
2059
|
-
#
|
|
2060
|
-
#
|
|
2091
|
+
# # purchasable.rb
|
|
2092
|
+
# class Purchasable
|
|
2093
|
+
# def initialize(defaults = {})
|
|
2094
|
+
# @defaults = defaults
|
|
2095
|
+
# end
|
|
2061
2096
|
#
|
|
2062
|
-
#
|
|
2063
|
-
#
|
|
2064
|
-
#
|
|
2065
|
-
#
|
|
2066
|
-
#
|
|
2097
|
+
# def call(mapper, options = {})
|
|
2098
|
+
# options = @defaults.merge(options)
|
|
2099
|
+
# mapper.resources :purchases
|
|
2100
|
+
# mapper.resources :receipts
|
|
2101
|
+
# mapper.resources :returns if options[:returnable]
|
|
2102
|
+
# end
|
|
2067
2103
|
# end
|
|
2068
|
-
# end
|
|
2069
2104
|
#
|
|
2070
|
-
#
|
|
2071
|
-
#
|
|
2105
|
+
# # routes.rb
|
|
2106
|
+
# concern :purchasable, Purchasable.new(returnable: true)
|
|
2072
2107
|
#
|
|
2073
|
-
#
|
|
2074
|
-
#
|
|
2075
|
-
#
|
|
2076
|
-
#
|
|
2077
|
-
#
|
|
2108
|
+
# resources :toys, concerns: :purchasable
|
|
2109
|
+
# resources :electronics, concerns: :purchasable
|
|
2110
|
+
# resources :pets do
|
|
2111
|
+
# concerns :purchasable, returnable: false
|
|
2112
|
+
# end
|
|
2078
2113
|
#
|
|
2079
|
-
# Any routing helpers can be used inside a concern. If using a
|
|
2080
|
-
#
|
|
2081
|
-
# <tt>call</tt>.
|
|
2114
|
+
# Any routing helpers can be used inside a concern. If using a callable, they're
|
|
2115
|
+
# accessible from the Mapper that's passed to `call`.
|
|
2082
2116
|
def concern(name, callable = nil, &block)
|
|
2083
2117
|
callable ||= lambda { |mapper, options| mapper.instance_exec(options, &block) }
|
|
2084
2118
|
@concerns[name] = callable
|
|
@@ -2086,15 +2120,15 @@ module ActionDispatch
|
|
|
2086
2120
|
|
|
2087
2121
|
# Use the named concerns
|
|
2088
2122
|
#
|
|
2089
|
-
#
|
|
2090
|
-
#
|
|
2091
|
-
#
|
|
2123
|
+
# resources :posts do
|
|
2124
|
+
# concerns :commentable
|
|
2125
|
+
# end
|
|
2092
2126
|
#
|
|
2093
2127
|
# Concerns also work in any routes helper that you want to use:
|
|
2094
2128
|
#
|
|
2095
|
-
#
|
|
2096
|
-
#
|
|
2097
|
-
#
|
|
2129
|
+
# namespace :posts do
|
|
2130
|
+
# concerns :commentable
|
|
2131
|
+
# end
|
|
2098
2132
|
def concerns(*args)
|
|
2099
2133
|
options = args.extract_options!
|
|
2100
2134
|
args.flatten.each do |name|
|
|
@@ -2108,53 +2142,55 @@ module ActionDispatch
|
|
|
2108
2142
|
end
|
|
2109
2143
|
|
|
2110
2144
|
module CustomUrls
|
|
2111
|
-
# Define custom URL helpers that will be added to the application's
|
|
2112
|
-
#
|
|
2113
|
-
#
|
|
2145
|
+
# Define custom URL helpers that will be added to the application's routes. This
|
|
2146
|
+
# allows you to override and/or replace the default behavior of routing helpers,
|
|
2147
|
+
# e.g:
|
|
2114
2148
|
#
|
|
2115
|
-
#
|
|
2116
|
-
#
|
|
2117
|
-
#
|
|
2149
|
+
# direct :homepage do
|
|
2150
|
+
# "https://rubyonrails.org"
|
|
2151
|
+
# end
|
|
2118
2152
|
#
|
|
2119
|
-
#
|
|
2120
|
-
#
|
|
2121
|
-
#
|
|
2153
|
+
# direct :commentable do |model|
|
|
2154
|
+
# [ model, anchor: model.dom_id ]
|
|
2155
|
+
# end
|
|
2122
2156
|
#
|
|
2123
|
-
#
|
|
2124
|
-
#
|
|
2125
|
-
#
|
|
2157
|
+
# direct :main do
|
|
2158
|
+
# { controller: "pages", action: "index", subdomain: "www" }
|
|
2159
|
+
# end
|
|
2126
2160
|
#
|
|
2127
|
-
# The return value from the block passed to
|
|
2128
|
-
# arguments for
|
|
2129
|
-
#
|
|
2161
|
+
# The return value from the block passed to `direct` must be a valid set of
|
|
2162
|
+
# arguments for `url_for` which will actually build the URL string. This can be
|
|
2163
|
+
# one of the following:
|
|
2130
2164
|
#
|
|
2131
|
-
# *
|
|
2132
|
-
# *
|
|
2133
|
-
# *
|
|
2134
|
-
# *
|
|
2135
|
-
# *
|
|
2165
|
+
# * A string, which is treated as a generated URL
|
|
2166
|
+
# * A hash, e.g. `{ controller: "pages", action: "index" }`
|
|
2167
|
+
# * An array, which is passed to `polymorphic_url`
|
|
2168
|
+
# * An Active Model instance
|
|
2169
|
+
# * An Active Model class
|
|
2136
2170
|
#
|
|
2137
|
-
# NOTE: Other URL helpers can be called in the block but be careful not to invoke
|
|
2138
|
-
# your custom URL helper again otherwise it will result in a stack overflow error.
|
|
2139
2171
|
#
|
|
2140
|
-
#
|
|
2141
|
-
# your URL helper
|
|
2172
|
+
# NOTE: Other URL helpers can be called in the block but be careful not to
|
|
2173
|
+
# invoke your custom URL helper again otherwise it will result in a stack
|
|
2174
|
+
# overflow error.
|
|
2142
2175
|
#
|
|
2143
|
-
#
|
|
2144
|
-
#
|
|
2145
|
-
# end
|
|
2176
|
+
# You can also specify default options that will be passed through to your URL
|
|
2177
|
+
# helper definition, e.g:
|
|
2146
2178
|
#
|
|
2147
|
-
#
|
|
2148
|
-
#
|
|
2149
|
-
#
|
|
2179
|
+
# direct :browse, page: 1, size: 10 do |options|
|
|
2180
|
+
# [ :products, options.merge(params.permit(:page, :size).to_h.symbolize_keys) ]
|
|
2181
|
+
# end
|
|
2150
2182
|
#
|
|
2151
|
-
#
|
|
2183
|
+
# In this instance the `params` object comes from the context in which the block
|
|
2184
|
+
# is executed, e.g. generating a URL inside a controller action or a view. If
|
|
2185
|
+
# the block is executed where there isn't a `params` object such as this:
|
|
2152
2186
|
#
|
|
2153
|
-
#
|
|
2187
|
+
# Rails.application.routes.url_helpers.browse_path
|
|
2188
|
+
#
|
|
2189
|
+
# then it will raise a `NameError`. Because of this you need to be aware of the
|
|
2154
2190
|
# context in which you will use your custom URL helper when defining it.
|
|
2155
2191
|
#
|
|
2156
|
-
# NOTE: The
|
|
2157
|
-
#
|
|
2192
|
+
# NOTE: The `direct` method can't be used inside of a scope block such as
|
|
2193
|
+
# `namespace` or `scope` and will raise an error if it detects that it is.
|
|
2158
2194
|
def direct(name, options = {}, &block)
|
|
2159
2195
|
unless @scope.root?
|
|
2160
2196
|
raise RuntimeError, "The direct method can't be used inside a routes scope block"
|
|
@@ -2163,50 +2199,50 @@ module ActionDispatch
|
|
|
2163
2199
|
@set.add_url_helper(name, options, &block)
|
|
2164
2200
|
end
|
|
2165
2201
|
|
|
2166
|
-
# Define custom polymorphic mappings of models to URLs. This alters the
|
|
2167
|
-
#
|
|
2168
|
-
#
|
|
2202
|
+
# Define custom polymorphic mappings of models to URLs. This alters the behavior
|
|
2203
|
+
# of `polymorphic_url` and consequently the behavior of `link_to` and `form_for`
|
|
2204
|
+
# when passed a model instance, e.g:
|
|
2169
2205
|
#
|
|
2170
|
-
#
|
|
2206
|
+
# resource :basket
|
|
2171
2207
|
#
|
|
2172
|
-
#
|
|
2173
|
-
#
|
|
2174
|
-
#
|
|
2208
|
+
# resolve "Basket" do
|
|
2209
|
+
# [:basket]
|
|
2210
|
+
# end
|
|
2175
2211
|
#
|
|
2176
|
-
# This will now generate "/basket" when a
|
|
2177
|
-
#
|
|
2212
|
+
# This will now generate "/basket" when a `Basket` instance is passed to
|
|
2213
|
+
# `link_to` or `form_for` instead of the standard "/baskets/:id".
|
|
2178
2214
|
#
|
|
2179
|
-
# NOTE: This custom behavior only applies to simple polymorphic URLs where
|
|
2180
|
-
#
|
|
2215
|
+
# NOTE: This custom behavior only applies to simple polymorphic URLs where a
|
|
2216
|
+
# single model instance is passed and not more complicated forms, e.g:
|
|
2181
2217
|
#
|
|
2182
|
-
#
|
|
2183
|
-
#
|
|
2184
|
-
#
|
|
2185
|
-
#
|
|
2186
|
-
#
|
|
2218
|
+
# # config/routes.rb
|
|
2219
|
+
# resource :profile
|
|
2220
|
+
# namespace :admin do
|
|
2221
|
+
# resources :users
|
|
2222
|
+
# end
|
|
2187
2223
|
#
|
|
2188
|
-
#
|
|
2224
|
+
# resolve("User") { [:profile] }
|
|
2189
2225
|
#
|
|
2190
|
-
#
|
|
2191
|
-
#
|
|
2192
|
-
#
|
|
2226
|
+
# # app/views/application/_menu.html.erb
|
|
2227
|
+
# link_to "Profile", @current_user
|
|
2228
|
+
# link_to "Profile", [:admin, @current_user]
|
|
2193
2229
|
#
|
|
2194
|
-
# The first
|
|
2195
|
-
#
|
|
2230
|
+
# The first `link_to` will generate "/profile" but the second will generate the
|
|
2231
|
+
# standard polymorphic URL of "/admin/users/1".
|
|
2196
2232
|
#
|
|
2197
|
-
# You can pass options to a polymorphic mapping - the arity for the block
|
|
2198
|
-
#
|
|
2233
|
+
# You can pass options to a polymorphic mapping - the arity for the block needs
|
|
2234
|
+
# to be two as the instance is passed as the first argument, e.g:
|
|
2199
2235
|
#
|
|
2200
|
-
#
|
|
2201
|
-
#
|
|
2202
|
-
#
|
|
2236
|
+
# resolve "Basket", anchor: "items" do |basket, options|
|
|
2237
|
+
# [:basket, options]
|
|
2238
|
+
# end
|
|
2203
2239
|
#
|
|
2204
|
-
# This generates the URL "/basket#items" because when the last item in an
|
|
2205
|
-
#
|
|
2206
|
-
#
|
|
2240
|
+
# This generates the URL "/basket#items" because when the last item in an array
|
|
2241
|
+
# passed to `polymorphic_url` is a hash then it's treated as options to the URL
|
|
2242
|
+
# helper that gets called.
|
|
2207
2243
|
#
|
|
2208
|
-
# NOTE: The
|
|
2209
|
-
#
|
|
2244
|
+
# NOTE: The `resolve` method can't be used inside of a scope block such as
|
|
2245
|
+
# `namespace` or `scope` and will raise an error if it detects that it is.
|
|
2210
2246
|
def resolve(*args, &block)
|
|
2211
2247
|
unless @scope.root?
|
|
2212
2248
|
raise RuntimeError, "The resolve method can't be used inside a routes scope block"
|