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
@@ -101,118 +101,230 @@ module ActionDispatch
|
|
101
101
|
# polymorphic_url(Comment) # same as comments_url()
|
102
102
|
#
|
103
103
|
def polymorphic_url(record_or_hash_or_array, options = {})
|
104
|
-
if record_or_hash_or_array
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
109
|
-
record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
|
104
|
+
if Hash === record_or_hash_or_array
|
105
|
+
options = record_or_hash_or_array.merge(options)
|
106
|
+
record = options.delete :id
|
107
|
+
return polymorphic_url record, options
|
110
108
|
end
|
111
109
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
args = Array === record_or_hash_or_array ?
|
116
|
-
record_or_hash_or_array.dup :
|
117
|
-
[ record_or_hash_or_array ]
|
110
|
+
opts = options.dup
|
111
|
+
action = opts.delete :action
|
112
|
+
type = opts.delete(:routing_type) || :url
|
118
113
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
:plural
|
125
|
-
elsif record.is_a?(Class)
|
126
|
-
args.pop
|
127
|
-
:plural
|
128
|
-
else
|
129
|
-
:singular
|
130
|
-
end
|
114
|
+
HelperMethodBuilder.polymorphic_method self,
|
115
|
+
record_or_hash_or_array,
|
116
|
+
action,
|
117
|
+
type,
|
118
|
+
opts
|
131
119
|
|
132
|
-
args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
|
133
|
-
named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
|
134
|
-
|
135
|
-
url_options = options.except(:action, :routing_type)
|
136
|
-
unless url_options.empty?
|
137
|
-
args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
|
138
|
-
end
|
139
|
-
|
140
|
-
args.collect! { |a| convert_to_model(a) }
|
141
|
-
|
142
|
-
(proxy || self).send(named_route, *args)
|
143
120
|
end
|
144
121
|
|
145
122
|
# Returns the path component of a URL for the given record. It uses
|
146
123
|
# <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>.
|
147
124
|
def polymorphic_path(record_or_hash_or_array, options = {})
|
148
|
-
|
125
|
+
if Hash === record_or_hash_or_array
|
126
|
+
options = record_or_hash_or_array.merge(options)
|
127
|
+
record = options.delete :id
|
128
|
+
return polymorphic_path record, options
|
129
|
+
end
|
130
|
+
|
131
|
+
opts = options.dup
|
132
|
+
action = opts.delete :action
|
133
|
+
type = :path
|
134
|
+
|
135
|
+
HelperMethodBuilder.polymorphic_method self,
|
136
|
+
record_or_hash_or_array,
|
137
|
+
action,
|
138
|
+
type,
|
139
|
+
opts
|
149
140
|
end
|
150
141
|
|
142
|
+
|
151
143
|
%w(edit new).each do |action|
|
152
144
|
module_eval <<-EOT, __FILE__, __LINE__ + 1
|
153
|
-
def #{action}_polymorphic_url(record_or_hash, options = {})
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
polymorphic_url( # polymorphic_url(
|
161
|
-
record_or_hash, # record_or_hash,
|
162
|
-
options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
|
163
|
-
end # end
|
145
|
+
def #{action}_polymorphic_url(record_or_hash, options = {})
|
146
|
+
polymorphic_url_for_action("#{action}", record_or_hash, options)
|
147
|
+
end
|
148
|
+
|
149
|
+
def #{action}_polymorphic_path(record_or_hash, options = {})
|
150
|
+
polymorphic_path_for_action("#{action}", record_or_hash, options)
|
151
|
+
end
|
164
152
|
EOT
|
165
153
|
end
|
166
154
|
|
167
155
|
private
|
168
|
-
|
169
|
-
|
156
|
+
|
157
|
+
def polymorphic_url_for_action(action, record_or_hash, options)
|
158
|
+
polymorphic_url(record_or_hash, options.merge(:action => action))
|
159
|
+
end
|
160
|
+
|
161
|
+
def polymorphic_path_for_action(action, record_or_hash, options)
|
162
|
+
options = options.merge(:action => action, :routing_type => :path)
|
163
|
+
polymorphic_path(record_or_hash, options)
|
164
|
+
end
|
165
|
+
|
166
|
+
class HelperMethodBuilder # :nodoc:
|
167
|
+
CACHE = { 'path' => {}, 'url' => {} }
|
168
|
+
|
169
|
+
def self.get(action, type)
|
170
|
+
type = type.to_s
|
171
|
+
CACHE[type].fetch(action) { build action, type }
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.url; CACHE['url'.freeze][nil]; end
|
175
|
+
def self.path; CACHE['path'.freeze][nil]; end
|
176
|
+
|
177
|
+
def self.build(action, type)
|
178
|
+
prefix = action ? "#{action}_" : ""
|
179
|
+
suffix = type
|
180
|
+
if action.to_s == 'new'
|
181
|
+
HelperMethodBuilder.singular prefix, suffix
|
182
|
+
else
|
183
|
+
HelperMethodBuilder.plural prefix, suffix
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.singular(prefix, suffix)
|
188
|
+
new(->(name) { name.singular_route_key }, prefix, suffix)
|
170
189
|
end
|
171
190
|
|
172
|
-
def
|
173
|
-
|
191
|
+
def self.plural(prefix, suffix)
|
192
|
+
new(->(name) { name.route_key }, prefix, suffix)
|
174
193
|
end
|
175
194
|
|
176
|
-
def
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
195
|
+
def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options)
|
196
|
+
builder = get action, type
|
197
|
+
|
198
|
+
case record_or_hash_or_array
|
199
|
+
when Array
|
200
|
+
if record_or_hash_or_array.empty? || record_or_hash_or_array.include?(nil)
|
201
|
+
raise ArgumentError, "Nil location provided. Can't build URI."
|
202
|
+
end
|
203
|
+
if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
|
204
|
+
recipient = record_or_hash_or_array.shift
|
185
205
|
end
|
206
|
+
|
207
|
+
method, args = builder.handle_list record_or_hash_or_array
|
208
|
+
when String, Symbol
|
209
|
+
method, args = builder.handle_string record_or_hash_or_array
|
210
|
+
when Class
|
211
|
+
method, args = builder.handle_class record_or_hash_or_array
|
212
|
+
|
213
|
+
when nil
|
214
|
+
raise ArgumentError, "Nil location provided. Can't build URI."
|
186
215
|
else
|
187
|
-
|
188
|
-
route = []
|
216
|
+
method, args = builder.handle_model record_or_hash_or_array
|
189
217
|
end
|
190
218
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
219
|
+
|
220
|
+
if options.empty?
|
221
|
+
recipient.send(method, *args)
|
222
|
+
else
|
223
|
+
recipient.send(method, *args, options)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
attr_reader :suffix, :prefix
|
228
|
+
|
229
|
+
def initialize(key_strategy, prefix, suffix)
|
230
|
+
@key_strategy = key_strategy
|
231
|
+
@prefix = prefix
|
232
|
+
@suffix = suffix
|
233
|
+
end
|
234
|
+
|
235
|
+
def handle_string(record)
|
236
|
+
[get_method_for_string(record), []]
|
237
|
+
end
|
238
|
+
|
239
|
+
def handle_string_call(target, str)
|
240
|
+
target.send get_method_for_string str
|
241
|
+
end
|
242
|
+
|
243
|
+
def handle_class(klass)
|
244
|
+
[get_method_for_class(klass), []]
|
245
|
+
end
|
246
|
+
|
247
|
+
def handle_class_call(target, klass)
|
248
|
+
target.send get_method_for_class klass
|
249
|
+
end
|
250
|
+
|
251
|
+
def handle_model(record)
|
252
|
+
args = []
|
253
|
+
|
254
|
+
model = record.to_model
|
255
|
+
name = if record.persisted?
|
256
|
+
args << model
|
257
|
+
model.model_name.singular_route_key
|
258
|
+
else
|
259
|
+
@key_strategy.call model.model_name
|
260
|
+
end
|
261
|
+
|
262
|
+
named_route = prefix + "#{name}_#{suffix}"
|
263
|
+
|
264
|
+
[named_route, args]
|
265
|
+
end
|
266
|
+
|
267
|
+
def handle_model_call(target, model)
|
268
|
+
method, args = handle_model model
|
269
|
+
target.send(method, *args)
|
270
|
+
end
|
271
|
+
|
272
|
+
def handle_list(list)
|
273
|
+
record_list = list.dup
|
274
|
+
record = record_list.pop
|
275
|
+
|
276
|
+
args = []
|
277
|
+
|
278
|
+
route = record_list.map { |parent|
|
279
|
+
case parent
|
280
|
+
when Symbol, String
|
281
|
+
parent.to_s
|
282
|
+
when Class
|
283
|
+
args << parent
|
284
|
+
parent.model_name.singular_route_key
|
196
285
|
else
|
197
|
-
|
286
|
+
args << parent.to_model
|
287
|
+
parent.to_model.model_name.singular_route_key
|
198
288
|
end
|
289
|
+
}
|
290
|
+
|
291
|
+
route <<
|
292
|
+
case record
|
293
|
+
when Symbol, String
|
294
|
+
record.to_s
|
295
|
+
when Class
|
296
|
+
@key_strategy.call record.model_name
|
199
297
|
else
|
200
|
-
|
298
|
+
if record.persisted?
|
299
|
+
args << record.to_model
|
300
|
+
record.to_model.model_name.singular_route_key
|
301
|
+
else
|
302
|
+
@key_strategy.call record.to_model.model_name
|
303
|
+
end
|
201
304
|
end
|
202
305
|
|
203
|
-
route <<
|
306
|
+
route << suffix
|
204
307
|
|
205
|
-
|
308
|
+
named_route = prefix + route.join("_")
|
309
|
+
[named_route, args]
|
206
310
|
end
|
207
311
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
312
|
+
private
|
313
|
+
|
314
|
+
def get_method_for_class(klass)
|
315
|
+
name = @key_strategy.call klass.model_name
|
316
|
+
prefix + "#{name}_#{suffix}"
|
317
|
+
end
|
318
|
+
|
319
|
+
def get_method_for_string(str)
|
320
|
+
prefix + "#{str}_#{suffix}"
|
214
321
|
end
|
322
|
+
|
323
|
+
[nil, 'new', 'edit'].each do |action|
|
324
|
+
CACHE['url'][action] = build action, 'url'
|
325
|
+
CACHE['path'][action] = build action, 'path'
|
326
|
+
end
|
327
|
+
end
|
215
328
|
end
|
216
329
|
end
|
217
330
|
end
|
218
|
-
|
@@ -3,10 +3,11 @@ require 'active_support/core_ext/uri'
|
|
3
3
|
require 'active_support/core_ext/array/extract_options'
|
4
4
|
require 'rack/utils'
|
5
5
|
require 'action_controller/metal/exceptions'
|
6
|
+
require 'action_dispatch/routing/endpoint'
|
6
7
|
|
7
8
|
module ActionDispatch
|
8
9
|
module Routing
|
9
|
-
class Redirect # :nodoc:
|
10
|
+
class Redirect < Endpoint # :nodoc:
|
10
11
|
attr_reader :status, :block
|
11
12
|
|
12
13
|
def initialize(status, block)
|
@@ -14,18 +15,15 @@ module ActionDispatch
|
|
14
15
|
@block = block
|
15
16
|
end
|
16
17
|
|
17
|
-
def
|
18
|
-
req = Request.new(env)
|
18
|
+
def redirect?; true; end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
unless value.valid_encoding?
|
24
|
-
raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
|
25
|
-
end
|
26
|
-
end
|
20
|
+
def call(env)
|
21
|
+
serve Request.new env
|
22
|
+
end
|
27
23
|
|
28
|
-
|
24
|
+
def serve(req)
|
25
|
+
req.check_path_parameters!
|
26
|
+
uri = URI.parse(path(req.path_parameters, req))
|
29
27
|
|
30
28
|
unless uri.host
|
31
29
|
if relative_path?(uri.path)
|
@@ -39,7 +37,7 @@ module ActionDispatch
|
|
39
37
|
uri.host ||= req.host
|
40
38
|
uri.port ||= req.port unless req.standard_port?
|
41
39
|
|
42
|
-
body = %(<html><body>You are being <a href="#{ERB::Util.
|
40
|
+
body = %(<html><body>You are being <a href="#{ERB::Util.unwrapped_html_escape(uri.to_s)}">redirected</a>.</body></html>)
|
43
41
|
|
44
42
|
headers = {
|
45
43
|
'Location' => uri.to_s,
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'action_dispatch/journey'
|
2
2
|
require 'forwardable'
|
3
|
+
require 'thread_safe'
|
3
4
|
require 'active_support/concern'
|
4
5
|
require 'active_support/core_ext/object/to_query'
|
5
6
|
require 'active_support/core_ext/hash/slice'
|
@@ -7,6 +8,7 @@ require 'active_support/core_ext/module/remove_method'
|
|
7
8
|
require 'active_support/core_ext/array/extract_options'
|
8
9
|
require 'action_controller/metal/exceptions'
|
9
10
|
require 'action_dispatch/http/request'
|
11
|
+
require 'action_dispatch/routing/endpoint'
|
10
12
|
|
11
13
|
module ActionDispatch
|
12
14
|
module Routing
|
@@ -17,26 +19,17 @@ module ActionDispatch
|
|
17
19
|
# alias inspect to to_s.
|
18
20
|
alias inspect to_s
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@defaults = options[:defaults]
|
25
|
-
@glob_param = options.delete(:glob)
|
22
|
+
class Dispatcher < Routing::Endpoint #:nodoc:
|
23
|
+
def initialize(defaults)
|
24
|
+
@defaults = defaults
|
25
|
+
@controller_class_names = ThreadSafe::Cache.new
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
params = env[PARAMETERS_KEY]
|
30
|
-
|
31
|
-
# If any of the path parameters has an invalid encoding then
|
32
|
-
# raise since it's likely to trigger errors further on.
|
33
|
-
params.each do |key, value|
|
34
|
-
next unless value.respond_to?(:valid_encoding?)
|
28
|
+
def dispatcher?; true; end
|
35
29
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
30
|
+
def serve(req)
|
31
|
+
req.check_path_parameters!
|
32
|
+
params = req.path_parameters
|
40
33
|
|
41
34
|
prepare_params!(params)
|
42
35
|
|
@@ -45,13 +38,12 @@ module ActionDispatch
|
|
45
38
|
return [404, {'X-Cascade' => 'pass'}, []]
|
46
39
|
end
|
47
40
|
|
48
|
-
dispatch(controller, params[:action], env)
|
41
|
+
dispatch(controller, params[:action], req.env)
|
49
42
|
end
|
50
43
|
|
51
44
|
def prepare_params!(params)
|
52
45
|
normalize_controller!(params)
|
53
46
|
merge_default_action!(params)
|
54
|
-
split_glob_param!(params) if @glob_param
|
55
47
|
end
|
56
48
|
|
57
49
|
# If this is a default_controller (i.e. a controller specified by the user)
|
@@ -72,7 +64,7 @@ module ActionDispatch
|
|
72
64
|
private
|
73
65
|
|
74
66
|
def controller_reference(controller_param)
|
75
|
-
const_name = "#{controller_param.camelize}Controller"
|
67
|
+
const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
|
76
68
|
ActiveSupport::Dependencies.constantize(const_name)
|
77
69
|
end
|
78
70
|
|
@@ -87,10 +79,6 @@ module ActionDispatch
|
|
87
79
|
def merge_default_action!(params)
|
88
80
|
params[:action] ||= 'index'
|
89
81
|
end
|
90
|
-
|
91
|
-
def split_glob_param!(params)
|
92
|
-
params[@glob_param] = params[@glob_param].split('/').map { |v| URI.parser.unescape(v) }
|
93
|
-
end
|
94
82
|
end
|
95
83
|
|
96
84
|
# A NamedRouteCollection instance is a collection of named routes, and also
|
@@ -98,36 +86,69 @@ module ActionDispatch
|
|
98
86
|
# named routes.
|
99
87
|
class NamedRouteCollection #:nodoc:
|
100
88
|
include Enumerable
|
101
|
-
attr_reader :routes, :
|
89
|
+
attr_reader :routes, :url_helpers_module
|
102
90
|
|
103
91
|
def initialize
|
104
92
|
@routes = {}
|
105
|
-
@
|
106
|
-
@
|
93
|
+
@path_helpers = Set.new
|
94
|
+
@url_helpers = Set.new
|
95
|
+
@url_helpers_module = Module.new
|
96
|
+
@path_helpers_module = Module.new
|
97
|
+
end
|
98
|
+
|
99
|
+
def route_defined?(name)
|
100
|
+
key = name.to_sym
|
101
|
+
@path_helpers.include?(key) || @url_helpers.include?(key)
|
102
|
+
end
|
103
|
+
|
104
|
+
def helpers
|
105
|
+
ActiveSupport::Deprecation.warn("`named_routes.helpers` is deprecated, please use `route_defined?(route_name)` to see if a named route was defined.")
|
106
|
+
@path_helpers + @url_helpers
|
107
107
|
end
|
108
108
|
|
109
109
|
def helper_names
|
110
|
-
@
|
110
|
+
@path_helpers.map(&:to_s) + @url_helpers.map(&:to_s)
|
111
111
|
end
|
112
112
|
|
113
113
|
def clear!
|
114
|
-
@
|
115
|
-
@
|
114
|
+
@path_helpers.each do |helper|
|
115
|
+
@path_helpers_module.send :undef_method, helper
|
116
|
+
end
|
117
|
+
|
118
|
+
@url_helpers.each do |helper|
|
119
|
+
@url_helpers_module.send :undef_method, helper
|
116
120
|
end
|
117
121
|
|
118
122
|
@routes.clear
|
119
|
-
@
|
123
|
+
@path_helpers.clear
|
124
|
+
@url_helpers.clear
|
120
125
|
end
|
121
126
|
|
122
127
|
def add(name, route)
|
123
|
-
|
124
|
-
|
128
|
+
key = name.to_sym
|
129
|
+
path_name = :"#{name}_path"
|
130
|
+
url_name = :"#{name}_url"
|
131
|
+
|
132
|
+
if routes.key? key
|
133
|
+
@path_helpers_module.send :undef_method, path_name
|
134
|
+
@url_helpers_module.send :undef_method, url_name
|
135
|
+
end
|
136
|
+
routes[key] = route
|
137
|
+
define_url_helper @path_helpers_module, route, path_name, route.defaults, name, PATH
|
138
|
+
define_url_helper @url_helpers_module, route, url_name, route.defaults, name, FULL
|
139
|
+
|
140
|
+
@path_helpers << path_name
|
141
|
+
@url_helpers << url_name
|
125
142
|
end
|
126
143
|
|
127
144
|
def get(name)
|
128
145
|
routes[name.to_sym]
|
129
146
|
end
|
130
147
|
|
148
|
+
def key?(name)
|
149
|
+
routes.key? name.to_sym
|
150
|
+
end
|
151
|
+
|
131
152
|
alias []= add
|
132
153
|
alias [] get
|
133
154
|
alias clear clear!
|
@@ -145,36 +166,54 @@ module ActionDispatch
|
|
145
166
|
routes.length
|
146
167
|
end
|
147
168
|
|
169
|
+
def path_helpers_module(warn = false)
|
170
|
+
if warn
|
171
|
+
mod = @path_helpers_module
|
172
|
+
helpers = @path_helpers
|
173
|
+
Module.new do
|
174
|
+
include mod
|
175
|
+
|
176
|
+
helpers.each do |meth|
|
177
|
+
define_method(meth) do |*args, &block|
|
178
|
+
ActiveSupport::Deprecation.warn("The method `#{meth}` cannot be used here as a full URL is required. Use `#{meth.to_s.sub(/_path$/, '_url')}` instead")
|
179
|
+
super(*args, &block)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
else
|
184
|
+
@path_helpers_module
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
148
188
|
class UrlHelper # :nodoc:
|
149
|
-
def self.create(route, options)
|
189
|
+
def self.create(route, options, route_name, url_strategy)
|
150
190
|
if optimize_helper?(route)
|
151
|
-
OptimizedUrlHelper.new(route, options)
|
191
|
+
OptimizedUrlHelper.new(route, options, route_name, url_strategy)
|
152
192
|
else
|
153
|
-
new route, options
|
193
|
+
new route, options, route_name, url_strategy
|
154
194
|
end
|
155
195
|
end
|
156
196
|
|
157
197
|
def self.optimize_helper?(route)
|
158
|
-
!route.glob? && route.requirements.
|
198
|
+
!route.glob? && route.path.requirements.empty?
|
159
199
|
end
|
160
200
|
|
201
|
+
attr_reader :url_strategy, :route_name
|
202
|
+
|
161
203
|
class OptimizedUrlHelper < UrlHelper # :nodoc:
|
162
204
|
attr_reader :arg_size
|
163
205
|
|
164
|
-
def initialize(route, options)
|
206
|
+
def initialize(route, options, route_name, url_strategy)
|
165
207
|
super
|
166
|
-
@klass = Journey::Router::Utils
|
167
208
|
@required_parts = @route.required_parts
|
168
209
|
@arg_size = @required_parts.size
|
169
|
-
@optimized_path = @route.optimized_path
|
170
210
|
end
|
171
211
|
|
172
|
-
def call(t, args)
|
173
|
-
if args.size == arg_size && !
|
174
|
-
options = @options
|
175
|
-
options.merge!(t.url_options) if t.respond_to?(:url_options)
|
212
|
+
def call(t, args, inner_options)
|
213
|
+
if args.size == arg_size && !inner_options && optimize_routes_generation?(t)
|
214
|
+
options = t.url_options.merge @options
|
176
215
|
options[:path] = optimized_helper(args)
|
177
|
-
|
216
|
+
url_strategy.call options
|
178
217
|
else
|
179
218
|
super
|
180
219
|
end
|
@@ -183,18 +222,14 @@ module ActionDispatch
|
|
183
222
|
private
|
184
223
|
|
185
224
|
def optimized_helper(args)
|
186
|
-
params =
|
225
|
+
params = parameterize_args(args)
|
187
226
|
missing_keys = missing_keys(params)
|
188
227
|
|
189
228
|
unless missing_keys.empty?
|
190
229
|
raise_generation_error(params, missing_keys)
|
191
230
|
end
|
192
231
|
|
193
|
-
@
|
194
|
-
end
|
195
|
-
|
196
|
-
def replace_segment(params, segment)
|
197
|
-
Symbol === segment ? @klass.escape_segment(params[segment]) : segment
|
232
|
+
@route.format params
|
198
233
|
end
|
199
234
|
|
200
235
|
def optimize_routes_generation?(t)
|
@@ -202,7 +237,9 @@ module ActionDispatch
|
|
202
237
|
end
|
203
238
|
|
204
239
|
def parameterize_args(args)
|
205
|
-
|
240
|
+
params = {}
|
241
|
+
@required_parts.zip(args.map(&:to_param)) { |k,v| params[k] = v }
|
242
|
+
params
|
206
243
|
end
|
207
244
|
|
208
245
|
def missing_keys(args)
|
@@ -218,41 +255,36 @@ module ActionDispatch
|
|
218
255
|
end
|
219
256
|
end
|
220
257
|
|
221
|
-
def initialize(route, options)
|
258
|
+
def initialize(route, options, route_name, url_strategy)
|
222
259
|
@options = options
|
223
260
|
@segment_keys = route.segment_keys.uniq
|
224
261
|
@route = route
|
262
|
+
@url_strategy = url_strategy
|
263
|
+
@route_name = route_name
|
225
264
|
end
|
226
265
|
|
227
|
-
def call(t, args)
|
228
|
-
t.
|
266
|
+
def call(t, args, inner_options)
|
267
|
+
controller_options = t.url_options
|
268
|
+
options = controller_options.merge @options
|
269
|
+
hash = handle_positional_args(controller_options,
|
270
|
+
inner_options || {},
|
271
|
+
args,
|
272
|
+
options,
|
273
|
+
@segment_keys)
|
274
|
+
|
275
|
+
t._routes.url_for(hash, route_name, url_strategy)
|
229
276
|
end
|
230
277
|
|
231
|
-
def handle_positional_args(
|
232
|
-
inner_options = args.extract_options!
|
233
|
-
result = options.dup
|
278
|
+
def handle_positional_args(controller_options, inner_options, args, result, path_params)
|
234
279
|
|
235
280
|
if args.size > 0
|
236
|
-
# take format into account
|
237
|
-
|
238
|
-
|
239
|
-
else
|
240
|
-
keys_size = keys.size
|
241
|
-
end
|
242
|
-
|
243
|
-
if args.size < keys_size
|
244
|
-
keys -= t.url_options.keys if t.respond_to?(:url_options)
|
245
|
-
keys -= options.keys
|
246
|
-
end
|
247
|
-
keys -= inner_options.keys
|
248
|
-
|
249
|
-
keys.each do |key|
|
250
|
-
value = inner_options.fetch(key) { args.shift }
|
251
|
-
|
252
|
-
unless key == :format && value.nil?
|
253
|
-
result[key] = value
|
254
|
-
end
|
281
|
+
if args.size < path_params.size - 1 # take format into account
|
282
|
+
path_params -= controller_options.keys
|
283
|
+
path_params -= result.keys
|
255
284
|
end
|
285
|
+
path_params.each { |param|
|
286
|
+
result[param] = inner_options[param] || args.shift
|
287
|
+
}
|
256
288
|
end
|
257
289
|
|
258
290
|
result.merge!(inner_options)
|
@@ -273,27 +305,25 @@ module ActionDispatch
|
|
273
305
|
#
|
274
306
|
# foo_url(bar, baz, bang, sort_by: 'baz')
|
275
307
|
#
|
276
|
-
def define_url_helper(route, name,
|
277
|
-
helper = UrlHelper.create(route,
|
278
|
-
|
279
|
-
@module.remove_possible_method name
|
280
|
-
@module.module_eval do
|
308
|
+
def define_url_helper(mod, route, name, opts, route_key, url_strategy)
|
309
|
+
helper = UrlHelper.create(route, opts, route_key, url_strategy)
|
310
|
+
mod.module_eval do
|
281
311
|
define_method(name) do |*args|
|
282
|
-
|
312
|
+
options = nil
|
313
|
+
options = args.pop if args.last.is_a? Hash
|
314
|
+
helper.call self, args, options
|
283
315
|
end
|
284
316
|
end
|
285
|
-
|
286
|
-
helpers << name
|
287
|
-
end
|
288
|
-
|
289
|
-
def define_named_route_methods(name, route)
|
290
|
-
define_url_helper route, :"#{name}_path",
|
291
|
-
route.defaults.merge(:use_route => name, :only_path => true)
|
292
|
-
define_url_helper route, :"#{name}_url",
|
293
|
-
route.defaults.merge(:use_route => name, :only_path => false)
|
294
317
|
end
|
295
318
|
end
|
296
319
|
|
320
|
+
# :stopdoc:
|
321
|
+
# strategy for building urls to send to the client
|
322
|
+
PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
|
323
|
+
FULL = ->(options) { ActionDispatch::Http::URL.full_url_for(options) }
|
324
|
+
UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
|
325
|
+
# :startdoc:
|
326
|
+
|
297
327
|
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
|
298
328
|
attr_accessor :disable_clear_and_finalize, :resources_path_names
|
299
329
|
attr_accessor :default_url_options, :request_class
|
@@ -306,7 +336,7 @@ module ActionDispatch
|
|
306
336
|
|
307
337
|
def initialize(request_class = ActionDispatch::Request)
|
308
338
|
self.named_routes = NamedRouteCollection.new
|
309
|
-
self.resources_path_names = self.class.default_resources_path_names
|
339
|
+
self.resources_path_names = self.class.default_resources_path_names
|
310
340
|
self.default_url_options = {}
|
311
341
|
self.request_class = request_class
|
312
342
|
|
@@ -316,9 +346,7 @@ module ActionDispatch
|
|
316
346
|
@finalized = false
|
317
347
|
|
318
348
|
@set = Journey::Routes.new
|
319
|
-
@router = Journey::Router.new
|
320
|
-
:parameters_key => PARAMETERS_KEY,
|
321
|
-
:request_class => request_class})
|
349
|
+
@router = Journey::Router.new @set
|
322
350
|
@formatter = Journey::Formatter.new @set
|
323
351
|
end
|
324
352
|
|
@@ -349,6 +377,7 @@ module ActionDispatch
|
|
349
377
|
mapper.instance_exec(&block)
|
350
378
|
end
|
351
379
|
end
|
380
|
+
private :eval_block
|
352
381
|
|
353
382
|
def finalize!
|
354
383
|
return if @finalized
|
@@ -364,6 +393,10 @@ module ActionDispatch
|
|
364
393
|
@prepend.each { |blk| eval_block(blk) }
|
365
394
|
end
|
366
395
|
|
396
|
+
def dispatcher(defaults)
|
397
|
+
Routing::RouteSet::Dispatcher.new(defaults)
|
398
|
+
end
|
399
|
+
|
367
400
|
module MountedHelpers #:nodoc:
|
368
401
|
extend ActiveSupport::Concern
|
369
402
|
include UrlFor
|
@@ -394,40 +427,51 @@ module ActionDispatch
|
|
394
427
|
RUBY
|
395
428
|
end
|
396
429
|
|
397
|
-
def url_helpers
|
398
|
-
|
399
|
-
routes = self
|
430
|
+
def url_helpers(include_path_helpers = true)
|
431
|
+
routes = self
|
400
432
|
|
401
|
-
|
402
|
-
|
403
|
-
|
433
|
+
Module.new do
|
434
|
+
extend ActiveSupport::Concern
|
435
|
+
include UrlFor
|
436
|
+
|
437
|
+
# Define url_for in the singleton level so one can do:
|
438
|
+
# Rails.application.routes.url_helpers.url_for(args)
|
439
|
+
@_routes = routes
|
440
|
+
class << self
|
441
|
+
delegate :url_for, :optimize_routes_generation?, to: '@_routes'
|
442
|
+
attr_reader :_routes
|
443
|
+
def url_options; {}; end
|
444
|
+
end
|
404
445
|
|
405
|
-
|
406
|
-
# Rails.application.routes.url_helpers.url_for(args)
|
407
|
-
@_routes = routes
|
408
|
-
class << self
|
409
|
-
delegate :url_for, :optimize_routes_generation?, :to => '@_routes'
|
410
|
-
end
|
446
|
+
url_helpers = routes.named_routes.url_helpers_module
|
411
447
|
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
448
|
+
# Make named_routes available in the module singleton
|
449
|
+
# as well, so one can do:
|
450
|
+
# Rails.application.routes.url_helpers.posts_path
|
451
|
+
extend url_helpers
|
416
452
|
|
417
|
-
|
418
|
-
|
419
|
-
|
453
|
+
# Any class that includes this module will get all
|
454
|
+
# named routes...
|
455
|
+
include url_helpers
|
420
456
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
457
|
+
if include_path_helpers
|
458
|
+
path_helpers = routes.named_routes.path_helpers_module
|
459
|
+
else
|
460
|
+
path_helpers = routes.named_routes.path_helpers_module(true)
|
461
|
+
end
|
425
462
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
463
|
+
include path_helpers
|
464
|
+
extend path_helpers
|
465
|
+
|
466
|
+
# plus a singleton class method called _routes ...
|
467
|
+
included do
|
468
|
+
singleton_class.send(:redefine_method, :_routes) { routes }
|
430
469
|
end
|
470
|
+
|
471
|
+
# And an instance method _routes. Note that
|
472
|
+
# UrlFor (included in this module) add extra
|
473
|
+
# conveniences for working with @_routes.
|
474
|
+
define_method(:_routes) { @_routes || routes }
|
431
475
|
end
|
432
476
|
end
|
433
477
|
|
@@ -446,7 +490,9 @@ module ActionDispatch
|
|
446
490
|
"http://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
|
447
491
|
end
|
448
492
|
|
449
|
-
path =
|
493
|
+
path = conditions.delete :path_info
|
494
|
+
ast = conditions.delete :parsed_path_info
|
495
|
+
path = build_path(path, ast, requirements, anchor)
|
450
496
|
conditions = build_conditions(conditions, path.names.map { |x| x.to_sym })
|
451
497
|
|
452
498
|
route = @set.add_route(app, path, conditions, defaults, name)
|
@@ -454,8 +500,9 @@ module ActionDispatch
|
|
454
500
|
route
|
455
501
|
end
|
456
502
|
|
457
|
-
def build_path(path,
|
503
|
+
def build_path(path, ast, requirements, anchor)
|
458
504
|
strexp = Journey::Router::Strexp.new(
|
505
|
+
ast,
|
459
506
|
path,
|
460
507
|
requirements,
|
461
508
|
SEPARATORS,
|
@@ -516,8 +563,8 @@ module ActionDispatch
|
|
516
563
|
|
517
564
|
attr_reader :options, :recall, :set, :named_route
|
518
565
|
|
519
|
-
def initialize(options, recall, set)
|
520
|
-
@named_route =
|
566
|
+
def initialize(named_route, options, recall, set)
|
567
|
+
@named_route = named_route
|
521
568
|
@options = options.dup
|
522
569
|
@recall = recall.dup
|
523
570
|
@set = set
|
@@ -608,7 +655,7 @@ module ActionDispatch
|
|
608
655
|
# Generates a path from routes, returns [path, params].
|
609
656
|
# If no route is generated the formatter will raise ActionController::UrlGenerationError
|
610
657
|
def generate
|
611
|
-
@set.formatter.generate(
|
658
|
+
@set.formatter.generate(named_route, options, recall, PARAMETERIZE)
|
612
659
|
end
|
613
660
|
|
614
661
|
def different_controller?
|
@@ -633,61 +680,74 @@ module ActionDispatch
|
|
633
680
|
end
|
634
681
|
|
635
682
|
def generate_extras(options, recall={})
|
636
|
-
|
683
|
+
route_key = options.delete :use_route
|
684
|
+
path, params = generate(route_key, options, recall)
|
637
685
|
return path, params.keys
|
638
686
|
end
|
639
687
|
|
640
|
-
def generate(options, recall = {})
|
641
|
-
Generator.new(options, recall, self).generate
|
688
|
+
def generate(route_key, options, recall = {})
|
689
|
+
Generator.new(route_key, options, recall, self).generate
|
642
690
|
end
|
691
|
+
private :generate
|
643
692
|
|
644
693
|
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
|
645
694
|
:trailing_slash, :anchor, :params, :only_path, :script_name,
|
646
695
|
:original_script_name]
|
647
696
|
|
648
|
-
def
|
649
|
-
|
697
|
+
def optimize_routes_generation?
|
698
|
+
default_url_options.empty?
|
650
699
|
end
|
651
700
|
|
652
|
-
def
|
653
|
-
|
701
|
+
def find_script_name(options)
|
702
|
+
options.delete(:script_name) { '' }
|
654
703
|
end
|
655
704
|
|
656
|
-
def
|
657
|
-
|
705
|
+
def path_for(options, route_name = nil) # :nodoc:
|
706
|
+
url_for(options, route_name, PATH)
|
658
707
|
end
|
659
708
|
|
660
|
-
# The +options+ argument must be
|
661
|
-
def url_for(options)
|
662
|
-
options = default_url_options.merge
|
709
|
+
# The +options+ argument must be a hash whose keys are *symbols*.
|
710
|
+
def url_for(options, route_name = nil, url_strategy = UNKNOWN)
|
711
|
+
options = default_url_options.merge options
|
663
712
|
|
664
|
-
user
|
665
|
-
|
713
|
+
user = password = nil
|
714
|
+
|
715
|
+
if options[:user] && options[:password]
|
716
|
+
user = options.delete :user
|
717
|
+
password = options.delete :password
|
718
|
+
end
|
666
719
|
|
667
|
-
|
668
|
-
script_name = options.delete(:script_name).presence || _generate_prefix(options)
|
720
|
+
recall = options.delete(:_recall) { {} }
|
669
721
|
|
670
|
-
|
722
|
+
original_script_name = options.delete(:original_script_name)
|
723
|
+
script_name = find_script_name options
|
724
|
+
|
725
|
+
if original_script_name
|
671
726
|
script_name = original_script_name + script_name
|
672
727
|
end
|
673
728
|
|
674
|
-
path_options = options.
|
675
|
-
|
729
|
+
path_options = options.dup
|
730
|
+
RESERVED_OPTIONS.each { |ro| path_options.delete ro }
|
731
|
+
|
732
|
+
path, params = generate(route_name, path_options, recall)
|
733
|
+
|
734
|
+
if options.key? :params
|
735
|
+
params.merge! options[:params]
|
736
|
+
end
|
676
737
|
|
677
|
-
path
|
678
|
-
|
738
|
+
options[:path] = path
|
739
|
+
options[:script_name] = script_name
|
740
|
+
options[:params] = params
|
741
|
+
options[:user] = user
|
742
|
+
options[:password] = password
|
679
743
|
|
680
|
-
|
681
|
-
:path => path,
|
682
|
-
:script_name => script_name,
|
683
|
-
:params => params,
|
684
|
-
:user => user,
|
685
|
-
:password => password
|
686
|
-
}))
|
744
|
+
url_strategy.call options
|
687
745
|
end
|
688
746
|
|
689
747
|
def call(env)
|
690
|
-
|
748
|
+
req = request_class.new(env)
|
749
|
+
req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
|
750
|
+
@router.serve(req)
|
691
751
|
end
|
692
752
|
|
693
753
|
def recognize_path(path, environment = {})
|
@@ -701,8 +761,8 @@ module ActionDispatch
|
|
701
761
|
raise ActionController::RoutingError, e.message
|
702
762
|
end
|
703
763
|
|
704
|
-
req =
|
705
|
-
@router.recognize(req) do |route,
|
764
|
+
req = request_class.new(env)
|
765
|
+
@router.recognize(req) do |route, params|
|
706
766
|
params.merge!(extras)
|
707
767
|
params.each do |key, value|
|
708
768
|
if value.is_a?(String)
|
@@ -710,14 +770,12 @@ module ActionDispatch
|
|
710
770
|
params[key] = URI.parser.unescape(value)
|
711
771
|
end
|
712
772
|
end
|
713
|
-
old_params =
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
dispatcher =
|
718
|
-
end
|
773
|
+
old_params = req.path_parameters
|
774
|
+
req.path_parameters = old_params.merge params
|
775
|
+
app = route.app
|
776
|
+
if app.matches?(req) && app.dispatcher?
|
777
|
+
dispatcher = app.app
|
719
778
|
|
720
|
-
if dispatcher.is_a?(Dispatcher)
|
721
779
|
if dispatcher.controller(params, false)
|
722
780
|
dispatcher.prepare_params!(params)
|
723
781
|
return params
|
@@ -729,17 +787,6 @@ module ActionDispatch
|
|
729
787
|
|
730
788
|
raise ActionController::RoutingError, "No route matches #{path.inspect}"
|
731
789
|
end
|
732
|
-
|
733
|
-
private
|
734
|
-
|
735
|
-
def extract_authentication(options)
|
736
|
-
if options[:user] && options[:password]
|
737
|
-
[options.delete(:user), options.delete(:password)]
|
738
|
-
else
|
739
|
-
nil
|
740
|
-
end
|
741
|
-
end
|
742
|
-
|
743
790
|
end
|
744
791
|
end
|
745
792
|
end
|