actionpack 5.0.7.2 → 5.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +189 -1002
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/abstract_controller.rb +3 -3
- data/lib/abstract_controller/base.rb +10 -12
- data/lib/abstract_controller/caching.rb +6 -3
- data/lib/abstract_controller/caching/fragments.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +2 -43
- data/lib/abstract_controller/collector.rb +2 -2
- data/lib/abstract_controller/helpers.rb +19 -19
- data/lib/abstract_controller/rendering.rb +9 -11
- data/lib/abstract_controller/translation.rb +3 -3
- data/lib/action_controller.rb +15 -13
- data/lib/action_controller/api.rb +3 -3
- data/lib/action_controller/base.rb +7 -12
- data/lib/action_controller/caching.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +2 -2
- data/lib/action_controller/metal.rb +34 -43
- data/lib/action_controller/metal/conditional_get.rb +10 -9
- data/lib/action_controller/metal/data_streaming.rb +8 -9
- data/lib/action_controller/metal/etag_with_flash.rb +16 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +15 -15
- data/lib/action_controller/metal/exceptions.rb +4 -14
- data/lib/action_controller/metal/flash.rb +1 -1
- data/lib/action_controller/metal/force_ssl.rb +6 -6
- data/lib/action_controller/metal/head.rb +13 -19
- data/lib/action_controller/metal/helpers.rb +6 -6
- data/lib/action_controller/metal/http_authentication.rb +22 -23
- data/lib/action_controller/metal/implicit_render.rb +2 -5
- data/lib/action_controller/metal/instrumentation.rb +14 -14
- data/lib/action_controller/metal/live.rb +15 -16
- data/lib/action_controller/metal/mime_responds.rb +3 -3
- data/lib/action_controller/metal/parameter_encoding.rb +49 -0
- data/lib/action_controller/metal/params_wrapper.rb +32 -32
- data/lib/action_controller/metal/redirecting.rb +8 -24
- data/lib/action_controller/metal/renderers.rb +2 -3
- data/lib/action_controller/metal/rendering.rb +50 -60
- data/lib/action_controller/metal/request_forgery_protection.rb +51 -49
- data/lib/action_controller/metal/rescue.rb +1 -1
- data/lib/action_controller/metal/streaming.rb +4 -4
- data/lib/action_controller/metal/strong_parameters.rb +117 -250
- data/lib/action_controller/metal/testing.rb +1 -1
- data/lib/action_controller/metal/url_for.rb +4 -4
- data/lib/action_controller/railtie.rb +9 -13
- data/lib/action_controller/renderer.rb +17 -16
- data/lib/action_controller/test_case.rb +75 -148
- data/lib/action_dispatch.rb +20 -19
- data/lib/action_dispatch/http/cache.rb +9 -10
- data/lib/action_dispatch/http/filter_parameters.rb +8 -8
- data/lib/action_dispatch/http/filter_redirect.rb +2 -4
- data/lib/action_dispatch/http/headers.rb +10 -10
- data/lib/action_dispatch/http/mime_negotiation.rb +17 -22
- data/lib/action_dispatch/http/mime_type.rb +27 -52
- data/lib/action_dispatch/http/parameter_filter.rb +8 -6
- data/lib/action_dispatch/http/parameters.rb +40 -17
- data/lib/action_dispatch/http/request.rb +38 -34
- data/lib/action_dispatch/http/response.rb +16 -16
- data/lib/action_dispatch/http/upload.rb +6 -10
- data/lib/action_dispatch/http/url.rb +48 -74
- data/lib/action_dispatch/journey.rb +5 -5
- data/lib/action_dispatch/journey/formatter.rb +8 -4
- data/lib/action_dispatch/journey/gtg/builder.rb +5 -5
- data/lib/action_dispatch/journey/gtg/simulator.rb +1 -1
- data/lib/action_dispatch/journey/gtg/transition_table.rb +15 -15
- data/lib/action_dispatch/journey/nfa/builder.rb +3 -3
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
- data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
- data/lib/action_dispatch/journey/nfa/transition_table.rb +2 -2
- data/lib/action_dispatch/journey/nodes/node.rb +5 -5
- data/lib/action_dispatch/journey/parser.rb +23 -24
- data/lib/action_dispatch/journey/parser.y +3 -2
- data/lib/action_dispatch/journey/parser_extras.rb +2 -2
- data/lib/action_dispatch/journey/path/pattern.rb +10 -3
- data/lib/action_dispatch/journey/route.rb +19 -12
- data/lib/action_dispatch/journey/router.rb +19 -12
- data/lib/action_dispatch/journey/router/utils.rb +9 -9
- data/lib/action_dispatch/journey/scanner.rb +17 -15
- data/lib/action_dispatch/journey/visitors.rb +23 -23
- data/lib/action_dispatch/middleware/callbacks.rb +0 -12
- data/lib/action_dispatch/middleware/cookies.rb +39 -39
- data/lib/action_dispatch/middleware/debug_exceptions.rb +126 -112
- data/lib/action_dispatch/middleware/debug_locks.rb +8 -8
- data/lib/action_dispatch/middleware/exception_wrapper.rb +55 -55
- data/lib/action_dispatch/middleware/executor.rb +1 -1
- data/lib/action_dispatch/middleware/flash.rb +17 -16
- data/lib/action_dispatch/middleware/public_exceptions.rb +20 -20
- data/lib/action_dispatch/middleware/reloader.rb +3 -47
- data/lib/action_dispatch/middleware/remote_ip.rb +6 -8
- data/lib/action_dispatch/middleware/request_id.rb +6 -5
- data/lib/action_dispatch/middleware/session/abstract_store.rb +14 -26
- data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +35 -35
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +2 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +19 -19
- data/lib/action_dispatch/middleware/ssl.rb +9 -27
- data/lib/action_dispatch/middleware/stack.rb +7 -26
- data/lib/action_dispatch/middleware/static.rb +13 -24
- data/lib/action_dispatch/railtie.rb +9 -11
- data/lib/action_dispatch/request/session.rb +22 -22
- data/lib/action_dispatch/request/utils.rb +11 -2
- data/lib/action_dispatch/routing.rb +8 -6
- data/lib/action_dispatch/routing/inspector.rb +37 -37
- data/lib/action_dispatch/routing/mapper.rb +296 -203
- data/lib/action_dispatch/routing/polymorphic_routes.rb +160 -134
- data/lib/action_dispatch/routing/redirection.rb +27 -22
- data/lib/action_dispatch/routing/route_set.rb +206 -92
- data/lib/action_dispatch/routing/routes_proxy.rb +2 -2
- data/lib/action_dispatch/routing/url_for.rb +14 -12
- data/lib/action_dispatch/system_test_case.rb +119 -0
- data/lib/action_dispatch/system_testing/browser.rb +28 -0
- data/lib/action_dispatch/system_testing/driver.rb +18 -0
- data/lib/action_dispatch/system_testing/server.rb +32 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +61 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +20 -0
- data/lib/action_dispatch/testing/assertion_response.rb +6 -6
- data/lib/action_dispatch/testing/assertions.rb +4 -4
- data/lib/action_dispatch/testing/assertions/response.rb +8 -3
- data/lib/action_dispatch/testing/assertions/routing.rb +11 -11
- data/lib/action_dispatch/testing/integration.rb +47 -138
- data/lib/action_dispatch/testing/test_process.rb +2 -2
- data/lib/action_dispatch/testing/test_request.rb +16 -16
- data/lib/action_dispatch/testing/test_response.rb +1 -1
- data/lib/action_pack.rb +2 -2
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack/version.rb +1 -1
- metadata +20 -12
- data/lib/action_dispatch/middleware/params_parser.rb +0 -46
@@ -8,20 +8,20 @@ module ActionDispatch
|
|
8
8
|
config.action_dispatch.show_exceptions = true
|
9
9
|
config.action_dispatch.tld_length = 1
|
10
10
|
config.action_dispatch.ignore_accept_header = false
|
11
|
-
config.action_dispatch.rescue_templates = {
|
12
|
-
config.action_dispatch.rescue_responses = {
|
11
|
+
config.action_dispatch.rescue_templates = {}
|
12
|
+
config.action_dispatch.rescue_responses = {}
|
13
13
|
config.action_dispatch.default_charset = nil
|
14
14
|
config.action_dispatch.rack_cache = false
|
15
|
-
config.action_dispatch.http_auth_salt =
|
16
|
-
config.action_dispatch.signed_cookie_salt =
|
17
|
-
config.action_dispatch.encrypted_cookie_salt =
|
18
|
-
config.action_dispatch.encrypted_signed_cookie_salt =
|
15
|
+
config.action_dispatch.http_auth_salt = "http authentication"
|
16
|
+
config.action_dispatch.signed_cookie_salt = "signed cookie"
|
17
|
+
config.action_dispatch.encrypted_cookie_salt = "encrypted cookie"
|
18
|
+
config.action_dispatch.encrypted_signed_cookie_salt = "signed encrypted cookie"
|
19
19
|
config.action_dispatch.perform_deep_munge = true
|
20
20
|
|
21
21
|
config.action_dispatch.default_headers = {
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
"X-Frame-Options" => "SAMEORIGIN",
|
23
|
+
"X-XSS-Protection" => "1; mode=block",
|
24
|
+
"X-Content-Type-Options" => "nosniff"
|
25
25
|
}
|
26
26
|
|
27
27
|
config.eager_load_namespaces << ActionDispatch
|
@@ -39,8 +39,6 @@ module ActionDispatch
|
|
39
39
|
config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil?
|
40
40
|
ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
|
41
41
|
|
42
|
-
ActionDispatch::Reloader.default_reloader = app.reloader
|
43
|
-
|
44
42
|
ActionDispatch.test_app = app
|
45
43
|
end
|
46
44
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "rack/session/abstract/id"
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
4
|
class Request
|
@@ -53,7 +53,7 @@ module ActionDispatch
|
|
53
53
|
}
|
54
54
|
end
|
55
55
|
|
56
|
-
def []=(k,v);
|
56
|
+
def []=(k, v); @delegate[k] = v; end
|
57
57
|
def to_hash; @delegate.dup; end
|
58
58
|
def values_at(*args); @delegate.values_at(*args); end
|
59
59
|
end
|
@@ -85,7 +85,7 @@ module ActionDispatch
|
|
85
85
|
end
|
86
86
|
|
87
87
|
# Returns value of the key stored in the session or
|
88
|
-
# nil if the given key is not found in the session.
|
88
|
+
# +nil+ if the given key is not found in the session.
|
89
89
|
def [](key)
|
90
90
|
load_for_read!
|
91
91
|
@delegate[key.to_s]
|
@@ -124,7 +124,7 @@ module ActionDispatch
|
|
124
124
|
# Returns the session as Hash.
|
125
125
|
def to_hash
|
126
126
|
load_for_read!
|
127
|
-
@delegate.dup.delete_if { |_,v| v.nil? }
|
127
|
+
@delegate.dup.delete_if { |_, v| v.nil? }
|
128
128
|
end
|
129
129
|
|
130
130
|
# Updates the session with given Hash.
|
@@ -162,7 +162,7 @@ module ActionDispatch
|
|
162
162
|
# :bar
|
163
163
|
# end
|
164
164
|
# # => :bar
|
165
|
-
def fetch(key, default=Unspecified, &block)
|
165
|
+
def fetch(key, default = Unspecified, &block)
|
166
166
|
load_for_read!
|
167
167
|
if default == Unspecified
|
168
168
|
@delegate.fetch(key.to_s, &block)
|
@@ -204,26 +204,26 @@ module ActionDispatch
|
|
204
204
|
|
205
205
|
private
|
206
206
|
|
207
|
-
|
208
|
-
|
209
|
-
|
207
|
+
def load_for_read!
|
208
|
+
load! if !loaded? && exists?
|
209
|
+
end
|
210
210
|
|
211
|
-
|
212
|
-
|
213
|
-
|
211
|
+
def load_for_write!
|
212
|
+
load! unless loaded?
|
213
|
+
end
|
214
214
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
215
|
+
def load!
|
216
|
+
id, session = @by.load_session @req
|
217
|
+
options[:id] = id
|
218
|
+
@delegate.replace(stringify_keys(session))
|
219
|
+
@loaded = true
|
220
|
+
end
|
221
221
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
222
|
+
def stringify_keys(other)
|
223
|
+
other.each_with_object({}) { |(key, value), hash|
|
224
|
+
hash[key.to_s] = value
|
225
|
+
}
|
226
|
+
end
|
227
227
|
end
|
228
228
|
end
|
229
229
|
end
|
@@ -1,10 +1,20 @@
|
|
1
1
|
module ActionDispatch
|
2
2
|
class Request
|
3
3
|
class Utils # :nodoc:
|
4
|
-
|
5
4
|
mattr_accessor :perform_deep_munge
|
6
5
|
self.perform_deep_munge = true
|
7
6
|
|
7
|
+
def self.each_param_value(params, &block)
|
8
|
+
case params
|
9
|
+
when Array
|
10
|
+
params.each { |element| each_param_value(element, &block) }
|
11
|
+
when Hash
|
12
|
+
params.each_value { |value| each_param_value(value, &block) }
|
13
|
+
when String
|
14
|
+
block.call params
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
8
18
|
def self.normalize_encode_params(params)
|
9
19
|
if perform_deep_munge
|
10
20
|
NoNilParamEncoder.normalize_encode_params params
|
@@ -64,4 +74,3 @@ module ActionDispatch
|
|
64
74
|
end
|
65
75
|
end
|
66
76
|
end
|
67
|
-
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "active_support/core_ext/string/filters"
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
# The routing module provides URL rewriting in native Ruby. It's a way to
|
3
5
|
# redirect incoming requests to controllers and actions. This replaces
|
@@ -89,7 +91,7 @@ module ActionDispatch
|
|
89
91
|
#
|
90
92
|
# Example:
|
91
93
|
#
|
92
|
-
# # In routes.rb
|
94
|
+
# # In config/routes.rb
|
93
95
|
# get '/login' => 'accounts#login', as: 'login'
|
94
96
|
#
|
95
97
|
# # With render, redirect_to, tests, etc.
|
@@ -101,7 +103,7 @@ module ActionDispatch
|
|
101
103
|
#
|
102
104
|
# Use <tt>root</tt> as a shorthand to name a route for the root path "/".
|
103
105
|
#
|
104
|
-
# # In routes.rb
|
106
|
+
# # In config/routes.rb
|
105
107
|
# root to: 'blogs#index'
|
106
108
|
#
|
107
109
|
# # would recognize http://www.example.com/ as
|
@@ -114,15 +116,15 @@ module ActionDispatch
|
|
114
116
|
# Note: when using +controller+, the route is simply named after the
|
115
117
|
# method you call on the block parameter rather than map.
|
116
118
|
#
|
117
|
-
# # In routes.rb
|
119
|
+
# # In config/routes.rb
|
118
120
|
# controller :blog do
|
119
121
|
# get 'blog/show' => :list
|
120
122
|
# get 'blog/delete' => :delete
|
121
|
-
# get 'blog/edit
|
123
|
+
# get 'blog/edit' => :edit
|
122
124
|
# end
|
123
125
|
#
|
124
126
|
# # provides named routes for show, delete, and edit
|
125
|
-
# link_to @article.title,
|
127
|
+
# link_to @article.title, blog_show_path(id: @article.id)
|
126
128
|
#
|
127
129
|
# == Pretty URLs
|
128
130
|
#
|
@@ -196,7 +198,7 @@ module ActionDispatch
|
|
196
198
|
#
|
197
199
|
# Rails.application.reload_routes!
|
198
200
|
#
|
199
|
-
# This will clear all named routes and reload routes.rb if the file has been modified from
|
201
|
+
# This will clear all named routes and reload config/routes.rb if the file has been modified from
|
200
202
|
# last load. To absolutely force reloading, use <tt>reload!</tt>.
|
201
203
|
#
|
202
204
|
# == Testing Routes
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "delegate"
|
2
|
+
require "active_support/core_ext/string/strip"
|
3
3
|
|
4
4
|
module ActionDispatch
|
5
5
|
module Routing
|
@@ -33,11 +33,11 @@ module ActionDispatch
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def controller
|
36
|
-
parts.include?(:controller) ?
|
36
|
+
parts.include?(:controller) ? ":controller" : requirements[:controller]
|
37
37
|
end
|
38
38
|
|
39
39
|
def action
|
40
|
-
parts.include?(:action) ?
|
40
|
+
parts.include?(:action) ? ":action" : requirements[:action]
|
41
41
|
end
|
42
42
|
|
43
43
|
def internal?
|
@@ -80,48 +80,48 @@ module ActionDispatch
|
|
80
80
|
|
81
81
|
private
|
82
82
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
83
|
+
def normalize_filter(filter)
|
84
|
+
if filter.is_a?(Hash) && filter[:controller]
|
85
|
+
{ controller: /#{filter[:controller].downcase.sub(/_?controller\z/, '').sub('::', '/')}/ }
|
86
|
+
elsif filter
|
87
|
+
{ controller: /#{filter}/, action: /#{filter}/, verb: /#{filter}/, name: /#{filter}/, path: /#{filter}/ }
|
88
|
+
end
|
88
89
|
end
|
89
|
-
end
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
91
|
+
def filter_routes(filter)
|
92
|
+
if filter
|
93
|
+
@routes.select do |route|
|
94
|
+
route_wrapper = RouteWrapper.new(route)
|
95
|
+
filter.any? { |default, value| route_wrapper.send(default) =~ value }
|
96
|
+
end
|
97
|
+
else
|
98
|
+
@routes
|
96
99
|
end
|
97
|
-
else
|
98
|
-
@routes
|
99
100
|
end
|
100
|
-
end
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
102
|
+
def collect_routes(routes)
|
103
|
+
routes.collect do |route|
|
104
|
+
RouteWrapper.new(route)
|
105
|
+
end.reject(&:internal?).collect do |route|
|
106
|
+
collect_engine_routes(route)
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
108
|
+
{ name: route.name,
|
109
|
+
verb: route.verb,
|
110
|
+
path: route.path,
|
111
|
+
reqs: route.reqs }
|
112
|
+
end
|
112
113
|
end
|
113
|
-
end
|
114
114
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
115
|
+
def collect_engine_routes(route)
|
116
|
+
name = route.endpoint
|
117
|
+
return unless route.engine?
|
118
|
+
return if @engines[name]
|
119
119
|
|
120
|
-
|
121
|
-
|
122
|
-
|
120
|
+
routes = route.rack_app.routes
|
121
|
+
if routes.is_a?(ActionDispatch::Routing::RouteSet)
|
122
|
+
@engines[name] = collect_routes(routes.routes)
|
123
|
+
end
|
123
124
|
end
|
124
|
-
end
|
125
125
|
end
|
126
126
|
|
127
127
|
class ConsoleFormatter
|
@@ -161,7 +161,7 @@ module ActionDispatch
|
|
161
161
|
|
162
162
|
private
|
163
163
|
def draw_section(routes)
|
164
|
-
header_lengths = [
|
164
|
+
header_lengths = ["Prefix", "Verb", "URI Pattern"].map(&:length)
|
165
165
|
name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
|
166
166
|
|
167
167
|
routes.map do |r|
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
1
|
+
require "active_support/core_ext/hash/slice"
|
2
|
+
require "active_support/core_ext/enumerable"
|
3
|
+
require "active_support/core_ext/array/extract_options"
|
4
|
+
require "active_support/core_ext/regexp"
|
5
|
+
require "action_dispatch/routing/redirection"
|
6
|
+
require "action_dispatch/routing/endpoint"
|
7
7
|
|
8
8
|
module ActionDispatch
|
9
9
|
module Routing
|
@@ -41,7 +41,7 @@ module ActionDispatch
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def serve(req)
|
44
|
-
return [ 404, {
|
44
|
+
return [ 404, { "X-Cascade" => "pass" }, [] ] unless matches?(req)
|
45
45
|
|
46
46
|
@strategy.call @app, req
|
47
47
|
end
|
@@ -54,7 +54,6 @@ module ActionDispatch
|
|
54
54
|
|
55
55
|
class Mapping #:nodoc:
|
56
56
|
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
|
57
|
-
OPTIONAL_FORMAT_REGEX = %r{(?:\(\.:format\)+|\.:format|/)\Z}
|
58
57
|
|
59
58
|
attr_reader :requirements, :defaults
|
60
59
|
attr_reader :to, :default_controller, :default_action
|
@@ -94,7 +93,7 @@ module ActionDispatch
|
|
94
93
|
end
|
95
94
|
|
96
95
|
def self.optional_format?(path, format)
|
97
|
-
format != false && path
|
96
|
+
format != false && !path.include?(":format") && !path.end_with?("/")
|
98
97
|
end
|
99
98
|
|
100
99
|
def initialize(set, ast, defaults, controller, default_action, modyoule, to, formatted, scope_constraints, blocks, via, options_constraints, anchor, options)
|
@@ -107,7 +106,7 @@ module ActionDispatch
|
|
107
106
|
@ast = ast
|
108
107
|
@anchor = anchor
|
109
108
|
@via = via
|
110
|
-
@internal = options
|
109
|
+
@internal = options.delete(:internal)
|
111
110
|
|
112
111
|
path_params = ast.find_all(&:symbol?).map(&:to_sym)
|
113
112
|
|
@@ -139,7 +138,7 @@ module ActionDispatch
|
|
139
138
|
@defaults = formats[:defaults].merge(@defaults).merge(normalize_defaults(options))
|
140
139
|
|
141
140
|
if path_params.include?(:action) && !@requirements.key?(:action)
|
142
|
-
@defaults[:action] ||=
|
141
|
+
@defaults[:action] ||= "index"
|
143
142
|
end
|
144
143
|
|
145
144
|
@required_defaults = (split_options[:required_defaults] || []).map(&:first)
|
@@ -240,7 +239,7 @@ module ActionDispatch
|
|
240
239
|
options[:controller] ||= /.+?/
|
241
240
|
end
|
242
241
|
|
243
|
-
if to.respond_to? :call
|
242
|
+
if to.respond_to?(:action) || to.respond_to?(:call)
|
244
243
|
options
|
245
244
|
else
|
246
245
|
to_endpoint = split_to to
|
@@ -271,7 +270,7 @@ module ActionDispatch
|
|
271
270
|
{ requirements: { format: Regexp.compile(formatted) },
|
272
271
|
defaults: { format: formatted } }
|
273
272
|
else
|
274
|
-
{ requirements: {
|
273
|
+
{ requirements: {}, defaults: {} }
|
275
274
|
end
|
276
275
|
end
|
277
276
|
|
@@ -292,16 +291,14 @@ module ActionDispatch
|
|
292
291
|
end
|
293
292
|
|
294
293
|
def app(blocks)
|
295
|
-
if to.
|
294
|
+
if to.respond_to?(:action)
|
296
295
|
Routing::RouteSet::StaticDispatcher.new to
|
296
|
+
elsif to.respond_to?(:call)
|
297
|
+
Constraints.new(to, blocks, Constraints::CALL)
|
298
|
+
elsif blocks.any?
|
299
|
+
Constraints.new(dispatcher(defaults.key?(:controller)), blocks, Constraints::SERVE)
|
297
300
|
else
|
298
|
-
|
299
|
-
Constraints.new(to, blocks, Constraints::CALL)
|
300
|
-
elsif blocks.any?
|
301
|
-
Constraints.new(dispatcher(defaults.key?(:controller)), blocks, Constraints::SERVE)
|
302
|
-
else
|
303
|
-
dispatcher(defaults.key?(:controller))
|
304
|
-
end
|
301
|
+
dispatcher(defaults.key?(:controller))
|
305
302
|
end
|
306
303
|
end
|
307
304
|
|
@@ -334,7 +331,7 @@ module ActionDispatch
|
|
334
331
|
|
335
332
|
def split_to(to)
|
336
333
|
if to =~ /#/
|
337
|
-
to.split(
|
334
|
+
to.split("#")
|
338
335
|
else
|
339
336
|
[]
|
340
337
|
end
|
@@ -569,7 +566,7 @@ module ActionDispatch
|
|
569
566
|
# [:format]
|
570
567
|
# Allows you to specify the default value for optional +format+
|
571
568
|
# segment or disable it by supplying +false+.
|
572
|
-
def match(path, options=nil)
|
569
|
+
def match(path, options = nil)
|
573
570
|
end
|
574
571
|
|
575
572
|
# Mount a Rack-based application to be used within the application.
|
@@ -615,7 +612,7 @@ module ActionDispatch
|
|
615
612
|
target_as = name_for_action(options[:as], path)
|
616
613
|
options[:via] ||= :all
|
617
614
|
|
618
|
-
match(path, options.merge(:
|
615
|
+
match(path, options.merge(to: app, anchor: false, format: false))
|
619
616
|
|
620
617
|
define_generate_prefix(app, target_as) if rails_app
|
621
618
|
self
|
@@ -662,7 +659,7 @@ module ActionDispatch
|
|
662
659
|
super(options)
|
663
660
|
else
|
664
661
|
prefix_options = options.slice(*_route.segment_keys)
|
665
|
-
prefix_options[:relative_url_root] =
|
662
|
+
prefix_options[:relative_url_root] = "".freeze
|
666
663
|
# we must actually delete prefix segment keys to avoid passing them to next url_for
|
667
664
|
_route.segment_keys.each { |k| options.delete(k) }
|
668
665
|
_routes.url_helpers.send("#{name}_path", prefix_options)
|
@@ -811,7 +808,7 @@ module ActionDispatch
|
|
811
808
|
options = args.extract_options!.dup
|
812
809
|
scope = {}
|
813
810
|
|
814
|
-
options[:path] = args.flatten.join(
|
811
|
+
options[:path] = args.flatten.join("/") if args.any?
|
815
812
|
options[:constraints] ||= {}
|
816
813
|
|
817
814
|
unless nested_scope?
|
@@ -835,7 +832,7 @@ module ActionDispatch
|
|
835
832
|
end
|
836
833
|
|
837
834
|
if options.key? :anchor
|
838
|
-
raise ArgumentError,
|
835
|
+
raise ArgumentError, "anchor is ignored unless passed to `match`"
|
839
836
|
end
|
840
837
|
|
841
838
|
@scope.options.each do |option|
|
@@ -866,20 +863,11 @@ module ActionDispatch
|
|
866
863
|
# controller "food" do
|
867
864
|
# match "bacon", action: :bacon, via: :get
|
868
865
|
# end
|
869
|
-
def controller(controller
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
ensure
|
875
|
-
@scope = @scope.parent
|
876
|
-
end
|
877
|
-
else
|
878
|
-
ActiveSupport::Deprecation.warn "#controller with options is deprecated. If you need to pass more options than the controller name use #scope."
|
879
|
-
|
880
|
-
options[:controller] = controller
|
881
|
-
scope(options) { yield }
|
882
|
-
end
|
866
|
+
def controller(controller)
|
867
|
+
@scope = @scope.new(controller: controller)
|
868
|
+
yield
|
869
|
+
ensure
|
870
|
+
@scope = @scope.parent
|
883
871
|
end
|
884
872
|
|
885
873
|
# Scopes routes to a specific namespace. For example:
|
@@ -991,7 +979,7 @@ module ActionDispatch
|
|
991
979
|
# resources :iphones
|
992
980
|
# end
|
993
981
|
def constraints(constraints = {})
|
994
|
-
scope(:
|
982
|
+
scope(constraints: constraints) { yield }
|
995
983
|
end
|
996
984
|
|
997
985
|
# Allows you to set default parameters for a route, such as this:
|
@@ -1007,65 +995,65 @@ module ActionDispatch
|
|
1007
995
|
end
|
1008
996
|
|
1009
997
|
private
|
1010
|
-
def merge_path_scope(parent, child)
|
998
|
+
def merge_path_scope(parent, child)
|
1011
999
|
Mapper.normalize_path("#{parent}/#{child}")
|
1012
1000
|
end
|
1013
1001
|
|
1014
|
-
def merge_shallow_path_scope(parent, child)
|
1002
|
+
def merge_shallow_path_scope(parent, child)
|
1015
1003
|
Mapper.normalize_path("#{parent}/#{child}")
|
1016
1004
|
end
|
1017
1005
|
|
1018
|
-
def merge_as_scope(parent, child)
|
1006
|
+
def merge_as_scope(parent, child)
|
1019
1007
|
parent ? "#{parent}_#{child}" : child
|
1020
1008
|
end
|
1021
1009
|
|
1022
|
-
def merge_shallow_prefix_scope(parent, child)
|
1010
|
+
def merge_shallow_prefix_scope(parent, child)
|
1023
1011
|
parent ? "#{parent}_#{child}" : child
|
1024
1012
|
end
|
1025
1013
|
|
1026
|
-
def merge_module_scope(parent, child)
|
1014
|
+
def merge_module_scope(parent, child)
|
1027
1015
|
parent ? "#{parent}/#{child}" : child
|
1028
1016
|
end
|
1029
1017
|
|
1030
|
-
def merge_controller_scope(parent, child)
|
1018
|
+
def merge_controller_scope(parent, child)
|
1031
1019
|
child
|
1032
1020
|
end
|
1033
1021
|
|
1034
|
-
def merge_action_scope(parent, child)
|
1022
|
+
def merge_action_scope(parent, child)
|
1035
1023
|
child
|
1036
1024
|
end
|
1037
1025
|
|
1038
|
-
def merge_via_scope(parent, child)
|
1026
|
+
def merge_via_scope(parent, child)
|
1039
1027
|
child
|
1040
1028
|
end
|
1041
1029
|
|
1042
|
-
def merge_format_scope(parent, child)
|
1030
|
+
def merge_format_scope(parent, child)
|
1043
1031
|
child
|
1044
1032
|
end
|
1045
1033
|
|
1046
|
-
def merge_path_names_scope(parent, child)
|
1034
|
+
def merge_path_names_scope(parent, child)
|
1047
1035
|
merge_options_scope(parent, child)
|
1048
1036
|
end
|
1049
1037
|
|
1050
|
-
def merge_constraints_scope(parent, child)
|
1038
|
+
def merge_constraints_scope(parent, child)
|
1051
1039
|
merge_options_scope(parent, child)
|
1052
1040
|
end
|
1053
1041
|
|
1054
|
-
def merge_defaults_scope(parent, child)
|
1042
|
+
def merge_defaults_scope(parent, child)
|
1055
1043
|
merge_options_scope(parent, child)
|
1056
1044
|
end
|
1057
1045
|
|
1058
|
-
def merge_blocks_scope(parent, child)
|
1046
|
+
def merge_blocks_scope(parent, child)
|
1059
1047
|
merged = parent ? parent.dup : []
|
1060
1048
|
merged << child if child
|
1061
1049
|
merged
|
1062
1050
|
end
|
1063
1051
|
|
1064
|
-
def merge_options_scope(parent, child)
|
1052
|
+
def merge_options_scope(parent, child)
|
1065
1053
|
(parent || {}).merge(child)
|
1066
1054
|
end
|
1067
1055
|
|
1068
|
-
def merge_shallow_scope(parent, child)
|
1056
|
+
def merge_shallow_scope(parent, child)
|
1069
1057
|
child ? true : false
|
1070
1058
|
end
|
1071
1059
|
|
@@ -1568,11 +1556,7 @@ module ActionDispatch
|
|
1568
1556
|
options = path
|
1569
1557
|
path, to = options.find { |name, _value| name.is_a?(String) }
|
1570
1558
|
|
1571
|
-
if path.nil?
|
1572
|
-
ActiveSupport::Deprecation.warn 'Omitting the route path is deprecated. '\
|
1573
|
-
'Specify the path with a String or a Symbol instead.'
|
1574
|
-
path = ''
|
1575
|
-
end
|
1559
|
+
raise ArgumentError, "Route path not specified" if path.nil?
|
1576
1560
|
|
1577
1561
|
case to
|
1578
1562
|
when Symbol
|
@@ -1601,63 +1585,6 @@ module ActionDispatch
|
|
1601
1585
|
end
|
1602
1586
|
end
|
1603
1587
|
|
1604
|
-
def get_to_from_path(path, to, action)
|
1605
|
-
return to if to || action
|
1606
|
-
|
1607
|
-
path_without_format = path.sub(/\(\.:format\)$/, '')
|
1608
|
-
if using_match_shorthand?(path_without_format)
|
1609
|
-
path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1').tr("-", "_")
|
1610
|
-
else
|
1611
|
-
nil
|
1612
|
-
end
|
1613
|
-
end
|
1614
|
-
|
1615
|
-
def using_match_shorthand?(path)
|
1616
|
-
path =~ %r{^/?[-\w]+/[-\w/]+$}
|
1617
|
-
end
|
1618
|
-
|
1619
|
-
def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc:
|
1620
|
-
if on = options.delete(:on)
|
1621
|
-
send(on) { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
|
1622
|
-
else
|
1623
|
-
case @scope.scope_level
|
1624
|
-
when :resources
|
1625
|
-
nested { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
|
1626
|
-
when :resource
|
1627
|
-
member { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
|
1628
|
-
else
|
1629
|
-
add_route(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
|
1630
|
-
end
|
1631
|
-
end
|
1632
|
-
end
|
1633
|
-
|
1634
|
-
def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc:
|
1635
|
-
path = path_for_action(action, _path)
|
1636
|
-
raise ArgumentError, "path is required" if path.blank?
|
1637
|
-
|
1638
|
-
action = action.to_s
|
1639
|
-
|
1640
|
-
default_action = options.delete(:action) || @scope[:action]
|
1641
|
-
|
1642
|
-
if action =~ /^[\w\-\/]+$/
|
1643
|
-
default_action ||= action.tr('-', '_') unless action.include?("/")
|
1644
|
-
else
|
1645
|
-
action = nil
|
1646
|
-
end
|
1647
|
-
|
1648
|
-
as = if !options.fetch(:as, true) # if it's set to nil or false
|
1649
|
-
options.delete(:as)
|
1650
|
-
else
|
1651
|
-
name_for_action(options.delete(:as), action)
|
1652
|
-
end
|
1653
|
-
|
1654
|
-
path = Mapping.normalize_path URI.parser.escape(path), formatted
|
1655
|
-
ast = Journey::Parser.parse path
|
1656
|
-
|
1657
|
-
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
|
1658
|
-
@set.add_route(mapping, ast, as, anchor)
|
1659
|
-
end
|
1660
|
-
|
1661
1588
|
# You can specify what Rails should route "/" to with the root method:
|
1662
1589
|
#
|
1663
1590
|
# root to: 'pages#main'
|
@@ -1674,7 +1601,7 @@ module ActionDispatch
|
|
1674
1601
|
def root(path, options = {})
|
1675
1602
|
if path.is_a?(String)
|
1676
1603
|
options[:to] = path
|
1677
|
-
elsif path.is_a?(Hash)
|
1604
|
+
elsif path.is_a?(Hash) && options.empty?
|
1678
1605
|
options = path
|
1679
1606
|
else
|
1680
1607
|
raise ArgumentError, "must be called with a path and/or options"
|
@@ -1691,13 +1618,13 @@ module ActionDispatch
|
|
1691
1618
|
end
|
1692
1619
|
end
|
1693
1620
|
|
1694
|
-
|
1621
|
+
private
|
1695
1622
|
|
1696
|
-
def parent_resource
|
1623
|
+
def parent_resource
|
1697
1624
|
@scope[:scope_level_resource]
|
1698
1625
|
end
|
1699
1626
|
|
1700
|
-
def apply_common_behavior_for(method, resources, options, &block)
|
1627
|
+
def apply_common_behavior_for(method, resources, options, &block)
|
1701
1628
|
if resources.length > 1
|
1702
1629
|
resources.each { |r| send(method, r, options, &block) }
|
1703
1630
|
return true
|
@@ -1730,48 +1657,48 @@ module ActionDispatch
|
|
1730
1657
|
false
|
1731
1658
|
end
|
1732
1659
|
|
1733
|
-
def apply_action_options(options)
|
1660
|
+
def apply_action_options(options)
|
1734
1661
|
return options if action_options? options
|
1735
1662
|
options.merge scope_action_options
|
1736
1663
|
end
|
1737
1664
|
|
1738
|
-
def action_options?(options)
|
1665
|
+
def action_options?(options)
|
1739
1666
|
options[:only] || options[:except]
|
1740
1667
|
end
|
1741
1668
|
|
1742
|
-
def scope_action_options
|
1669
|
+
def scope_action_options
|
1743
1670
|
@scope[:action_options] || {}
|
1744
1671
|
end
|
1745
1672
|
|
1746
|
-
def resource_scope?
|
1673
|
+
def resource_scope?
|
1747
1674
|
@scope.resource_scope?
|
1748
1675
|
end
|
1749
1676
|
|
1750
|
-
def resource_method_scope?
|
1677
|
+
def resource_method_scope?
|
1751
1678
|
@scope.resource_method_scope?
|
1752
1679
|
end
|
1753
1680
|
|
1754
|
-
def nested_scope?
|
1681
|
+
def nested_scope?
|
1755
1682
|
@scope.nested?
|
1756
1683
|
end
|
1757
1684
|
|
1758
|
-
def with_scope_level(kind)
|
1685
|
+
def with_scope_level(kind) # :doc:
|
1759
1686
|
@scope = @scope.new_level(kind)
|
1760
1687
|
yield
|
1761
1688
|
ensure
|
1762
1689
|
@scope = @scope.parent
|
1763
1690
|
end
|
1764
1691
|
|
1765
|
-
def resource_scope(resource)
|
1766
|
-
@scope = @scope.new(:
|
1692
|
+
def resource_scope(resource)
|
1693
|
+
@scope = @scope.new(scope_level_resource: resource)
|
1767
1694
|
|
1768
1695
|
controller(resource.resource_scope) { yield }
|
1769
1696
|
ensure
|
1770
1697
|
@scope = @scope.parent
|
1771
1698
|
end
|
1772
1699
|
|
1773
|
-
def nested_options
|
1774
|
-
options = { :
|
1700
|
+
def nested_options
|
1701
|
+
options = { as: parent_resource.member_name }
|
1775
1702
|
options[:constraints] = {
|
1776
1703
|
parent_resource.nested_param => param_constraint
|
1777
1704
|
} if param_constraint?
|
@@ -1779,27 +1706,27 @@ module ActionDispatch
|
|
1779
1706
|
options
|
1780
1707
|
end
|
1781
1708
|
|
1782
|
-
def shallow_nesting_depth
|
1709
|
+
def shallow_nesting_depth
|
1783
1710
|
@scope.find_all { |node|
|
1784
1711
|
node.frame[:scope_level_resource]
|
1785
1712
|
}.count { |node| node.frame[:scope_level_resource].shallow? }
|
1786
1713
|
end
|
1787
1714
|
|
1788
|
-
def param_constraint?
|
1715
|
+
def param_constraint?
|
1789
1716
|
@scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp)
|
1790
1717
|
end
|
1791
1718
|
|
1792
|
-
def param_constraint
|
1719
|
+
def param_constraint
|
1793
1720
|
@scope[:constraints][parent_resource.param]
|
1794
1721
|
end
|
1795
1722
|
|
1796
|
-
def canonical_action?(action)
|
1723
|
+
def canonical_action?(action)
|
1797
1724
|
resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
|
1798
1725
|
end
|
1799
1726
|
|
1800
|
-
def shallow_scope
|
1801
|
-
scope = { :
|
1802
|
-
:
|
1727
|
+
def shallow_scope
|
1728
|
+
scope = { as: @scope[:shallow_prefix],
|
1729
|
+
path: @scope[:shallow_path] }
|
1803
1730
|
@scope = @scope.new scope
|
1804
1731
|
|
1805
1732
|
yield
|
@@ -1807,7 +1734,7 @@ module ActionDispatch
|
|
1807
1734
|
@scope = @scope.parent
|
1808
1735
|
end
|
1809
1736
|
|
1810
|
-
def path_for_action(action, path)
|
1737
|
+
def path_for_action(action, path)
|
1811
1738
|
return "#{@scope[:path]}/#{path}" if path
|
1812
1739
|
|
1813
1740
|
if canonical_action?(action)
|
@@ -1817,23 +1744,23 @@ module ActionDispatch
|
|
1817
1744
|
end
|
1818
1745
|
end
|
1819
1746
|
|
1820
|
-
def action_path(name)
|
1747
|
+
def action_path(name)
|
1821
1748
|
@scope[:path_names][name.to_sym] || name
|
1822
1749
|
end
|
1823
1750
|
|
1824
|
-
def prefix_name_for_action(as, action)
|
1751
|
+
def prefix_name_for_action(as, action)
|
1825
1752
|
if as
|
1826
1753
|
prefix = as
|
1827
1754
|
elsif !canonical_action?(action)
|
1828
1755
|
prefix = action
|
1829
1756
|
end
|
1830
1757
|
|
1831
|
-
if prefix && prefix !=
|
1832
|
-
Mapper.normalize_name prefix.to_s.tr(
|
1758
|
+
if prefix && prefix != "/" && !prefix.empty?
|
1759
|
+
Mapper.normalize_name prefix.to_s.tr("-", "_")
|
1833
1760
|
end
|
1834
1761
|
end
|
1835
1762
|
|
1836
|
-
def name_for_action(as, action)
|
1763
|
+
def name_for_action(as, action)
|
1837
1764
|
prefix = prefix_name_for_action(as, action)
|
1838
1765
|
name_prefix = @scope[:as]
|
1839
1766
|
|
@@ -1845,7 +1772,7 @@ module ActionDispatch
|
|
1845
1772
|
end
|
1846
1773
|
|
1847
1774
|
action_name = @scope.action_name(name_prefix, prefix, collection_name, member_name)
|
1848
|
-
candidate = action_name.select(&:present?).join(
|
1775
|
+
candidate = action_name.select(&:present?).join("_")
|
1849
1776
|
|
1850
1777
|
unless candidate.empty?
|
1851
1778
|
# If a name was not explicitly given, we check if it is valid
|
@@ -1859,7 +1786,7 @@ module ActionDispatch
|
|
1859
1786
|
end
|
1860
1787
|
end
|
1861
1788
|
|
1862
|
-
def set_member_mappings_for_resource
|
1789
|
+
def set_member_mappings_for_resource # :doc:
|
1863
1790
|
member do
|
1864
1791
|
get :edit if parent_resource.actions.include?(:edit)
|
1865
1792
|
get :show if parent_resource.actions.include?(:show)
|
@@ -1871,77 +1798,121 @@ module ActionDispatch
|
|
1871
1798
|
end
|
1872
1799
|
end
|
1873
1800
|
|
1874
|
-
def api_only?
|
1801
|
+
def api_only? # :doc:
|
1875
1802
|
@set.api_only?
|
1876
1803
|
end
|
1877
1804
|
|
1878
|
-
|
1805
|
+
def path_scope(path)
|
1806
|
+
@scope = @scope.new(path: merge_path_scope(@scope[:path], path))
|
1807
|
+
yield
|
1808
|
+
ensure
|
1809
|
+
@scope = @scope.parent
|
1810
|
+
end
|
1879
1811
|
|
1880
|
-
|
1881
|
-
|
1882
|
-
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1812
|
+
def map_match(paths, options)
|
1813
|
+
if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
|
1814
|
+
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
|
1815
|
+
end
|
1816
|
+
|
1817
|
+
if @scope[:to]
|
1818
|
+
options[:to] ||= @scope[:to]
|
1819
|
+
end
|
1886
1820
|
|
1887
|
-
|
1888
|
-
|
1889
|
-
|
1821
|
+
if @scope[:controller] && @scope[:action]
|
1822
|
+
options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}"
|
1823
|
+
end
|
1824
|
+
|
1825
|
+
controller = options.delete(:controller) || @scope[:controller]
|
1826
|
+
option_path = options.delete :path
|
1827
|
+
to = options.delete :to
|
1828
|
+
via = Mapping.check_via Array(options.delete(:via) {
|
1829
|
+
@scope[:via]
|
1830
|
+
})
|
1831
|
+
formatted = options.delete(:format) { @scope[:format] }
|
1832
|
+
anchor = options.delete(:anchor) { true }
|
1833
|
+
options_constraints = options.delete(:constraints) || {}
|
1834
|
+
|
1835
|
+
path_types = paths.group_by(&:class)
|
1836
|
+
path_types.fetch(String, []).each do |_path|
|
1837
|
+
route_options = options.dup
|
1838
|
+
if _path && option_path
|
1839
|
+
raise ArgumentError, "Ambigous route definition. Both :path and the route path where specified as strings."
|
1840
|
+
end
|
1841
|
+
to = get_to_from_path(_path, to, route_options[:action])
|
1842
|
+
decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
|
1843
|
+
end
|
1844
|
+
|
1845
|
+
path_types.fetch(Symbol, []).each do |action|
|
1846
|
+
route_options = options.dup
|
1847
|
+
decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
|
1848
|
+
end
|
1849
|
+
|
1850
|
+
self
|
1890
1851
|
end
|
1891
1852
|
|
1892
|
-
|
1893
|
-
|
1853
|
+
def get_to_from_path(path, to, action)
|
1854
|
+
return to if to || action
|
1855
|
+
|
1856
|
+
path_without_format = path.sub(/\(\.:format\)$/, "")
|
1857
|
+
if using_match_shorthand?(path_without_format)
|
1858
|
+
path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1').tr("-", "_")
|
1859
|
+
else
|
1860
|
+
nil
|
1861
|
+
end
|
1894
1862
|
end
|
1895
1863
|
|
1896
|
-
|
1897
|
-
|
1864
|
+
def using_match_shorthand?(path)
|
1865
|
+
path =~ %r{^/?[-\w]+/[-\w/]+$}
|
1898
1866
|
end
|
1899
1867
|
|
1900
|
-
|
1901
|
-
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1906
|
-
|
1907
|
-
|
1908
|
-
|
1868
|
+
def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
|
1869
|
+
if on = options.delete(:on)
|
1870
|
+
send(on) { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
|
1871
|
+
else
|
1872
|
+
case @scope.scope_level
|
1873
|
+
when :resources
|
1874
|
+
nested { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
|
1875
|
+
when :resource
|
1876
|
+
member { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
|
1877
|
+
else
|
1878
|
+
add_route(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
|
1879
|
+
end
|
1880
|
+
end
|
1881
|
+
end
|
1909
1882
|
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
if _path && option_path
|
1914
|
-
ActiveSupport::Deprecation.warn <<-eowarn
|
1915
|
-
Specifying strings for both :path and the route path is deprecated. Change things like this:
|
1883
|
+
def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints)
|
1884
|
+
path = path_for_action(action, _path)
|
1885
|
+
raise ArgumentError, "path is required" if path.blank?
|
1916
1886
|
|
1917
|
-
|
1887
|
+
action = action.to_s
|
1918
1888
|
|
1919
|
-
|
1889
|
+
default_action = options.delete(:action) || @scope[:action]
|
1920
1890
|
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
1924
|
-
|
1925
|
-
_path = option_path
|
1891
|
+
if action =~ /^[\w\-\/]+$/
|
1892
|
+
default_action ||= action.tr("-", "_") unless action.include?("/")
|
1893
|
+
else
|
1894
|
+
action = nil
|
1926
1895
|
end
|
1927
|
-
to = get_to_from_path(_path, to, route_options[:action])
|
1928
|
-
decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
|
1929
|
-
end
|
1930
1896
|
|
1931
|
-
|
1932
|
-
|
1933
|
-
|
1934
|
-
|
1897
|
+
as = if !options.fetch(:as, true) # if it's set to nil or false
|
1898
|
+
options.delete(:as)
|
1899
|
+
else
|
1900
|
+
name_for_action(options.delete(:as), action)
|
1901
|
+
end
|
1935
1902
|
|
1936
|
-
|
1937
|
-
|
1903
|
+
path = Mapping.normalize_path URI.parser.escape(path), formatted
|
1904
|
+
ast = Journey::Parser.parse path
|
1938
1905
|
|
1939
|
-
|
1940
|
-
|
1941
|
-
|
1906
|
+
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
|
1907
|
+
@set.add_route(mapping, ast, as, anchor)
|
1908
|
+
end
|
1942
1909
|
|
1943
|
-
|
1944
|
-
|
1910
|
+
def match_root_route(options)
|
1911
|
+
name = has_named_route?(name_for_action(:root, nil)) ? nil : :root
|
1912
|
+
args = ["/", { as: name, via: :get }.merge!(options)]
|
1913
|
+
|
1914
|
+
match(*args)
|
1915
|
+
end
|
1945
1916
|
end
|
1946
1917
|
|
1947
1918
|
# Routing Concerns allow you to declare common routes that can be reused
|
@@ -2049,6 +2020,120 @@ match #{option_path.inspect}, :as => #{_path.inspect}, :action => #{_path.inspec
|
|
2049
2020
|
end
|
2050
2021
|
end
|
2051
2022
|
|
2023
|
+
module CustomUrls
|
2024
|
+
# Define custom url helpers that will be added to the application's
|
2025
|
+
# routes. This allows you to override and/or replace the default behavior
|
2026
|
+
# of routing helpers, e.g:
|
2027
|
+
#
|
2028
|
+
# direct :homepage do
|
2029
|
+
# "http://www.rubyonrails.org"
|
2030
|
+
# end
|
2031
|
+
#
|
2032
|
+
# direct :commentable do |model|
|
2033
|
+
# [ model, anchor: model.dom_id ]
|
2034
|
+
# end
|
2035
|
+
#
|
2036
|
+
# direct :main do
|
2037
|
+
# { controller: "pages", action: "index", subdomain: "www" }
|
2038
|
+
# end
|
2039
|
+
#
|
2040
|
+
# The return value from the block passed to `direct` must be a valid set of
|
2041
|
+
# arguments for `url_for` which will actually build the url string. This can
|
2042
|
+
# be one of the following:
|
2043
|
+
#
|
2044
|
+
# * A string, which is treated as a generated url
|
2045
|
+
# * A hash, e.g. { controller: "pages", action: "index" }
|
2046
|
+
# * An array, which is passed to `polymorphic_url`
|
2047
|
+
# * An Active Model instance
|
2048
|
+
# * An Active Model class
|
2049
|
+
#
|
2050
|
+
# NOTE: Other url helpers can be called in the block but be careful not to invoke
|
2051
|
+
# your custom url helper again otherwise it will result in a stack overflow error
|
2052
|
+
#
|
2053
|
+
# You can also specify default options that will be passed through to
|
2054
|
+
# your url helper definition, e.g:
|
2055
|
+
#
|
2056
|
+
# direct :browse, page: 1, size: 10 do |options|
|
2057
|
+
# [ :products, options.merge(params.permit(:page, :size)) ]
|
2058
|
+
# end
|
2059
|
+
#
|
2060
|
+
# In this instance the `params` object comes from the context in which the the
|
2061
|
+
# block is executed, e.g. generating a url inside a controller action or a view.
|
2062
|
+
# If the block is executed where there isn't a params object such as this:
|
2063
|
+
#
|
2064
|
+
# Rails.application.routes.url_helpers.browse_path
|
2065
|
+
#
|
2066
|
+
# then it will raise a `NameError`. Because of this you need to be aware of the
|
2067
|
+
# context in which you will use your custom url helper when defining it.
|
2068
|
+
#
|
2069
|
+
# NOTE: The `direct` method can't be used inside of a scope block such as
|
2070
|
+
# `namespace` or `scope` and will raise an error if it detects that it is.
|
2071
|
+
def direct(name, options = {}, &block)
|
2072
|
+
unless @scope.root?
|
2073
|
+
raise RuntimeError, "The direct method can't be used inside a routes scope block"
|
2074
|
+
end
|
2075
|
+
|
2076
|
+
@set.add_url_helper(name, options, &block)
|
2077
|
+
end
|
2078
|
+
|
2079
|
+
# Define custom polymorphic mappings of models to urls. This alters the
|
2080
|
+
# behavior of `polymorphic_url` and consequently the behavior of
|
2081
|
+
# `link_to` and `form_for` when passed a model instance, e.g:
|
2082
|
+
#
|
2083
|
+
# resource :basket
|
2084
|
+
#
|
2085
|
+
# resolve "Basket" do
|
2086
|
+
# [:basket]
|
2087
|
+
# end
|
2088
|
+
#
|
2089
|
+
# This will now generate "/basket" when a `Basket` instance is passed to
|
2090
|
+
# `link_to` or `form_for` instead of the standard "/baskets/:id".
|
2091
|
+
#
|
2092
|
+
# NOTE: This custom behavior only applies to simple polymorphic urls where
|
2093
|
+
# a single model instance is passed and not more complicated forms, e.g:
|
2094
|
+
#
|
2095
|
+
# # config/routes.rb
|
2096
|
+
# resource :profile
|
2097
|
+
# namespace :admin do
|
2098
|
+
# resources :users
|
2099
|
+
# end
|
2100
|
+
#
|
2101
|
+
# resolve("User") { [:profile] }
|
2102
|
+
#
|
2103
|
+
# # app/views/application/_menu.html.erb
|
2104
|
+
# link_to "Profile", @current_user
|
2105
|
+
# link_to "Profile", [:admin, @current_user]
|
2106
|
+
#
|
2107
|
+
# The first `link_to` will generate "/profile" but the second will generate
|
2108
|
+
# the standard polymorphic url of "/admin/users/1".
|
2109
|
+
#
|
2110
|
+
# You can pass options to a polymorphic mapping - the arity for the block
|
2111
|
+
# needs to be two as the instance is passed as the first argument, e.g:
|
2112
|
+
#
|
2113
|
+
# resolve "Basket", anchor: "items" do |basket, options|
|
2114
|
+
# [:basket, options]
|
2115
|
+
# end
|
2116
|
+
#
|
2117
|
+
# This generates the url "/basket#items" because when the last item in an
|
2118
|
+
# array passed to `polymorphic_url` is a hash then it's treated as options
|
2119
|
+
# to the url helper that gets called.
|
2120
|
+
#
|
2121
|
+
# NOTE: The `resolve` method can't be used inside of a scope block such as
|
2122
|
+
# `namespace` or `scope` and will raise an error if it detects that it is.
|
2123
|
+
def resolve(*args, &block)
|
2124
|
+
unless @scope.root?
|
2125
|
+
raise RuntimeError, "The resolve method can't be used inside a routes scope block"
|
2126
|
+
end
|
2127
|
+
|
2128
|
+
options = args.extract_options!
|
2129
|
+
args = args.flatten(1)
|
2130
|
+
|
2131
|
+
args.each do |klass|
|
2132
|
+
@set.add_polymorphic_mapping(klass, options, &block)
|
2133
|
+
end
|
2134
|
+
end
|
2135
|
+
end
|
2136
|
+
|
2052
2137
|
class Scope # :nodoc:
|
2053
2138
|
OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module,
|
2054
2139
|
:controller, :action, :path_names, :constraints,
|
@@ -2069,6 +2154,14 @@ match #{option_path.inspect}, :as => #{_path.inspect}, :action => #{_path.inspec
|
|
2069
2154
|
scope_level == :nested
|
2070
2155
|
end
|
2071
2156
|
|
2157
|
+
def null?
|
2158
|
+
@hash.nil? && @parent.nil?
|
2159
|
+
end
|
2160
|
+
|
2161
|
+
def root?
|
2162
|
+
@parent.null?
|
2163
|
+
end
|
2164
|
+
|
2072
2165
|
def resources?
|
2073
2166
|
scope_level == :resources
|
2074
2167
|
end
|
@@ -2119,8 +2212,7 @@ match #{option_path.inspect}, :as => #{_path.inspect}, :action => #{_path.inspec
|
|
2119
2212
|
|
2120
2213
|
def each
|
2121
2214
|
node = self
|
2122
|
-
|
2123
|
-
break if node.equal? NULL
|
2215
|
+
until node.equal? NULL
|
2124
2216
|
yield node
|
2125
2217
|
node = node.parent
|
2126
2218
|
end
|
@@ -2133,7 +2225,7 @@ match #{option_path.inspect}, :as => #{_path.inspect}, :action => #{_path.inspec
|
|
2133
2225
|
|
2134
2226
|
def initialize(set) #:nodoc:
|
2135
2227
|
@set = set
|
2136
|
-
@scope = Scope.new(
|
2228
|
+
@scope = Scope.new(path_names: @set.resources_path_names)
|
2137
2229
|
@concerns = {}
|
2138
2230
|
end
|
2139
2231
|
|
@@ -2143,6 +2235,7 @@ match #{option_path.inspect}, :as => #{_path.inspect}, :action => #{_path.inspec
|
|
2143
2235
|
include Scoping
|
2144
2236
|
include Concerns
|
2145
2237
|
include Resources
|
2238
|
+
include CustomUrls
|
2146
2239
|
end
|
2147
2240
|
end
|
2148
2241
|
end
|