actionpack 4.1.16 → 4.2.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 +4 -4
- data/CHANGELOG.md +163 -690
- data/README.rdoc +7 -2
- data/lib/abstract_controller/base.rb +16 -6
- data/lib/abstract_controller/callbacks.rb +28 -51
- data/lib/abstract_controller/helpers.rb +0 -3
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
- data/lib/abstract_controller/rendering.rb +1 -7
- data/lib/abstract_controller/url_for.rb +1 -1
- data/lib/action_controller.rb +1 -0
- data/lib/action_controller/base.rb +2 -1
- data/lib/action_controller/caching.rb +1 -1
- data/lib/action_controller/caching/fragments.rb +7 -1
- data/lib/action_controller/log_subscriber.rb +26 -25
- data/lib/action_controller/metal.rb +11 -7
- data/lib/action_controller/metal/conditional_get.rb +31 -6
- data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
- data/lib/action_controller/metal/force_ssl.rb +1 -1
- data/lib/action_controller/metal/head.rb +2 -0
- data/lib/action_controller/metal/http_authentication.rb +3 -15
- data/lib/action_controller/metal/instrumentation.rb +4 -7
- data/lib/action_controller/metal/live.rb +57 -6
- data/lib/action_controller/metal/mime_responds.rb +17 -227
- data/lib/action_controller/metal/redirecting.rb +14 -8
- data/lib/action_controller/metal/renderers.rb +19 -3
- data/lib/action_controller/metal/rendering.rb +2 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +75 -7
- data/lib/action_controller/metal/streaming.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +111 -11
- data/lib/action_controller/metal/url_for.rb +11 -12
- data/lib/action_controller/model_naming.rb +1 -1
- data/lib/action_controller/railtie.rb +4 -0
- data/lib/action_controller/test_case.rb +87 -75
- data/lib/action_dispatch/http/cache.rb +1 -1
- data/lib/action_dispatch/http/filter_parameters.rb +2 -2
- data/lib/action_dispatch/http/headers.rb +43 -9
- data/lib/action_dispatch/http/mime_negotiation.rb +10 -4
- data/lib/action_dispatch/http/mime_type.rb +2 -16
- data/lib/action_dispatch/http/parameter_filter.rb +1 -1
- data/lib/action_dispatch/http/parameters.rb +11 -26
- data/lib/action_dispatch/http/request.rb +30 -10
- data/lib/action_dispatch/http/response.rb +52 -17
- data/lib/action_dispatch/http/upload.rb +3 -8
- data/lib/action_dispatch/http/url.rb +87 -70
- data/lib/action_dispatch/journey/formatter.rb +18 -17
- data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +18 -26
- 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 +5 -5
- data/lib/action_dispatch/journey/nodes/node.rb +4 -0
- data/lib/action_dispatch/journey/parser.rb +52 -60
- data/lib/action_dispatch/journey/parser.y +11 -10
- data/lib/action_dispatch/journey/path/pattern.rb +16 -19
- data/lib/action_dispatch/journey/route.rb +3 -18
- data/lib/action_dispatch/journey/router.rb +34 -65
- data/lib/action_dispatch/journey/router/strexp.rb +9 -6
- data/lib/action_dispatch/journey/routes.rb +0 -4
- data/lib/action_dispatch/journey/visitors.rb +81 -92
- data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
- data/lib/action_dispatch/middleware/cookies.rb +27 -31
- data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -3
- data/lib/action_dispatch/middleware/exception_wrapper.rb +19 -17
- data/lib/action_dispatch/middleware/flash.rb +7 -4
- data/lib/action_dispatch/middleware/public_exceptions.rb +13 -8
- data/lib/action_dispatch/middleware/remote_ip.rb +3 -3
- data/lib/action_dispatch/middleware/request_id.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -0
- data/lib/action_dispatch/middleware/static.rb +22 -23
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +22 -18
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +36 -8
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +2 -8
- data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -24
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +0 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +119 -63
- data/lib/action_dispatch/routing/endpoint.rb +10 -0
- data/lib/action_dispatch/routing/inspector.rb +4 -11
- data/lib/action_dispatch/routing/mapper.rb +399 -278
- data/lib/action_dispatch/routing/polymorphic_routes.rb +190 -78
- data/lib/action_dispatch/routing/redirection.rb +10 -12
- data/lib/action_dispatch/routing/route_set.rb +224 -177
- data/lib/action_dispatch/routing/url_for.rb +9 -4
- data/lib/action_dispatch/testing/assertions.rb +11 -7
- data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
- data/lib/action_dispatch/testing/assertions/response.rb +2 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +9 -9
- data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
- data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
- data/lib/action_dispatch/testing/integration.rb +15 -18
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +5 -1
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +57 -15
- data/lib/action_controller/metal/responder.rb +0 -297
@@ -6,130 +6,198 @@ require 'active_support/core_ext/array/extract_options'
|
|
6
6
|
require 'active_support/core_ext/module/remove_method'
|
7
7
|
require 'active_support/inflector'
|
8
8
|
require 'action_dispatch/routing/redirection'
|
9
|
+
require 'action_dispatch/routing/endpoint'
|
10
|
+
require 'active_support/deprecation'
|
9
11
|
|
10
12
|
module ActionDispatch
|
11
13
|
module Routing
|
12
14
|
class Mapper
|
13
15
|
URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
|
14
|
-
SCOPE_OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module,
|
15
|
-
:controller, :action, :path_names, :constraints,
|
16
|
-
:shallow, :blocks, :defaults, :options]
|
17
|
-
|
18
|
-
class Constraints #:nodoc:
|
19
|
-
def self.new(app, constraints, request = Rack::Request)
|
20
|
-
if constraints.any?
|
21
|
-
super(app, constraints, request)
|
22
|
-
else
|
23
|
-
app
|
24
|
-
end
|
25
|
-
end
|
26
16
|
|
17
|
+
class Constraints < Endpoint #:nodoc:
|
27
18
|
attr_reader :app, :constraints
|
28
19
|
|
29
|
-
def initialize(app, constraints,
|
30
|
-
|
20
|
+
def initialize(app, constraints, dispatcher_p)
|
21
|
+
# Unwrap Constraints objects. I don't actually think it's possible
|
22
|
+
# to pass a Constraints object to this constructor, but there were
|
23
|
+
# multiple places that kept testing children of this object. I
|
24
|
+
# *think* they were just being defensive, but I have no idea.
|
25
|
+
if app.is_a?(self.class)
|
26
|
+
constraints += app.constraints
|
27
|
+
app = app.app
|
28
|
+
end
|
29
|
+
|
30
|
+
@dispatcher = dispatcher_p
|
31
|
+
|
32
|
+
@app, @constraints, = app, constraints
|
31
33
|
end
|
32
34
|
|
33
|
-
def
|
34
|
-
req = @request.new(env)
|
35
|
+
def dispatcher?; @dispatcher; end
|
35
36
|
|
37
|
+
def matches?(req)
|
36
38
|
@constraints.all? do |constraint|
|
37
39
|
(constraint.respond_to?(:matches?) && constraint.matches?(req)) ||
|
38
40
|
(constraint.respond_to?(:call) && constraint.call(*constraint_args(constraint, req)))
|
39
41
|
end
|
40
|
-
ensure
|
41
|
-
req.reset_parameters
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
|
44
|
+
def serve(req)
|
45
|
+
return [ 404, {'X-Cascade' => 'pass'}, [] ] unless matches?(req)
|
46
|
+
|
47
|
+
if dispatcher?
|
48
|
+
@app.serve req
|
49
|
+
else
|
50
|
+
@app.call req.env
|
51
|
+
end
|
46
52
|
end
|
47
53
|
|
48
54
|
private
|
49
55
|
def constraint_args(constraint, request)
|
50
|
-
constraint.arity == 1 ? [request] : [request.
|
56
|
+
constraint.arity == 1 ? [request] : [request.path_parameters, request]
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
54
60
|
class Mapping #:nodoc:
|
55
|
-
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix, :format]
|
56
61
|
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
|
57
|
-
WILDCARD_PATH = %r{\*([^/\)]+)\)?$}
|
58
62
|
|
59
|
-
attr_reader :
|
63
|
+
attr_reader :requirements, :conditions, :defaults
|
64
|
+
attr_reader :to, :default_controller, :default_action, :as, :anchor
|
65
|
+
|
66
|
+
def self.build(scope, set, path, as, options)
|
67
|
+
options = scope[:options].merge(options) if scope[:options]
|
68
|
+
|
69
|
+
options.delete :only
|
70
|
+
options.delete :except
|
71
|
+
options.delete :shallow_path
|
72
|
+
options.delete :shallow_prefix
|
73
|
+
options.delete :shallow
|
74
|
+
|
75
|
+
defaults = (scope[:defaults] || {}).merge options.delete(:defaults) || {}
|
76
|
+
|
77
|
+
new scope, set, path, defaults, as, options
|
78
|
+
end
|
79
|
+
|
80
|
+
def initialize(scope, set, path, defaults, as, options)
|
81
|
+
@requirements, @conditions = {}, {}
|
82
|
+
@defaults = defaults
|
83
|
+
@set = set
|
84
|
+
|
85
|
+
@to = options.delete :to
|
86
|
+
@default_controller = options.delete(:controller) || scope[:controller]
|
87
|
+
@default_action = options.delete(:action) || scope[:action]
|
88
|
+
@as = as
|
89
|
+
@anchor = options.delete :anchor
|
90
|
+
|
91
|
+
formatted = options.delete :format
|
92
|
+
via = Array(options.delete(:via) { [] })
|
93
|
+
options_constraints = options.delete :constraints
|
94
|
+
|
95
|
+
path = normalize_path! path, formatted
|
96
|
+
ast = path_ast path
|
97
|
+
path_params = path_params ast
|
98
|
+
|
99
|
+
options = normalize_options!(options, formatted, path_params, ast, scope[:module])
|
60
100
|
|
61
|
-
def initialize(set, scope, path, options)
|
62
|
-
@set, @scope, @path, @options = set, scope, path, options
|
63
|
-
@requirements, @conditions, @defaults = {}, {}, {}
|
64
101
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
102
|
+
split_constraints(path_params, scope[:constraints]) if scope[:constraints]
|
103
|
+
constraints = constraints(options, path_params)
|
104
|
+
|
105
|
+
split_constraints path_params, constraints
|
106
|
+
|
107
|
+
@blocks = blocks(options_constraints, scope[:blocks])
|
108
|
+
|
109
|
+
if options_constraints.is_a?(Hash)
|
110
|
+
split_constraints path_params, options_constraints
|
111
|
+
options_constraints.each do |key, default|
|
112
|
+
if URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
|
113
|
+
@defaults[key] ||= default
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
normalize_format!(formatted)
|
119
|
+
|
120
|
+
@conditions[:path_info] = path
|
121
|
+
@conditions[:parsed_path_info] = ast
|
122
|
+
|
123
|
+
add_request_method(via, @conditions)
|
124
|
+
normalize_defaults!(options)
|
70
125
|
end
|
71
126
|
|
72
127
|
def to_route
|
73
|
-
[ app, conditions, requirements, defaults,
|
128
|
+
[ app(@blocks), conditions, requirements, defaults, as, anchor ]
|
74
129
|
end
|
75
130
|
|
76
131
|
private
|
77
132
|
|
78
|
-
def normalize_path!
|
79
|
-
|
80
|
-
@path = Mapper.normalize_path(@path)
|
133
|
+
def normalize_path!(path, format)
|
134
|
+
path = Mapper.normalize_path(path)
|
81
135
|
|
82
|
-
if
|
83
|
-
|
84
|
-
elsif optional_format?
|
85
|
-
|
136
|
+
if format == true
|
137
|
+
"#{path}.:format"
|
138
|
+
elsif optional_format?(path, format)
|
139
|
+
"#{path}(.:format)"
|
140
|
+
else
|
141
|
+
path
|
86
142
|
end
|
87
143
|
end
|
88
144
|
|
89
|
-
def
|
90
|
-
|
145
|
+
def optional_format?(path, format)
|
146
|
+
format != false && !path.include?(':format') && !path.end_with?('/')
|
91
147
|
end
|
92
148
|
|
93
|
-
def
|
94
|
-
options[:format] != false && !path.include?(':format') && !path.end_with?('/')
|
95
|
-
end
|
96
|
-
|
97
|
-
def normalize_options!
|
98
|
-
@options.reverse_merge!(scope[:options]) if scope[:options]
|
99
|
-
path_without_format = path.sub(/\(\.:format\)$/, '')
|
100
|
-
|
149
|
+
def normalize_options!(options, formatted, path_params, path_ast, modyoule)
|
101
150
|
# Add a constraint for wildcard route to make it non-greedy and match the
|
102
151
|
# optional format part of the route by default
|
103
|
-
if
|
104
|
-
|
152
|
+
if formatted != false
|
153
|
+
path_ast.grep(Journey::Nodes::Star) do |node|
|
154
|
+
options[node.name.to_sym] ||= /.+?/
|
155
|
+
end
|
105
156
|
end
|
106
157
|
|
107
|
-
if
|
108
|
-
raise ArgumentError, ":controller segment is not allowed within a namespace block" if
|
158
|
+
if path_params.include?(:controller)
|
159
|
+
raise ArgumentError, ":controller segment is not allowed within a namespace block" if modyoule
|
109
160
|
|
110
161
|
# Add a default constraint for :controller path segments that matches namespaced
|
111
162
|
# controllers with default routes like :controller/:action/:id(.:format), e.g:
|
112
163
|
# GET /admin/products/show/1
|
113
164
|
# => { controller: 'admin/products', action: 'show', id: '1' }
|
114
|
-
|
165
|
+
options[:controller] ||= /.+?/
|
115
166
|
end
|
116
167
|
|
117
|
-
|
168
|
+
if to.respond_to? :call
|
169
|
+
options
|
170
|
+
else
|
171
|
+
to_endpoint = split_to to
|
172
|
+
controller = to_endpoint[0] || default_controller
|
173
|
+
action = to_endpoint[1] || default_action
|
174
|
+
|
175
|
+
controller = add_controller_module(controller, modyoule)
|
176
|
+
|
177
|
+
options.merge! check_controller_and_action(path_params, controller, action)
|
178
|
+
end
|
118
179
|
end
|
119
180
|
|
120
|
-
def
|
121
|
-
constraints.
|
122
|
-
|
123
|
-
|
124
|
-
|
181
|
+
def split_constraints(path_params, constraints)
|
182
|
+
constraints.each_pair do |key, requirement|
|
183
|
+
if path_params.include?(key) || key == :controller
|
184
|
+
verify_regexp_requirement(requirement) if requirement.is_a?(Regexp)
|
185
|
+
@requirements[key] = requirement
|
186
|
+
else
|
187
|
+
@conditions[key] = requirement
|
188
|
+
end
|
125
189
|
end
|
190
|
+
end
|
126
191
|
|
127
|
-
|
192
|
+
def normalize_format!(formatted)
|
193
|
+
if formatted == true
|
128
194
|
@requirements[:format] ||= /.+/
|
129
|
-
elsif Regexp ===
|
130
|
-
@requirements[:format] =
|
131
|
-
|
132
|
-
|
195
|
+
elsif Regexp === formatted
|
196
|
+
@requirements[:format] = formatted
|
197
|
+
@defaults[:format] = nil
|
198
|
+
elsif String === formatted
|
199
|
+
@requirements[:format] = Regexp.compile(formatted)
|
200
|
+
@defaults[:format] = formatted
|
133
201
|
end
|
134
202
|
end
|
135
203
|
|
@@ -143,169 +211,143 @@ module ActionDispatch
|
|
143
211
|
end
|
144
212
|
end
|
145
213
|
|
146
|
-
def normalize_defaults!
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
options.each do |key, default|
|
151
|
-
unless Regexp === default || IGNORE_OPTIONS.include?(key)
|
214
|
+
def normalize_defaults!(options)
|
215
|
+
options.each_pair do |key, default|
|
216
|
+
unless Regexp === default
|
152
217
|
@defaults[key] = default
|
153
218
|
end
|
154
219
|
end
|
155
|
-
|
156
|
-
if options[:constraints].is_a?(Hash)
|
157
|
-
options[:constraints].each do |key, default|
|
158
|
-
if URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
|
159
|
-
@defaults[key] ||= default
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
if Regexp === options[:format]
|
165
|
-
@defaults[:format] = nil
|
166
|
-
elsif String === options[:format]
|
167
|
-
@defaults[:format] = options[:format]
|
168
|
-
end
|
169
220
|
end
|
170
221
|
|
171
|
-
def
|
172
|
-
|
173
|
-
|
174
|
-
constraints.each do |key, condition|
|
175
|
-
unless segment_keys.include?(key) || key == :controller
|
176
|
-
@conditions[key] = condition
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
required_defaults = []
|
181
|
-
options.each do |key, required_default|
|
182
|
-
unless segment_keys.include?(key) || IGNORE_OPTIONS.include?(key) || Regexp === required_default
|
183
|
-
required_defaults << key
|
184
|
-
end
|
222
|
+
def verify_callable_constraint(callable_constraint)
|
223
|
+
unless callable_constraint.respond_to?(:call) || callable_constraint.respond_to?(:matches?)
|
224
|
+
raise ArgumentError, "Invalid constraint: #{callable_constraint.inspect} must respond to :call or :matches?"
|
185
225
|
end
|
186
|
-
|
226
|
+
end
|
187
227
|
|
188
|
-
|
228
|
+
def add_request_method(via, conditions)
|
229
|
+
return if via == [:all]
|
189
230
|
|
190
|
-
if
|
231
|
+
if via.empty?
|
191
232
|
msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
|
192
233
|
"If you want to expose your action to both GET and POST, add `via: [:get, :post]` option.\n" \
|
193
234
|
"If you want to expose your action to GET, use `get` in the router:\n" \
|
194
235
|
" Instead of: match \"controller#action\"\n" \
|
195
236
|
" Do: get \"controller#action\""
|
196
|
-
raise msg
|
237
|
+
raise ArgumentError, msg
|
197
238
|
end
|
198
239
|
|
199
|
-
|
200
|
-
@conditions[:request_method] = Array(via).map { |m| m.to_s.dasherize.upcase }
|
201
|
-
end
|
240
|
+
conditions[:request_method] = via.map { |m| m.to_s.dasherize.upcase }
|
202
241
|
end
|
203
242
|
|
204
|
-
def app
|
205
|
-
|
206
|
-
end
|
243
|
+
def app(blocks)
|
244
|
+
return to if Redirect === to
|
207
245
|
|
208
|
-
def default_controller_and_action
|
209
246
|
if to.respond_to?(:call)
|
210
|
-
|
247
|
+
Constraints.new(to, blocks, false)
|
211
248
|
else
|
212
|
-
if
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
end
|
217
|
-
|
218
|
-
controller ||= default_controller
|
219
|
-
action ||= default_action
|
220
|
-
|
221
|
-
if @scope[:module] && !controller.is_a?(Regexp)
|
222
|
-
if controller =~ %r{\A/}
|
223
|
-
controller = controller[1..-1]
|
224
|
-
else
|
225
|
-
controller = [@scope[:module], controller].compact.join("/").presence
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
if controller.is_a?(String) && controller =~ %r{\A/}
|
230
|
-
raise ArgumentError, "controller name should not start with a slash"
|
249
|
+
if blocks.any?
|
250
|
+
Constraints.new(dispatcher(defaults), blocks, true)
|
251
|
+
else
|
252
|
+
dispatcher(defaults)
|
231
253
|
end
|
254
|
+
end
|
255
|
+
end
|
232
256
|
|
233
|
-
|
234
|
-
|
257
|
+
def check_controller_and_action(path_params, controller, action)
|
258
|
+
hash = check_part(:controller, controller, path_params, {}) do |part|
|
259
|
+
translate_controller(part) {
|
260
|
+
message = "'#{part}' is not a supported controller name. This can lead to potential routing problems."
|
261
|
+
message << " See http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use"
|
235
262
|
|
236
|
-
if controller.blank? && segment_keys.exclude?(:controller)
|
237
|
-
message = "Missing :controller key on routes definition, please check your routes."
|
238
263
|
raise ArgumentError, message
|
239
|
-
|
264
|
+
}
|
265
|
+
end
|
240
266
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
267
|
+
check_part(:action, action, path_params, hash) { |part|
|
268
|
+
part.is_a?(Regexp) ? part : part.to_s
|
269
|
+
}
|
270
|
+
end
|
245
271
|
|
246
|
-
|
247
|
-
|
248
|
-
|
272
|
+
def check_part(name, part, path_params, hash)
|
273
|
+
if part
|
274
|
+
hash[name] = yield(part)
|
275
|
+
else
|
276
|
+
unless path_params.include?(name)
|
277
|
+
message = "Missing :#{name} key on routes definition, please check your routes."
|
249
278
|
raise ArgumentError, message
|
250
279
|
end
|
251
|
-
|
252
|
-
hash = {}
|
253
|
-
hash[:controller] = controller unless controller.blank?
|
254
|
-
hash[:action] = action unless action.blank?
|
255
|
-
hash
|
256
280
|
end
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
281
|
+
hash
|
282
|
+
end
|
283
|
+
|
284
|
+
def split_to(to)
|
285
|
+
case to
|
286
|
+
when Symbol
|
287
|
+
ActiveSupport::Deprecation.warn "defining a route where `to` is a symbol is deprecated. Please change \"to: :#{to}\" to \"action: :#{to}\""
|
288
|
+
[nil, to.to_s]
|
289
|
+
when /#/ then to.split('#')
|
290
|
+
when String
|
291
|
+
ActiveSupport::Deprecation.warn "defining a route where `to` is a controller without an action is deprecated. Please change \"to: :#{to}\" to \"controller: :#{to}\""
|
292
|
+
[to, nil]
|
262
293
|
else
|
263
|
-
|
294
|
+
[]
|
264
295
|
end
|
265
296
|
end
|
266
297
|
|
267
|
-
def
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
298
|
+
def add_controller_module(controller, modyoule)
|
299
|
+
if modyoule && !controller.is_a?(Regexp)
|
300
|
+
if controller =~ %r{\A/}
|
301
|
+
controller[1..-1]
|
302
|
+
else
|
303
|
+
[modyoule, controller].compact.join("/")
|
273
304
|
end
|
274
|
-
|
275
|
-
|
305
|
+
else
|
306
|
+
controller
|
276
307
|
end
|
277
308
|
end
|
278
309
|
|
279
|
-
def
|
280
|
-
|
281
|
-
|
310
|
+
def translate_controller(controller)
|
311
|
+
return controller if Regexp === controller
|
312
|
+
return controller.to_s if controller =~ /\A[a-z_0-9][a-z_0-9\/]*\z/
|
282
313
|
|
283
|
-
|
284
|
-
Journey::Path::Pattern.new(strexp)
|
285
|
-
end
|
286
|
-
|
287
|
-
def strexp
|
288
|
-
Journey::Router::Strexp.compile(path, requirements, SEPARATORS)
|
314
|
+
yield
|
289
315
|
end
|
290
316
|
|
291
|
-
def
|
292
|
-
|
317
|
+
def blocks(options_constraints, scope_blocks)
|
318
|
+
if options_constraints && !options_constraints.is_a?(Hash)
|
319
|
+
verify_callable_constraint(options_constraints)
|
320
|
+
[options_constraints]
|
321
|
+
else
|
322
|
+
scope_blocks || []
|
323
|
+
end
|
293
324
|
end
|
294
325
|
|
295
|
-
def
|
296
|
-
|
326
|
+
def constraints(options, path_params)
|
327
|
+
constraints = {}
|
328
|
+
required_defaults = []
|
329
|
+
options.each_pair do |key, option|
|
330
|
+
if Regexp === option
|
331
|
+
constraints[key] = option
|
332
|
+
else
|
333
|
+
required_defaults << key unless path_params.include?(key)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
@conditions[:required_defaults] = required_defaults
|
337
|
+
constraints
|
297
338
|
end
|
298
339
|
|
299
|
-
def
|
300
|
-
|
340
|
+
def path_params(ast)
|
341
|
+
ast.grep(Journey::Nodes::Symbol).map { |n| n.name.to_sym }
|
301
342
|
end
|
302
343
|
|
303
|
-
def
|
304
|
-
|
344
|
+
def path_ast(path)
|
345
|
+
parser = Journey::Parser.new
|
346
|
+
parser.parse path
|
305
347
|
end
|
306
348
|
|
307
|
-
def
|
308
|
-
|
349
|
+
def dispatcher(defaults)
|
350
|
+
@set.dispatcher defaults
|
309
351
|
end
|
310
352
|
end
|
311
353
|
|
@@ -346,13 +388,12 @@ module ActionDispatch
|
|
346
388
|
# without specifying an HTTP method.
|
347
389
|
#
|
348
390
|
# If you want to expose your action to both GET and POST, use:
|
349
|
-
#
|
391
|
+
#
|
350
392
|
# # sets :controller, :action and :id in params
|
351
393
|
# match ':controller/:action/:id', via: [:get, :post]
|
352
394
|
#
|
353
|
-
# Note that +:controller+, +:action
|
354
|
-
# parameters and thus available
|
355
|
-
# in an action.
|
395
|
+
# Note that +:controller+, +:action+ and +:id+ are interpreted as url
|
396
|
+
# query parameters and thus available through +params+ in an action.
|
356
397
|
#
|
357
398
|
# If you want to expose your action to GET, use `get` in the router:
|
358
399
|
#
|
@@ -374,20 +415,24 @@ module ActionDispatch
|
|
374
415
|
# # params[:category] = 'rock/classic'
|
375
416
|
# # params[:title] = 'stairway-to-heaven'
|
376
417
|
#
|
418
|
+
# To match a wildcard parameter, it must have a name assigned to it.
|
419
|
+
# Without a variable name to attach the glob parameter to, the route
|
420
|
+
# can't be parsed.
|
421
|
+
#
|
377
422
|
# When a pattern points to an internal route, the route's +:action+ and
|
378
423
|
# +:controller+ should be set in options or hash shorthand. Examples:
|
379
424
|
#
|
380
|
-
# match 'photos/:id' => 'photos#show', via:
|
381
|
-
# match 'photos/:id', to: 'photos#show', via:
|
382
|
-
# match 'photos/:id', controller: 'photos', action: 'show', via:
|
425
|
+
# match 'photos/:id' => 'photos#show', via: :get
|
426
|
+
# match 'photos/:id', to: 'photos#show', via: :get
|
427
|
+
# match 'photos/:id', controller: 'photos', action: 'show', via: :get
|
383
428
|
#
|
384
429
|
# A pattern can also point to a +Rack+ endpoint i.e. anything that
|
385
430
|
# responds to +call+:
|
386
431
|
#
|
387
|
-
# match 'photos/:id', to: lambda {|hash| [200, {}, ["Coming soon"]] }, via:
|
388
|
-
# match 'photos/:id', to: PhotoRackApp, via:
|
432
|
+
# match 'photos/:id', to: lambda {|hash| [200, {}, ["Coming soon"]] }, via: :get
|
433
|
+
# match 'photos/:id', to: PhotoRackApp, via: :get
|
389
434
|
# # Yes, controller actions are just rack endpoints
|
390
|
-
# match 'photos/:id', to: PhotosController.action(:show), via:
|
435
|
+
# match 'photos/:id', to: PhotosController.action(:show), via: :get
|
391
436
|
#
|
392
437
|
# Because requesting various HTTP verbs with a single action has security
|
393
438
|
# implications, you must either specify the actions in
|
@@ -416,7 +461,7 @@ module ActionDispatch
|
|
416
461
|
# [:module]
|
417
462
|
# The namespace for :controller.
|
418
463
|
#
|
419
|
-
# match 'path', to: 'c#a', module: 'sekret', controller: 'posts', via:
|
464
|
+
# match 'path', to: 'c#a', module: 'sekret', controller: 'posts', via: :get
|
420
465
|
# # => Sekret::PostsController
|
421
466
|
#
|
422
467
|
# See <tt>Scoping#namespace</tt> for its scope equivalent.
|
@@ -435,9 +480,9 @@ module ActionDispatch
|
|
435
480
|
# Points to a +Rack+ endpoint. Can be an object that responds to
|
436
481
|
# +call+ or a string representing a controller's action.
|
437
482
|
#
|
438
|
-
# match 'path', to: 'controller#action', via:
|
439
|
-
# match 'path', to: lambda { |env| [200, {}, ["Success!"]] }, via:
|
440
|
-
# match 'path', to: RackApp, via:
|
483
|
+
# match 'path', to: 'controller#action', via: :get
|
484
|
+
# match 'path', to: lambda { |env| [200, {}, ["Success!"]] }, via: :get
|
485
|
+
# match 'path', to: RackApp, via: :get
|
441
486
|
#
|
442
487
|
# [:on]
|
443
488
|
# Shorthand for wrapping routes in a specific RESTful context. Valid
|
@@ -462,14 +507,14 @@ module ActionDispatch
|
|
462
507
|
# other than path can also be specified with any object
|
463
508
|
# that responds to <tt>===</tt> (eg. String, Array, Range, etc.).
|
464
509
|
#
|
465
|
-
# match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }, via:
|
510
|
+
# match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }, via: :get
|
466
511
|
#
|
467
|
-
# match 'json_only', constraints: { format: 'json' }, via:
|
512
|
+
# match 'json_only', constraints: { format: 'json' }, via: :get
|
468
513
|
#
|
469
514
|
# class Whitelist
|
470
515
|
# def matches?(request) request.remote_ip == '1.2.3.4' end
|
471
516
|
# end
|
472
|
-
# match 'path', to: 'c#a', constraints: Whitelist.new, via:
|
517
|
+
# match 'path', to: 'c#a', constraints: Whitelist.new, via: :get
|
473
518
|
#
|
474
519
|
# See <tt>Scoping#constraints</tt> for more examples with its scope
|
475
520
|
# equivalent.
|
@@ -478,7 +523,7 @@ module ActionDispatch
|
|
478
523
|
# Sets defaults for parameters
|
479
524
|
#
|
480
525
|
# # Sets params[:format] to 'jpg' by default
|
481
|
-
# match 'path', to: 'c#a', defaults: { format: 'jpg' }, via:
|
526
|
+
# match 'path', to: 'c#a', defaults: { format: 'jpg' }, via: :get
|
482
527
|
#
|
483
528
|
# See <tt>Scoping#defaults</tt> for its scope equivalent.
|
484
529
|
#
|
@@ -487,7 +532,7 @@ module ActionDispatch
|
|
487
532
|
# false, the pattern matches any request prefixed with the given path.
|
488
533
|
#
|
489
534
|
# # Matches any request starting with 'path'
|
490
|
-
# match 'path', to: 'c#a', anchor: false, via:
|
535
|
+
# match 'path', to: 'c#a', anchor: false, via: :get
|
491
536
|
#
|
492
537
|
# [:format]
|
493
538
|
# Allows you to specify the default value for optional +format+
|
@@ -529,13 +574,21 @@ module ActionDispatch
|
|
529
574
|
|
530
575
|
raise "A rack application must be specified" unless path
|
531
576
|
|
532
|
-
|
577
|
+
rails_app = rails_app? app
|
578
|
+
|
579
|
+
if rails_app
|
580
|
+
options[:as] ||= app.railtie_name
|
581
|
+
else
|
582
|
+
# non rails apps can't have an :as
|
583
|
+
options[:as] = nil
|
584
|
+
end
|
585
|
+
|
533
586
|
target_as = name_for_action(options[:as], path)
|
534
587
|
options[:via] ||= :all
|
535
588
|
|
536
589
|
match(path, options.merge(:to => app, :anchor => false, :format => false))
|
537
590
|
|
538
|
-
define_generate_prefix(app, target_as)
|
591
|
+
define_generate_prefix(app, target_as) if rails_app
|
539
592
|
self
|
540
593
|
end
|
541
594
|
|
@@ -556,35 +609,27 @@ module ActionDispatch
|
|
556
609
|
end
|
557
610
|
|
558
611
|
private
|
559
|
-
def
|
560
|
-
|
561
|
-
|
562
|
-
if app.respond_to?(:railtie_name)
|
563
|
-
app.railtie_name
|
564
|
-
else
|
565
|
-
class_name = app.class.is_a?(Class) ? app.name : app.class.name
|
566
|
-
ActiveSupport::Inflector.underscore(class_name).tr("/", "_")
|
567
|
-
end
|
612
|
+
def rails_app?(app)
|
613
|
+
app.is_a?(Class) && app < Rails::Railtie
|
568
614
|
end
|
569
615
|
|
570
616
|
def define_generate_prefix(app, name)
|
571
|
-
|
572
|
-
|
573
|
-
_route = @set.named_routes.routes[name.to_sym]
|
617
|
+
_route = @set.named_routes.get name
|
574
618
|
_routes = @set
|
575
619
|
app.routes.define_mounted_helper(name)
|
576
|
-
app.routes.
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
620
|
+
app.routes.extend Module.new {
|
621
|
+
def optimize_routes_generation?; false; end
|
622
|
+
define_method :find_script_name do |options|
|
623
|
+
if options.key? :script_name
|
624
|
+
super(options)
|
625
|
+
else
|
626
|
+
prefix_options = options.slice(*_route.segment_keys)
|
627
|
+
# we must actually delete prefix segment keys to avoid passing them to next url_for
|
628
|
+
_route.segment_keys.each { |k| options.delete(k) }
|
629
|
+
_routes.url_helpers.send("#{name}_path", prefix_options)
|
630
|
+
end
|
586
631
|
end
|
587
|
-
|
632
|
+
}
|
588
633
|
end
|
589
634
|
end
|
590
635
|
|
@@ -671,7 +716,7 @@ module ActionDispatch
|
|
671
716
|
# resources :posts, module: "admin"
|
672
717
|
#
|
673
718
|
# If you want to route /admin/posts to +PostsController+
|
674
|
-
# (without the Admin
|
719
|
+
# (without the <tt>Admin::</tt> module prefix), you could use
|
675
720
|
#
|
676
721
|
# scope "/admin" do
|
677
722
|
# resources :posts
|
@@ -725,7 +770,7 @@ module ActionDispatch
|
|
725
770
|
# end
|
726
771
|
def scope(*args)
|
727
772
|
options = args.extract_options!.dup
|
728
|
-
|
773
|
+
scope = {}
|
729
774
|
|
730
775
|
options[:path] = args.flatten.join('/') if args.any?
|
731
776
|
options[:constraints] ||= {}
|
@@ -745,7 +790,7 @@ module ActionDispatch
|
|
745
790
|
block, options[:constraints] = options[:constraints], {}
|
746
791
|
end
|
747
792
|
|
748
|
-
|
793
|
+
@scope.options.each do |option|
|
749
794
|
if option == :blocks
|
750
795
|
value = block
|
751
796
|
elsif option == :options
|
@@ -755,15 +800,15 @@ module ActionDispatch
|
|
755
800
|
end
|
756
801
|
|
757
802
|
if value
|
758
|
-
|
759
|
-
@scope[option] = send("merge_#{option}_scope", @scope[option], value)
|
803
|
+
scope[option] = send("merge_#{option}_scope", @scope[option], value)
|
760
804
|
end
|
761
805
|
end
|
762
806
|
|
807
|
+
@scope = @scope.new scope
|
763
808
|
yield
|
764
809
|
self
|
765
810
|
ensure
|
766
|
-
@scope.
|
811
|
+
@scope = @scope.parent
|
767
812
|
end
|
768
813
|
|
769
814
|
# Scopes routes to a specific controller
|
@@ -1001,8 +1046,6 @@ module ActionDispatch
|
|
1001
1046
|
VALID_ON_OPTIONS = [:new, :collection, :member]
|
1002
1047
|
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
|
1003
1048
|
CANONICAL_ACTIONS = %w(index create new show update destroy)
|
1004
|
-
RESOURCE_METHOD_SCOPES = [:collection, :member, :new]
|
1005
|
-
RESOURCE_SCOPES = [:resource, :resources]
|
1006
1049
|
|
1007
1050
|
class Resource #:nodoc:
|
1008
1051
|
attr_reader :controller, :path, :options, :param
|
@@ -1422,7 +1465,20 @@ module ActionDispatch
|
|
1422
1465
|
if rest.empty? && Hash === path
|
1423
1466
|
options = path
|
1424
1467
|
path, to = options.find { |name, _value| name.is_a?(String) }
|
1425
|
-
|
1468
|
+
|
1469
|
+
case to
|
1470
|
+
when Symbol
|
1471
|
+
options[:action] = to
|
1472
|
+
when String
|
1473
|
+
if to =~ /#/
|
1474
|
+
options[:to] = to
|
1475
|
+
else
|
1476
|
+
options[:controller] = to
|
1477
|
+
end
|
1478
|
+
else
|
1479
|
+
options[:to] = to
|
1480
|
+
end
|
1481
|
+
|
1426
1482
|
options.delete(path)
|
1427
1483
|
paths = [path]
|
1428
1484
|
else
|
@@ -1463,7 +1519,7 @@ module ActionDispatch
|
|
1463
1519
|
if on = options.delete(:on)
|
1464
1520
|
send(on) { decomposed_match(path, options) }
|
1465
1521
|
else
|
1466
|
-
case @scope
|
1522
|
+
case @scope.scope_level
|
1467
1523
|
when :resources
|
1468
1524
|
nested { decomposed_match(path, options) }
|
1469
1525
|
when :resource
|
@@ -1476,6 +1532,8 @@ module ActionDispatch
|
|
1476
1532
|
|
1477
1533
|
def add_route(action, options) # :nodoc:
|
1478
1534
|
path = path_for_action(action, options.delete(:path))
|
1535
|
+
raise ArgumentError, "path is required" if path.blank?
|
1536
|
+
|
1479
1537
|
action = action.to_s.dup
|
1480
1538
|
|
1481
1539
|
if action =~ /^[\w\-\/]+$/
|
@@ -1484,13 +1542,13 @@ module ActionDispatch
|
|
1484
1542
|
action = nil
|
1485
1543
|
end
|
1486
1544
|
|
1487
|
-
if !options.fetch(:as, true)
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1545
|
+
as = if !options.fetch(:as, true) # if it's set to nil or false
|
1546
|
+
options.delete(:as)
|
1547
|
+
else
|
1548
|
+
name_for_action(options.delete(:as), action)
|
1549
|
+
end
|
1492
1550
|
|
1493
|
-
mapping = Mapping.
|
1551
|
+
mapping = Mapping.build(@scope, @set, URI.parser.escape(path), as, options)
|
1494
1552
|
app, conditions, requirements, defaults, as, anchor = mapping.to_route
|
1495
1553
|
@set.add_route(app, conditions, requirements, defaults, as, anchor)
|
1496
1554
|
end
|
@@ -1504,7 +1562,7 @@ module ActionDispatch
|
|
1504
1562
|
raise ArgumentError, "must be called with a path and/or options"
|
1505
1563
|
end
|
1506
1564
|
|
1507
|
-
if @scope
|
1565
|
+
if @scope.resources?
|
1508
1566
|
with_scope_level(:root) do
|
1509
1567
|
scope(parent_resource.path) do
|
1510
1568
|
super(options)
|
@@ -1571,40 +1629,39 @@ module ActionDispatch
|
|
1571
1629
|
end
|
1572
1630
|
|
1573
1631
|
def resource_scope? #:nodoc:
|
1574
|
-
|
1632
|
+
@scope.resource_scope?
|
1575
1633
|
end
|
1576
1634
|
|
1577
1635
|
def resource_method_scope? #:nodoc:
|
1578
|
-
|
1636
|
+
@scope.resource_method_scope?
|
1579
1637
|
end
|
1580
1638
|
|
1581
1639
|
def nested_scope? #:nodoc:
|
1582
|
-
@scope
|
1640
|
+
@scope.nested?
|
1583
1641
|
end
|
1584
1642
|
|
1585
1643
|
def with_exclusive_scope
|
1586
1644
|
begin
|
1587
|
-
|
1588
|
-
@scope[:as], @scope[:path] = nil, nil
|
1645
|
+
@scope = @scope.new(:as => nil, :path => nil)
|
1589
1646
|
|
1590
1647
|
with_scope_level(:exclusive) do
|
1591
1648
|
yield
|
1592
1649
|
end
|
1593
1650
|
ensure
|
1594
|
-
@scope
|
1651
|
+
@scope = @scope.parent
|
1595
1652
|
end
|
1596
1653
|
end
|
1597
1654
|
|
1598
1655
|
def with_scope_level(kind)
|
1599
|
-
|
1656
|
+
@scope = @scope.new_level(kind)
|
1600
1657
|
yield
|
1601
1658
|
ensure
|
1602
|
-
@scope
|
1659
|
+
@scope = @scope.parent
|
1603
1660
|
end
|
1604
1661
|
|
1605
1662
|
def resource_scope(kind, resource) #:nodoc:
|
1606
1663
|
resource.shallow = @scope[:shallow]
|
1607
|
-
|
1664
|
+
@scope = @scope.new(:scope_level_resource => resource)
|
1608
1665
|
@nesting.push(resource)
|
1609
1666
|
|
1610
1667
|
with_scope_level(kind) do
|
@@ -1612,7 +1669,7 @@ module ActionDispatch
|
|
1612
1669
|
end
|
1613
1670
|
ensure
|
1614
1671
|
@nesting.pop
|
1615
|
-
@scope
|
1672
|
+
@scope = @scope.parent
|
1616
1673
|
end
|
1617
1674
|
|
1618
1675
|
def nested_options #:nodoc:
|
@@ -1640,21 +1697,22 @@ module ActionDispatch
|
|
1640
1697
|
@scope[:constraints][parent_resource.param]
|
1641
1698
|
end
|
1642
1699
|
|
1643
|
-
def canonical_action?(action
|
1644
|
-
|
1700
|
+
def canonical_action?(action) #:nodoc:
|
1701
|
+
resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
|
1645
1702
|
end
|
1646
1703
|
|
1647
1704
|
def shallow_scope(path, options = {}) #:nodoc:
|
1648
|
-
|
1649
|
-
|
1705
|
+
scope = { :as => @scope[:shallow_prefix],
|
1706
|
+
:path => @scope[:shallow_path] }
|
1707
|
+
@scope = @scope.new scope
|
1650
1708
|
|
1651
1709
|
scope(path, options) { yield }
|
1652
1710
|
ensure
|
1653
|
-
@scope
|
1711
|
+
@scope = @scope.parent
|
1654
1712
|
end
|
1655
1713
|
|
1656
1714
|
def path_for_action(action, path) #:nodoc:
|
1657
|
-
if canonical_action?(action
|
1715
|
+
if path.blank? && canonical_action?(action)
|
1658
1716
|
@scope[:path].to_s
|
1659
1717
|
else
|
1660
1718
|
"#{@scope[:path]}/#{action_path(action, path)}"
|
@@ -1669,15 +1727,17 @@ module ActionDispatch
|
|
1669
1727
|
def prefix_name_for_action(as, action) #:nodoc:
|
1670
1728
|
if as
|
1671
1729
|
prefix = as
|
1672
|
-
elsif !canonical_action?(action
|
1730
|
+
elsif !canonical_action?(action)
|
1673
1731
|
prefix = action
|
1674
1732
|
end
|
1675
|
-
|
1733
|
+
|
1734
|
+
if prefix && prefix != '/' && !prefix.empty?
|
1735
|
+
Mapper.normalize_name prefix.to_s.tr('-', '_')
|
1736
|
+
end
|
1676
1737
|
end
|
1677
1738
|
|
1678
1739
|
def name_for_action(as, action) #:nodoc:
|
1679
1740
|
prefix = prefix_name_for_action(as, action)
|
1680
|
-
prefix = Mapper.normalize_name(prefix) if prefix
|
1681
1741
|
name_prefix = @scope[:as]
|
1682
1742
|
|
1683
1743
|
if parent_resource
|
@@ -1687,27 +1747,14 @@ module ActionDispatch
|
|
1687
1747
|
member_name = parent_resource.member_name
|
1688
1748
|
end
|
1689
1749
|
|
1690
|
-
name =
|
1691
|
-
when :nested
|
1692
|
-
[name_prefix, prefix]
|
1693
|
-
when :collection
|
1694
|
-
[prefix, name_prefix, collection_name]
|
1695
|
-
when :new
|
1696
|
-
[prefix, :new, name_prefix, member_name]
|
1697
|
-
when :member
|
1698
|
-
[prefix, name_prefix, member_name]
|
1699
|
-
when :root
|
1700
|
-
[name_prefix, collection_name, prefix]
|
1701
|
-
else
|
1702
|
-
[name_prefix, member_name, prefix]
|
1703
|
-
end
|
1750
|
+
name = @scope.action_name(name_prefix, prefix, collection_name, member_name)
|
1704
1751
|
|
1705
|
-
if candidate = name.
|
1752
|
+
if candidate = name.compact.join("_").presence
|
1706
1753
|
# If a name was not explicitly given, we check if it is valid
|
1707
1754
|
# and return nil in case it isn't. Otherwise, we pass the invalid name
|
1708
1755
|
# forward so the underlying router engine treats it and raises an exception.
|
1709
1756
|
if as.nil?
|
1710
|
-
candidate unless
|
1757
|
+
candidate unless candidate !~ /\A[_a-z]/i || @set.named_routes.key?(candidate)
|
1711
1758
|
else
|
1712
1759
|
candidate
|
1713
1760
|
end
|
@@ -1832,9 +1879,83 @@ module ActionDispatch
|
|
1832
1879
|
end
|
1833
1880
|
end
|
1834
1881
|
|
1882
|
+
class Scope # :nodoc:
|
1883
|
+
OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module,
|
1884
|
+
:controller, :action, :path_names, :constraints,
|
1885
|
+
:shallow, :blocks, :defaults, :options]
|
1886
|
+
|
1887
|
+
RESOURCE_SCOPES = [:resource, :resources]
|
1888
|
+
RESOURCE_METHOD_SCOPES = [:collection, :member, :new]
|
1889
|
+
|
1890
|
+
attr_reader :parent, :scope_level
|
1891
|
+
|
1892
|
+
def initialize(hash, parent = {}, scope_level = nil)
|
1893
|
+
@hash = hash
|
1894
|
+
@parent = parent
|
1895
|
+
@scope_level = scope_level
|
1896
|
+
end
|
1897
|
+
|
1898
|
+
def nested?
|
1899
|
+
scope_level == :nested
|
1900
|
+
end
|
1901
|
+
|
1902
|
+
def resources?
|
1903
|
+
scope_level == :resources
|
1904
|
+
end
|
1905
|
+
|
1906
|
+
def resource_method_scope?
|
1907
|
+
RESOURCE_METHOD_SCOPES.include? scope_level
|
1908
|
+
end
|
1909
|
+
|
1910
|
+
def action_name(name_prefix, prefix, collection_name, member_name)
|
1911
|
+
case scope_level
|
1912
|
+
when :nested
|
1913
|
+
[name_prefix, prefix]
|
1914
|
+
when :collection
|
1915
|
+
[prefix, name_prefix, collection_name]
|
1916
|
+
when :new
|
1917
|
+
[prefix, :new, name_prefix, member_name]
|
1918
|
+
when :member
|
1919
|
+
[prefix, name_prefix, member_name]
|
1920
|
+
when :root
|
1921
|
+
[name_prefix, collection_name, prefix]
|
1922
|
+
else
|
1923
|
+
[name_prefix, member_name, prefix]
|
1924
|
+
end
|
1925
|
+
end
|
1926
|
+
|
1927
|
+
def resource_scope?
|
1928
|
+
RESOURCE_SCOPES.include? scope_level
|
1929
|
+
end
|
1930
|
+
|
1931
|
+
def options
|
1932
|
+
OPTIONS
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
def new(hash)
|
1936
|
+
self.class.new hash, self, scope_level
|
1937
|
+
end
|
1938
|
+
|
1939
|
+
def new_level(level)
|
1940
|
+
self.class.new(self, self, level)
|
1941
|
+
end
|
1942
|
+
|
1943
|
+
def fetch(key, &block)
|
1944
|
+
@hash.fetch(key, &block)
|
1945
|
+
end
|
1946
|
+
|
1947
|
+
def [](key)
|
1948
|
+
@hash.fetch(key) { @parent[key] }
|
1949
|
+
end
|
1950
|
+
|
1951
|
+
def []=(k,v)
|
1952
|
+
@hash[k] = v
|
1953
|
+
end
|
1954
|
+
end
|
1955
|
+
|
1835
1956
|
def initialize(set) #:nodoc:
|
1836
1957
|
@set = set
|
1837
|
-
@scope = { :path_names => @set.resources_path_names }
|
1958
|
+
@scope = Scope.new({ :path_names => @set.resources_path_names })
|
1838
1959
|
@concerns = {}
|
1839
1960
|
@nesting = []
|
1840
1961
|
end
|