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