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.
- 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"
|