actionpack 7.1.3 → 7.2.1.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -501
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +102 -98
- 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 +6 -6
- data/lib/abstract_controller/deprecator.rb +2 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +70 -85
- 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 +74 -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 +188 -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 +210 -205
- 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 +113 -107
- 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 +108 -82
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +103 -75
- data/lib/action_controller/metal/request_forgery_protection.rb +162 -133
- 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 -480
- 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 +42 -36
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +146 -126
- data/lib/action_controller.rb +10 -3
- data/lib/action_dispatch/constants.rb +2 -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 +44 -38
- 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 +30 -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 +94 -75
- data/lib/action_dispatch/http/response.rb +73 -61
- data/lib/action_dispatch/http/upload.rb +18 -16
- data/lib/action_dispatch/http/url.rb +75 -73
- data/lib/action_dispatch/journey/formatter.rb +13 -6
- 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 +10 -8
- 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 +13 -5
- 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 +6 -11
- data/lib/action_dispatch/middleware/executor.rb +8 -0
- 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 +8 -6
- data/lib/action_dispatch/middleware/reloader.rb +5 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +77 -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 +5 -3
- data/lib/action_dispatch/routing/mapper.rb +671 -636
- 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 +59 -45
- 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 +8 -6
- 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 +223 -222
- data/lib/action_dispatch/testing/request_encoder.rb +2 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +12 -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 +22 -28
- 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 +39 -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,74 @@ 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
|
+
# defaults id: 'home' do
|
1052
|
+
# match 'scoped_pages/(:id)', to: 'pages#show'
|
1053
|
+
# end
|
1054
|
+
#
|
1055
|
+
# Using this, the `:id` parameter here will default to 'home'.
|
1019
1056
|
def defaults(defaults = {})
|
1020
1057
|
@scope = @scope.new(defaults: merge_defaults_scope(@scope[:defaults], defaults))
|
1021
1058
|
yield
|
@@ -1091,48 +1128,46 @@ module ActionDispatch
|
|
1091
1128
|
end
|
1092
1129
|
end
|
1093
1130
|
|
1094
|
-
# Resource routing allows you to quickly declare all of the common routes
|
1095
|
-
#
|
1096
|
-
#
|
1097
|
-
#
|
1131
|
+
# Resource routing allows you to quickly declare all of the common routes for a
|
1132
|
+
# given resourceful controller. Instead of declaring separate routes for your
|
1133
|
+
# `index`, `show`, `new`, `edit`, `create`, `update`, and `destroy` actions, a
|
1134
|
+
# resourceful route declares them in a single line of code:
|
1098
1135
|
#
|
1099
|
-
#
|
1136
|
+
# resources :photos
|
1100
1137
|
#
|
1101
|
-
# Sometimes, you have a resource that clients always look up without
|
1102
|
-
#
|
1103
|
-
#
|
1104
|
-
#
|
1138
|
+
# Sometimes, you have a resource that clients always look up without referencing
|
1139
|
+
# an ID. A common example, /profile always shows the profile of the currently
|
1140
|
+
# logged in user. In this case, you can use a singular resource to map /profile
|
1141
|
+
# (rather than /profile/:id) to the show action.
|
1105
1142
|
#
|
1106
|
-
#
|
1143
|
+
# resource :profile
|
1107
1144
|
#
|
1108
|
-
# It's common to have resources that are logically children of other
|
1109
|
-
# resources:
|
1145
|
+
# It's common to have resources that are logically children of other resources:
|
1110
1146
|
#
|
1111
|
-
#
|
1112
|
-
#
|
1113
|
-
#
|
1147
|
+
# resources :magazines do
|
1148
|
+
# resources :ads
|
1149
|
+
# end
|
1114
1150
|
#
|
1115
1151
|
# 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
|
1152
|
+
# commonly, you might group a number of administrative controllers under an
|
1153
|
+
# `admin` namespace. You would place these controllers under the
|
1154
|
+
# `app/controllers/admin` directory, and you can group them together in your
|
1155
|
+
# router:
|
1124
1156
|
#
|
1125
|
-
#
|
1126
|
-
#
|
1127
|
-
#
|
1157
|
+
# namespace "admin" do
|
1158
|
+
# resources :posts, :comments
|
1159
|
+
# end
|
1128
1160
|
#
|
1129
|
-
#
|
1161
|
+
# By default the `:id` parameter doesn't accept dots. If you need to use dots as
|
1162
|
+
# part of the `:id` parameter add a constraint which overrides this restriction,
|
1163
|
+
# e.g:
|
1130
1164
|
#
|
1131
|
-
#
|
1165
|
+
# resources :articles, id: /[^\/]+/
|
1132
1166
|
#
|
1167
|
+
# This allows any character other than a slash as part of your `:id`.
|
1133
1168
|
module Resources
|
1134
|
-
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
|
1135
|
-
#
|
1169
|
+
# CANONICAL_ACTIONS holds all actions that does not need a prefix or a path
|
1170
|
+
# appended since they fit properly in their scope level.
|
1136
1171
|
VALID_ON_OPTIONS = [:new, :collection, :member]
|
1137
1172
|
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
|
1138
1173
|
CANONICAL_ACTIONS = %w(index create new show update destroy)
|
@@ -1195,8 +1230,8 @@ module ActionDispatch
|
|
1195
1230
|
|
1196
1231
|
alias :member_name :singular
|
1197
1232
|
|
1198
|
-
# Checks for uncountable plurals, and appends "_index" if the plural
|
1199
|
-
#
|
1233
|
+
# Checks for uncountable plurals, and appends "_index" if the plural and
|
1234
|
+
# singular form are the same.
|
1200
1235
|
def collection_name
|
1201
1236
|
singular == plural ? "#{plural}_index" : plural
|
1202
1237
|
end
|
@@ -1269,37 +1304,35 @@ module ActionDispatch
|
|
1269
1304
|
@scope[:path_names].merge!(options)
|
1270
1305
|
end
|
1271
1306
|
|
1272
|
-
# Sometimes, you have a resource that clients always look up without
|
1273
|
-
#
|
1274
|
-
#
|
1275
|
-
#
|
1276
|
-
# the show action:
|
1307
|
+
# Sometimes, you have a resource that clients always look up without referencing
|
1308
|
+
# an ID. A common example, /profile always shows the profile of the currently
|
1309
|
+
# logged in user. In this case, you can use a singular resource to map /profile
|
1310
|
+
# (rather than /profile/:id) to the show action:
|
1277
1311
|
#
|
1278
|
-
#
|
1312
|
+
# resource :profile
|
1279
1313
|
#
|
1280
|
-
# This creates six different routes in your application, all mapping to
|
1281
|
-
#
|
1282
|
-
# the plural):
|
1314
|
+
# This creates six different routes in your application, all mapping to the
|
1315
|
+
# `Profiles` controller (note that the controller is named after the plural):
|
1283
1316
|
#
|
1284
|
-
#
|
1285
|
-
#
|
1286
|
-
#
|
1287
|
-
#
|
1288
|
-
#
|
1289
|
-
#
|
1317
|
+
# GET /profile/new
|
1318
|
+
# GET /profile
|
1319
|
+
# GET /profile/edit
|
1320
|
+
# PATCH/PUT /profile
|
1321
|
+
# DELETE /profile
|
1322
|
+
# POST /profile
|
1290
1323
|
#
|
1291
|
-
# If you want instances of a model to work with this resource via
|
1292
|
-
#
|
1293
|
-
#
|
1324
|
+
# If you want instances of a model to work with this resource via record
|
1325
|
+
# identification (e.g. in `form_with` or `redirect_to`), you will need to call
|
1326
|
+
# [resolve](rdoc-ref:CustomUrls#resolve):
|
1294
1327
|
#
|
1295
|
-
#
|
1296
|
-
#
|
1328
|
+
# resource :profile
|
1329
|
+
# resolve('Profile') { [:profile] }
|
1297
1330
|
#
|
1298
|
-
#
|
1299
|
-
#
|
1331
|
+
# # Enables this to work with singular routes:
|
1332
|
+
# form_with(model: @profile) {}
|
1300
1333
|
#
|
1301
|
-
#
|
1302
|
-
# Takes same options as resources
|
1334
|
+
# ### Options
|
1335
|
+
# Takes same options as [resources](rdoc-ref:#resources)
|
1303
1336
|
def resource(*resources, &block)
|
1304
1337
|
options = resources.extract_options!.dup
|
1305
1338
|
|
@@ -1329,143 +1362,147 @@ module ActionDispatch
|
|
1329
1362
|
self
|
1330
1363
|
end
|
1331
1364
|
|
1332
|
-
# In
|
1333
|
-
# and
|
1334
|
-
#
|
1335
|
-
# routing file, such as
|
1365
|
+
# In Rails, a resourceful route provides a mapping between HTTP verbs and URLs
|
1366
|
+
# and controller actions. By convention, each action also maps to particular
|
1367
|
+
# CRUD operations in a database. A single entry in the routing file, such as
|
1336
1368
|
#
|
1337
|
-
#
|
1369
|
+
# resources :photos
|
1338
1370
|
#
|
1339
|
-
# creates seven different routes in your application, all mapping to
|
1340
|
-
#
|
1371
|
+
# creates seven different routes in your application, all mapping to the
|
1372
|
+
# `Photos` controller:
|
1341
1373
|
#
|
1342
|
-
#
|
1343
|
-
#
|
1344
|
-
#
|
1345
|
-
#
|
1346
|
-
#
|
1347
|
-
#
|
1348
|
-
#
|
1374
|
+
# GET /photos
|
1375
|
+
# GET /photos/new
|
1376
|
+
# POST /photos
|
1377
|
+
# GET /photos/:id
|
1378
|
+
# GET /photos/:id/edit
|
1379
|
+
# PATCH/PUT /photos/:id
|
1380
|
+
# DELETE /photos/:id
|
1349
1381
|
#
|
1350
1382
|
# Resources can also be nested infinitely by using this block syntax:
|
1351
1383
|
#
|
1352
|
-
#
|
1353
|
-
#
|
1354
|
-
#
|
1384
|
+
# resources :photos do
|
1385
|
+
# resources :comments
|
1386
|
+
# end
|
1355
1387
|
#
|
1356
1388
|
# This generates the following comments routes:
|
1357
1389
|
#
|
1358
|
-
#
|
1359
|
-
#
|
1360
|
-
#
|
1361
|
-
#
|
1362
|
-
#
|
1363
|
-
#
|
1364
|
-
#
|
1390
|
+
# GET /photos/:photo_id/comments
|
1391
|
+
# GET /photos/:photo_id/comments/new
|
1392
|
+
# POST /photos/:photo_id/comments
|
1393
|
+
# GET /photos/:photo_id/comments/:id
|
1394
|
+
# GET /photos/:photo_id/comments/:id/edit
|
1395
|
+
# PATCH/PUT /photos/:photo_id/comments/:id
|
1396
|
+
# DELETE /photos/:photo_id/comments/:id
|
1365
1397
|
#
|
1366
|
-
#
|
1367
|
-
# Takes same options as match
|
1398
|
+
# ### Options
|
1399
|
+
# Takes same options as [match](rdoc-ref:Base#match) as well as:
|
1368
1400
|
#
|
1369
|
-
#
|
1370
|
-
# Allows you to change the segment component of the
|
1371
|
-
#
|
1401
|
+
# :path_names
|
1402
|
+
# : Allows you to change the segment component of the `edit` and `new`
|
1403
|
+
# actions. Actions not specified are not changed.
|
1372
1404
|
#
|
1373
|
-
#
|
1405
|
+
# resources :posts, path_names: { new: "brand_new" }
|
1374
1406
|
#
|
1375
|
-
#
|
1407
|
+
# The above example will now change /posts/new to /posts/brand_new.
|
1376
1408
|
#
|
1377
|
-
#
|
1378
|
-
# Allows you to change the path prefix for the resource.
|
1409
|
+
# :path
|
1410
|
+
# : Allows you to change the path prefix for the resource.
|
1379
1411
|
#
|
1380
|
-
#
|
1412
|
+
# resources :posts, path: 'postings'
|
1381
1413
|
#
|
1382
|
-
#
|
1414
|
+
# The resource and all segments will now route to /postings instead of
|
1415
|
+
# /posts.
|
1383
1416
|
#
|
1384
|
-
#
|
1385
|
-
# Only generate routes for the given actions.
|
1417
|
+
# :only
|
1418
|
+
# : Only generate routes for the given actions.
|
1386
1419
|
#
|
1387
|
-
#
|
1388
|
-
#
|
1420
|
+
# resources :cows, only: :show
|
1421
|
+
# resources :cows, only: [:show, :index]
|
1389
1422
|
#
|
1390
|
-
#
|
1391
|
-
# Generate all routes except for the given actions.
|
1423
|
+
# :except
|
1424
|
+
# : Generate all routes except for the given actions.
|
1392
1425
|
#
|
1393
|
-
#
|
1394
|
-
#
|
1426
|
+
# resources :cows, except: :show
|
1427
|
+
# resources :cows, except: [:show, :index]
|
1395
1428
|
#
|
1396
|
-
#
|
1397
|
-
# Generates shallow routes for nested resource(s). When placed on a parent
|
1398
|
-
#
|
1429
|
+
# :shallow
|
1430
|
+
# : Generates shallow routes for nested resource(s). When placed on a parent
|
1431
|
+
# resource, generates shallow routes for all nested resources.
|
1399
1432
|
#
|
1400
|
-
#
|
1401
|
-
#
|
1402
|
-
#
|
1433
|
+
# resources :posts, shallow: true do
|
1434
|
+
# resources :comments
|
1435
|
+
# end
|
1403
1436
|
#
|
1404
|
-
#
|
1437
|
+
# Is the same as:
|
1405
1438
|
#
|
1406
|
-
#
|
1407
|
-
#
|
1408
|
-
#
|
1409
|
-
#
|
1439
|
+
# resources :posts do
|
1440
|
+
# resources :comments, except: [:show, :edit, :update, :destroy]
|
1441
|
+
# end
|
1442
|
+
# resources :comments, only: [:show, :edit, :update, :destroy]
|
1410
1443
|
#
|
1411
|
-
#
|
1412
|
-
#
|
1413
|
-
#
|
1444
|
+
# This allows URLs for resources that otherwise would be deeply nested such
|
1445
|
+
# as a comment on a blog post like `/posts/a-long-permalink/comments/1234`
|
1446
|
+
# to be shortened to just `/comments/1234`.
|
1414
1447
|
#
|
1415
|
-
#
|
1448
|
+
# Set `shallow: false` on a child resource to ignore a parent's shallow
|
1449
|
+
# parameter.
|
1416
1450
|
#
|
1417
|
-
#
|
1418
|
-
# Prefixes nested shallow routes with the specified path.
|
1451
|
+
# :shallow_path
|
1452
|
+
# : Prefixes nested shallow routes with the specified path.
|
1419
1453
|
#
|
1420
|
-
#
|
1421
|
-
#
|
1422
|
-
#
|
1423
|
-
#
|
1424
|
-
#
|
1454
|
+
# scope shallow_path: "sekret" do
|
1455
|
+
# resources :posts do
|
1456
|
+
# resources :comments, shallow: true
|
1457
|
+
# end
|
1458
|
+
# end
|
1425
1459
|
#
|
1426
|
-
#
|
1460
|
+
# The `comments` resource here will have the following routes generated for
|
1461
|
+
# it:
|
1427
1462
|
#
|
1428
|
-
#
|
1429
|
-
#
|
1430
|
-
#
|
1431
|
-
#
|
1432
|
-
#
|
1433
|
-
#
|
1434
|
-
#
|
1463
|
+
# post_comments GET /posts/:post_id/comments(.:format)
|
1464
|
+
# post_comments POST /posts/:post_id/comments(.:format)
|
1465
|
+
# new_post_comment GET /posts/:post_id/comments/new(.:format)
|
1466
|
+
# edit_comment GET /sekret/comments/:id/edit(.:format)
|
1467
|
+
# comment GET /sekret/comments/:id(.:format)
|
1468
|
+
# comment PATCH/PUT /sekret/comments/:id(.:format)
|
1469
|
+
# comment DELETE /sekret/comments/:id(.:format)
|
1435
1470
|
#
|
1436
|
-
#
|
1437
|
-
# Prefixes nested shallow route names with specified prefix.
|
1471
|
+
# :shallow_prefix
|
1472
|
+
# : Prefixes nested shallow route names with specified prefix.
|
1438
1473
|
#
|
1439
|
-
#
|
1440
|
-
#
|
1441
|
-
#
|
1442
|
-
#
|
1443
|
-
#
|
1474
|
+
# scope shallow_prefix: "sekret" do
|
1475
|
+
# resources :posts do
|
1476
|
+
# resources :comments, shallow: true
|
1477
|
+
# end
|
1478
|
+
# end
|
1479
|
+
#
|
1480
|
+
# The `comments` resource here will have the following routes generated for
|
1481
|
+
# it:
|
1444
1482
|
#
|
1445
|
-
#
|
1483
|
+
# post_comments GET /posts/:post_id/comments(.:format)
|
1484
|
+
# post_comments POST /posts/:post_id/comments(.:format)
|
1485
|
+
# new_post_comment GET /posts/:post_id/comments/new(.:format)
|
1486
|
+
# edit_sekret_comment GET /comments/:id/edit(.:format)
|
1487
|
+
# sekret_comment GET /comments/:id(.:format)
|
1488
|
+
# sekret_comment PATCH/PUT /comments/:id(.:format)
|
1489
|
+
# sekret_comment DELETE /comments/:id(.:format)
|
1446
1490
|
#
|
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)
|
1491
|
+
# :format
|
1492
|
+
# : Allows you to specify the default value for optional `format` segment or
|
1493
|
+
# disable it by supplying `false`.
|
1454
1494
|
#
|
1455
|
-
#
|
1456
|
-
# Allows you to
|
1457
|
-
# segment or disable it by supplying +false+.
|
1495
|
+
# :param
|
1496
|
+
# : Allows you to override the default param name of `:id` in the URL.
|
1458
1497
|
#
|
1459
|
-
# [:param]
|
1460
|
-
# Allows you to override the default param name of +:id+ in the URL.
|
1461
1498
|
#
|
1462
|
-
#
|
1499
|
+
# ### Examples
|
1463
1500
|
#
|
1464
|
-
#
|
1465
|
-
#
|
1501
|
+
# # routes call +Admin::PostsController+
|
1502
|
+
# resources :posts, module: "admin"
|
1466
1503
|
#
|
1467
|
-
#
|
1468
|
-
#
|
1504
|
+
# # resource actions are at /admin/posts.
|
1505
|
+
# resources :posts, path: "admin/posts"
|
1469
1506
|
def resources(*resources, &block)
|
1470
1507
|
options = resources.extract_options!.dup
|
1471
1508
|
|
@@ -1498,16 +1535,15 @@ module ActionDispatch
|
|
1498
1535
|
|
1499
1536
|
# To add a route to the collection:
|
1500
1537
|
#
|
1501
|
-
#
|
1502
|
-
#
|
1503
|
-
#
|
1538
|
+
# resources :photos do
|
1539
|
+
# collection do
|
1540
|
+
# get 'search'
|
1541
|
+
# end
|
1504
1542
|
# end
|
1505
|
-
# end
|
1506
1543
|
#
|
1507
|
-
# This will enable
|
1508
|
-
#
|
1509
|
-
#
|
1510
|
-
# route helpers.
|
1544
|
+
# This will enable Rails to recognize paths such as `/photos/search` with GET,
|
1545
|
+
# and route to the search action of `PhotosController`. It will also create the
|
1546
|
+
# `search_photos_url` and `search_photos_path` route helpers.
|
1511
1547
|
def collection(&block)
|
1512
1548
|
unless resource_scope?
|
1513
1549
|
raise ArgumentError, "can't use collection outside resource(s) scope"
|
@@ -1520,15 +1556,15 @@ module ActionDispatch
|
|
1520
1556
|
|
1521
1557
|
# To add a member route, add a member block into the resource block:
|
1522
1558
|
#
|
1523
|
-
#
|
1524
|
-
#
|
1525
|
-
#
|
1559
|
+
# resources :photos do
|
1560
|
+
# member do
|
1561
|
+
# get 'preview'
|
1562
|
+
# end
|
1526
1563
|
# end
|
1527
|
-
# end
|
1528
1564
|
#
|
1529
|
-
# This will recognize
|
1530
|
-
#
|
1531
|
-
#
|
1565
|
+
# This will recognize `/photos/1/preview` with GET, and route to the preview
|
1566
|
+
# action of `PhotosController`. It will also create the `preview_photo_url` and
|
1567
|
+
# `preview_photo_path` helpers.
|
1532
1568
|
def member(&block)
|
1533
1569
|
unless resource_scope?
|
1534
1570
|
raise ArgumentError, "can't use member outside resource(s) scope"
|
@@ -1595,29 +1631,28 @@ module ActionDispatch
|
|
1595
1631
|
!parent_resource.singleton? && @scope[:shallow]
|
1596
1632
|
end
|
1597
1633
|
|
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.
|
1634
|
+
# Loads another routes file with the given `name` located inside the
|
1635
|
+
# `config/routes` directory. In that file, you can use the normal routing DSL,
|
1636
|
+
# but *do not* surround it with a `Rails.application.routes.draw` block.
|
1637
|
+
#
|
1638
|
+
# # config/routes.rb
|
1639
|
+
# Rails.application.routes.draw do
|
1640
|
+
# draw :admin # Loads `config/routes/admin.rb`
|
1641
|
+
# draw "third_party/some_gem" # Loads `config/routes/third_party/some_gem.rb`
|
1642
|
+
# end
|
1643
|
+
#
|
1644
|
+
# # config/routes/admin.rb
|
1645
|
+
# namespace :admin do
|
1646
|
+
# resources :accounts
|
1647
|
+
# end
|
1648
|
+
#
|
1649
|
+
# # config/routes/third_party/some_gem.rb
|
1650
|
+
# mount SomeGem::Engine, at: "/some_gem"
|
1651
|
+
#
|
1652
|
+
# **CAUTION:** Use this feature with care. Having multiple routes files can
|
1653
|
+
# negatively impact discoverability and readability. For most applications —
|
1654
|
+
# even those with a few hundred routes — it's easier for developers to have a
|
1655
|
+
# single routes file.
|
1621
1656
|
def draw(name)
|
1622
1657
|
path = @draw_paths.find do |_path|
|
1623
1658
|
File.exist? "#{_path}/#{name}.rb"
|
@@ -1634,12 +1669,12 @@ module ActionDispatch
|
|
1634
1669
|
instance_eval(File.read(route_path), route_path.to_s)
|
1635
1670
|
end
|
1636
1671
|
|
1637
|
-
# Matches a URL pattern to one or more routes.
|
1638
|
-
#
|
1672
|
+
# Matches a URL pattern to one or more routes. For more information, see
|
1673
|
+
# [match](rdoc-ref:Base#match).
|
1639
1674
|
#
|
1640
|
-
#
|
1641
|
-
#
|
1642
|
-
#
|
1675
|
+
# match 'path' => 'controller#action', via: :patch
|
1676
|
+
# match 'path', to: 'controller#action', via: :post
|
1677
|
+
# match 'path', 'otherpath', on: :member, via: :get
|
1643
1678
|
def match(path, *rest, &block)
|
1644
1679
|
if rest.empty? && Hash === path
|
1645
1680
|
options = path
|
@@ -1674,19 +1709,19 @@ module ActionDispatch
|
|
1674
1709
|
end
|
1675
1710
|
end
|
1676
1711
|
|
1677
|
-
# You can specify what
|
1712
|
+
# You can specify what Rails should route "/" to with the root method:
|
1678
1713
|
#
|
1679
|
-
#
|
1714
|
+
# root to: 'pages#main'
|
1680
1715
|
#
|
1681
|
-
# For options, see
|
1716
|
+
# For options, see `match`, as `root` uses it internally.
|
1682
1717
|
#
|
1683
1718
|
# You can also pass a string which will expand
|
1684
1719
|
#
|
1685
|
-
#
|
1720
|
+
# root 'pages#main'
|
1686
1721
|
#
|
1687
|
-
# You should put the root route at the top of
|
1688
|
-
#
|
1689
|
-
#
|
1722
|
+
# You should put the root route at the top of `config/routes.rb`, because this
|
1723
|
+
# means it will be matched first. As this is the most popular route of most
|
1724
|
+
# Rails applications, this is beneficial.
|
1690
1725
|
def root(path, options = {})
|
1691
1726
|
if path.is_a?(String)
|
1692
1727
|
options[:to] = path
|
@@ -1864,9 +1899,9 @@ module ActionDispatch
|
|
1864
1899
|
candidate = action_name.select(&:present?).join("_")
|
1865
1900
|
|
1866
1901
|
unless candidate.empty?
|
1867
|
-
# If a name was not explicitly given, we check if it is valid
|
1868
|
-
#
|
1869
|
-
#
|
1902
|
+
# If a name was not explicitly given, we check if it is valid and return nil in
|
1903
|
+
# case it isn't. Otherwise, we pass the invalid name forward so the underlying
|
1904
|
+
# router engine treats it and raises an exception.
|
1870
1905
|
if as.nil?
|
1871
1906
|
candidate unless !candidate.match?(/\A[_a-z]/i) || has_named_route?(candidate)
|
1872
1907
|
else
|
@@ -2002,83 +2037,81 @@ module ActionDispatch
|
|
2002
2037
|
end
|
2003
2038
|
end
|
2004
2039
|
|
2005
|
-
# Routing Concerns allow you to declare common routes that can be reused
|
2006
|
-
#
|
2040
|
+
# Routing Concerns allow you to declare common routes that can be reused inside
|
2041
|
+
# others resources and routes.
|
2007
2042
|
#
|
2008
|
-
#
|
2009
|
-
#
|
2010
|
-
#
|
2043
|
+
# concern :commentable do
|
2044
|
+
# resources :comments
|
2045
|
+
# end
|
2011
2046
|
#
|
2012
|
-
#
|
2013
|
-
#
|
2014
|
-
#
|
2047
|
+
# concern :image_attachable do
|
2048
|
+
# resources :images, only: :index
|
2049
|
+
# end
|
2015
2050
|
#
|
2016
2051
|
# These concerns are used in Resources routing:
|
2017
2052
|
#
|
2018
|
-
#
|
2053
|
+
# resources :messages, concerns: [:commentable, :image_attachable]
|
2019
2054
|
#
|
2020
2055
|
# or in a scope or namespace:
|
2021
2056
|
#
|
2022
|
-
#
|
2023
|
-
#
|
2024
|
-
#
|
2057
|
+
# namespace :posts do
|
2058
|
+
# concerns :commentable
|
2059
|
+
# end
|
2025
2060
|
module Concerns
|
2026
2061
|
# Define a routing concern using a name.
|
2027
2062
|
#
|
2028
|
-
# Concerns may be defined inline, using a block, or handled by
|
2029
|
-
#
|
2063
|
+
# Concerns may be defined inline, using a block, or handled by another object,
|
2064
|
+
# by passing that object as the second parameter.
|
2030
2065
|
#
|
2031
|
-
# The concern object, if supplied, should respond to
|
2032
|
-
#
|
2066
|
+
# The concern object, if supplied, should respond to `call`, which will receive
|
2067
|
+
# two parameters:
|
2033
2068
|
#
|
2034
|
-
#
|
2035
|
-
#
|
2069
|
+
# * The current mapper
|
2070
|
+
# * A hash of options which the concern object may use
|
2036
2071
|
#
|
2037
|
-
# Options may also be used by concerns defined in a block by accepting
|
2038
|
-
#
|
2039
|
-
#
|
2040
|
-
#
|
2072
|
+
# Options may also be used by concerns defined in a block by accepting a block
|
2073
|
+
# parameter. So, using a block, you might do something as simple as limit the
|
2074
|
+
# actions available on certain resources, passing standard resource options
|
2075
|
+
# through the concern:
|
2041
2076
|
#
|
2042
|
-
#
|
2043
|
-
#
|
2044
|
-
#
|
2077
|
+
# concern :commentable do |options|
|
2078
|
+
# resources :comments, options
|
2079
|
+
# end
|
2045
2080
|
#
|
2046
|
-
#
|
2047
|
-
#
|
2048
|
-
#
|
2049
|
-
#
|
2050
|
-
#
|
2081
|
+
# resources :posts, concerns: :commentable
|
2082
|
+
# resources :archived_posts do
|
2083
|
+
# # Don't allow comments on archived posts
|
2084
|
+
# concerns :commentable, only: [:index, :show]
|
2085
|
+
# end
|
2051
2086
|
#
|
2052
|
-
# Or, using a callable object, you might implement something more
|
2053
|
-
#
|
2054
|
-
# routes file.
|
2087
|
+
# Or, using a callable object, you might implement something more specific to
|
2088
|
+
# your application, which would be out of place in your routes file.
|
2055
2089
|
#
|
2056
|
-
#
|
2057
|
-
#
|
2058
|
-
#
|
2059
|
-
#
|
2060
|
-
#
|
2090
|
+
# # purchasable.rb
|
2091
|
+
# class Purchasable
|
2092
|
+
# def initialize(defaults = {})
|
2093
|
+
# @defaults = defaults
|
2094
|
+
# end
|
2061
2095
|
#
|
2062
|
-
#
|
2063
|
-
#
|
2064
|
-
#
|
2065
|
-
#
|
2066
|
-
#
|
2096
|
+
# def call(mapper, options = {})
|
2097
|
+
# options = @defaults.merge(options)
|
2098
|
+
# mapper.resources :purchases
|
2099
|
+
# mapper.resources :receipts
|
2100
|
+
# mapper.resources :returns if options[:returnable]
|
2101
|
+
# end
|
2067
2102
|
# end
|
2068
|
-
# end
|
2069
2103
|
#
|
2070
|
-
#
|
2071
|
-
#
|
2104
|
+
# # routes.rb
|
2105
|
+
# concern :purchasable, Purchasable.new(returnable: true)
|
2072
2106
|
#
|
2073
|
-
#
|
2074
|
-
#
|
2075
|
-
#
|
2076
|
-
#
|
2077
|
-
#
|
2107
|
+
# resources :toys, concerns: :purchasable
|
2108
|
+
# resources :electronics, concerns: :purchasable
|
2109
|
+
# resources :pets do
|
2110
|
+
# concerns :purchasable, returnable: false
|
2111
|
+
# end
|
2078
2112
|
#
|
2079
|
-
# Any routing helpers can be used inside a concern. If using a
|
2080
|
-
#
|
2081
|
-
# <tt>call</tt>.
|
2113
|
+
# Any routing helpers can be used inside a concern. If using a callable, they're
|
2114
|
+
# accessible from the Mapper that's passed to `call`.
|
2082
2115
|
def concern(name, callable = nil, &block)
|
2083
2116
|
callable ||= lambda { |mapper, options| mapper.instance_exec(options, &block) }
|
2084
2117
|
@concerns[name] = callable
|
@@ -2086,15 +2119,15 @@ module ActionDispatch
|
|
2086
2119
|
|
2087
2120
|
# Use the named concerns
|
2088
2121
|
#
|
2089
|
-
#
|
2090
|
-
#
|
2091
|
-
#
|
2122
|
+
# resources :posts do
|
2123
|
+
# concerns :commentable
|
2124
|
+
# end
|
2092
2125
|
#
|
2093
2126
|
# Concerns also work in any routes helper that you want to use:
|
2094
2127
|
#
|
2095
|
-
#
|
2096
|
-
#
|
2097
|
-
#
|
2128
|
+
# namespace :posts do
|
2129
|
+
# concerns :commentable
|
2130
|
+
# end
|
2098
2131
|
def concerns(*args)
|
2099
2132
|
options = args.extract_options!
|
2100
2133
|
args.flatten.each do |name|
|
@@ -2108,53 +2141,55 @@ module ActionDispatch
|
|
2108
2141
|
end
|
2109
2142
|
|
2110
2143
|
module CustomUrls
|
2111
|
-
# Define custom URL helpers that will be added to the application's
|
2112
|
-
#
|
2113
|
-
#
|
2144
|
+
# Define custom URL helpers that will be added to the application's routes. This
|
2145
|
+
# allows you to override and/or replace the default behavior of routing helpers,
|
2146
|
+
# e.g:
|
2114
2147
|
#
|
2115
|
-
#
|
2116
|
-
#
|
2117
|
-
#
|
2148
|
+
# direct :homepage do
|
2149
|
+
# "https://rubyonrails.org"
|
2150
|
+
# end
|
2118
2151
|
#
|
2119
|
-
#
|
2120
|
-
#
|
2121
|
-
#
|
2152
|
+
# direct :commentable do |model|
|
2153
|
+
# [ model, anchor: model.dom_id ]
|
2154
|
+
# end
|
2122
2155
|
#
|
2123
|
-
#
|
2124
|
-
#
|
2125
|
-
#
|
2156
|
+
# direct :main do
|
2157
|
+
# { controller: "pages", action: "index", subdomain: "www" }
|
2158
|
+
# end
|
2126
2159
|
#
|
2127
|
-
# The return value from the block passed to
|
2128
|
-
# arguments for
|
2129
|
-
#
|
2160
|
+
# The return value from the block passed to `direct` must be a valid set of
|
2161
|
+
# arguments for `url_for` which will actually build the URL string. This can be
|
2162
|
+
# one of the following:
|
2130
2163
|
#
|
2131
|
-
# *
|
2132
|
-
# *
|
2133
|
-
# *
|
2134
|
-
# *
|
2135
|
-
# *
|
2164
|
+
# * A string, which is treated as a generated URL
|
2165
|
+
# * A hash, e.g. `{ controller: "pages", action: "index" }`
|
2166
|
+
# * An array, which is passed to `polymorphic_url`
|
2167
|
+
# * An Active Model instance
|
2168
|
+
# * An Active Model class
|
2136
2169
|
#
|
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
2170
|
#
|
2140
|
-
#
|
2141
|
-
# your URL helper
|
2171
|
+
# NOTE: Other URL helpers can be called in the block but be careful not to
|
2172
|
+
# invoke your custom URL helper again otherwise it will result in a stack
|
2173
|
+
# overflow error.
|
2142
2174
|
#
|
2143
|
-
#
|
2144
|
-
#
|
2145
|
-
# end
|
2175
|
+
# You can also specify default options that will be passed through to your URL
|
2176
|
+
# helper definition, e.g:
|
2146
2177
|
#
|
2147
|
-
#
|
2148
|
-
#
|
2149
|
-
#
|
2178
|
+
# direct :browse, page: 1, size: 10 do |options|
|
2179
|
+
# [ :products, options.merge(params.permit(:page, :size).to_h.symbolize_keys) ]
|
2180
|
+
# end
|
2150
2181
|
#
|
2151
|
-
#
|
2182
|
+
# In this instance the `params` object comes from the context in which the block
|
2183
|
+
# is executed, e.g. generating a URL inside a controller action or a view. If
|
2184
|
+
# the block is executed where there isn't a `params` object such as this:
|
2152
2185
|
#
|
2153
|
-
#
|
2186
|
+
# Rails.application.routes.url_helpers.browse_path
|
2187
|
+
#
|
2188
|
+
# then it will raise a `NameError`. Because of this you need to be aware of the
|
2154
2189
|
# context in which you will use your custom URL helper when defining it.
|
2155
2190
|
#
|
2156
|
-
# NOTE: The
|
2157
|
-
#
|
2191
|
+
# NOTE: The `direct` method can't be used inside of a scope block such as
|
2192
|
+
# `namespace` or `scope` and will raise an error if it detects that it is.
|
2158
2193
|
def direct(name, options = {}, &block)
|
2159
2194
|
unless @scope.root?
|
2160
2195
|
raise RuntimeError, "The direct method can't be used inside a routes scope block"
|
@@ -2163,50 +2198,50 @@ module ActionDispatch
|
|
2163
2198
|
@set.add_url_helper(name, options, &block)
|
2164
2199
|
end
|
2165
2200
|
|
2166
|
-
# Define custom polymorphic mappings of models to URLs. This alters the
|
2167
|
-
#
|
2168
|
-
#
|
2201
|
+
# Define custom polymorphic mappings of models to URLs. This alters the behavior
|
2202
|
+
# of `polymorphic_url` and consequently the behavior of `link_to` and `form_for`
|
2203
|
+
# when passed a model instance, e.g:
|
2169
2204
|
#
|
2170
|
-
#
|
2205
|
+
# resource :basket
|
2171
2206
|
#
|
2172
|
-
#
|
2173
|
-
#
|
2174
|
-
#
|
2207
|
+
# resolve "Basket" do
|
2208
|
+
# [:basket]
|
2209
|
+
# end
|
2175
2210
|
#
|
2176
|
-
# This will now generate "/basket" when a
|
2177
|
-
#
|
2211
|
+
# This will now generate "/basket" when a `Basket` instance is passed to
|
2212
|
+
# `link_to` or `form_for` instead of the standard "/baskets/:id".
|
2178
2213
|
#
|
2179
|
-
# NOTE: This custom behavior only applies to simple polymorphic URLs where
|
2180
|
-
#
|
2214
|
+
# NOTE: This custom behavior only applies to simple polymorphic URLs where a
|
2215
|
+
# single model instance is passed and not more complicated forms, e.g:
|
2181
2216
|
#
|
2182
|
-
#
|
2183
|
-
#
|
2184
|
-
#
|
2185
|
-
#
|
2186
|
-
#
|
2217
|
+
# # config/routes.rb
|
2218
|
+
# resource :profile
|
2219
|
+
# namespace :admin do
|
2220
|
+
# resources :users
|
2221
|
+
# end
|
2187
2222
|
#
|
2188
|
-
#
|
2223
|
+
# resolve("User") { [:profile] }
|
2189
2224
|
#
|
2190
|
-
#
|
2191
|
-
#
|
2192
|
-
#
|
2225
|
+
# # app/views/application/_menu.html.erb
|
2226
|
+
# link_to "Profile", @current_user
|
2227
|
+
# link_to "Profile", [:admin, @current_user]
|
2193
2228
|
#
|
2194
|
-
# The first
|
2195
|
-
#
|
2229
|
+
# The first `link_to` will generate "/profile" but the second will generate the
|
2230
|
+
# standard polymorphic URL of "/admin/users/1".
|
2196
2231
|
#
|
2197
|
-
# You can pass options to a polymorphic mapping - the arity for the block
|
2198
|
-
#
|
2232
|
+
# You can pass options to a polymorphic mapping - the arity for the block needs
|
2233
|
+
# to be two as the instance is passed as the first argument, e.g:
|
2199
2234
|
#
|
2200
|
-
#
|
2201
|
-
#
|
2202
|
-
#
|
2235
|
+
# resolve "Basket", anchor: "items" do |basket, options|
|
2236
|
+
# [:basket, options]
|
2237
|
+
# end
|
2203
2238
|
#
|
2204
|
-
# This generates the URL "/basket#items" because when the last item in an
|
2205
|
-
#
|
2206
|
-
#
|
2239
|
+
# This generates the URL "/basket#items" because when the last item in an array
|
2240
|
+
# passed to `polymorphic_url` is a hash then it's treated as options to the URL
|
2241
|
+
# helper that gets called.
|
2207
2242
|
#
|
2208
|
-
# NOTE: The
|
2209
|
-
#
|
2243
|
+
# NOTE: The `resolve` method can't be used inside of a scope block such as
|
2244
|
+
# `namespace` or `scope` and will raise an error if it detects that it is.
|
2210
2245
|
def resolve(*args, &block)
|
2211
2246
|
unless @scope.root?
|
2212
2247
|
raise RuntimeError, "The resolve method can't be used inside a routes scope block"
|