actionpack 3.0.0.beta → 3.0.0.beta2

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.

Files changed (118) hide show
  1. data/CHANGELOG +291 -260
  2. data/lib/abstract_controller.rb +5 -2
  3. data/lib/abstract_controller/assigns.rb +21 -0
  4. data/lib/abstract_controller/base.rb +13 -5
  5. data/lib/abstract_controller/collector.rb +2 -0
  6. data/lib/abstract_controller/helpers.rb +4 -14
  7. data/lib/abstract_controller/layouts.rb +50 -99
  8. data/lib/abstract_controller/logger.rb +2 -2
  9. data/lib/abstract_controller/rendering.rb +105 -173
  10. data/lib/abstract_controller/view_paths.rb +69 -0
  11. data/lib/action_controller.rb +1 -2
  12. data/lib/action_controller/base.rb +10 -32
  13. data/lib/action_controller/caching.rb +19 -18
  14. data/lib/action_controller/caching/actions.rb +17 -11
  15. data/lib/action_controller/caching/fragments.rb +5 -17
  16. data/lib/action_controller/caching/pages.rb +24 -24
  17. data/lib/action_controller/caching/sweeping.rb +1 -3
  18. data/lib/action_controller/deprecated.rb +0 -2
  19. data/lib/action_controller/deprecated/base.rb +143 -0
  20. data/lib/action_controller/metal.rb +29 -26
  21. data/lib/action_controller/metal/compatibility.rb +18 -87
  22. data/lib/action_controller/metal/cookies.rb +0 -1
  23. data/lib/action_controller/metal/head.rb +1 -0
  24. data/lib/action_controller/metal/helpers.rb +2 -2
  25. data/lib/action_controller/metal/hide_actions.rb +4 -6
  26. data/lib/action_controller/metal/http_authentication.rb +18 -33
  27. data/lib/action_controller/metal/implicit_render.rb +21 -0
  28. data/lib/action_controller/metal/instrumentation.rb +1 -1
  29. data/lib/action_controller/metal/mime_responds.rb +2 -1
  30. data/lib/action_controller/metal/rack_delegation.rb +3 -8
  31. data/lib/action_controller/metal/redirecting.rb +2 -1
  32. data/lib/action_controller/metal/renderers.rb +4 -2
  33. data/lib/action_controller/metal/rendering.rb +31 -44
  34. data/lib/action_controller/metal/request_forgery_protection.rb +41 -4
  35. data/lib/action_controller/metal/responder.rb +2 -0
  36. data/lib/action_controller/metal/session_management.rb +0 -36
  37. data/lib/action_controller/metal/streaming.rb +20 -47
  38. data/lib/action_controller/metal/testing.rb +0 -1
  39. data/lib/action_controller/metal/url_for.rb +11 -148
  40. data/lib/action_controller/middleware.rb +2 -1
  41. data/lib/action_controller/polymorphic_routes.rb +1 -2
  42. data/lib/action_controller/railtie.rb +63 -10
  43. data/lib/action_controller/railties/{subscriber.rb → log_subscriber.rb} +5 -12
  44. data/lib/action_controller/railties/url_helpers.rb +14 -0
  45. data/lib/action_controller/record_identifier.rb +20 -1
  46. data/lib/action_controller/test_case.rb +123 -12
  47. data/lib/action_dispatch.rb +1 -0
  48. data/lib/action_dispatch/http/cache.rb +20 -3
  49. data/lib/action_dispatch/http/filter_parameters.rb +40 -25
  50. data/lib/action_dispatch/http/mime_negotiation.rb +6 -17
  51. data/lib/action_dispatch/http/mime_type.rb +2 -7
  52. data/lib/action_dispatch/http/request.rb +12 -33
  53. data/lib/action_dispatch/http/response.rb +35 -15
  54. data/lib/action_dispatch/http/upload.rb +2 -0
  55. data/lib/action_dispatch/http/url.rb +5 -32
  56. data/lib/action_dispatch/middleware/callbacks.rb +1 -1
  57. data/lib/action_dispatch/middleware/cookies.rb +4 -3
  58. data/lib/action_dispatch/middleware/params_parser.rb +4 -3
  59. data/lib/action_dispatch/middleware/remote_ip.rb +51 -0
  60. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -0
  61. data/lib/action_dispatch/middleware/session/cookie_store.rb +6 -8
  62. data/lib/action_dispatch/middleware/show_exceptions.rb +0 -14
  63. data/lib/action_dispatch/middleware/stack.rb +6 -2
  64. data/lib/action_dispatch/railtie.rb +3 -1
  65. data/lib/action_dispatch/routing.rb +2 -0
  66. data/lib/action_dispatch/routing/deprecated_mapper.rb +35 -7
  67. data/lib/action_dispatch/routing/mapper.rb +134 -48
  68. data/lib/action_dispatch/routing/route.rb +2 -2
  69. data/lib/action_dispatch/routing/route_set.rb +217 -158
  70. data/lib/action_dispatch/routing/url_for.rb +139 -0
  71. data/lib/action_dispatch/testing/assertions/response.rb +14 -61
  72. data/lib/action_dispatch/testing/assertions/routing.rb +25 -14
  73. data/lib/action_dispatch/testing/integration.rb +32 -50
  74. data/lib/action_dispatch/testing/performance_test.rb +3 -1
  75. data/lib/action_dispatch/testing/test_process.rb +2 -0
  76. data/lib/action_dispatch/testing/test_request.rb +2 -0
  77. data/lib/action_pack/version.rb +4 -3
  78. data/lib/action_view.rb +11 -6
  79. data/lib/action_view/base.rb +33 -121
  80. data/lib/action_view/context.rb +0 -2
  81. data/lib/action_view/helpers.rb +26 -23
  82. data/lib/action_view/helpers/active_model_helper.rb +28 -18
  83. data/lib/action_view/helpers/asset_tag_helper.rb +109 -54
  84. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  85. data/lib/action_view/helpers/cache_helper.rb +22 -1
  86. data/lib/action_view/helpers/capture_helper.rb +22 -22
  87. data/lib/action_view/helpers/date_helper.rb +6 -5
  88. data/lib/action_view/helpers/form_helper.rb +78 -63
  89. data/lib/action_view/helpers/form_options_helper.rb +6 -4
  90. data/lib/action_view/helpers/form_tag_helper.rb +26 -15
  91. data/lib/action_view/helpers/javascript_helper.rb +90 -10
  92. data/lib/action_view/helpers/number_helper.rb +315 -118
  93. data/lib/action_view/helpers/prototype_helper.rb +19 -46
  94. data/lib/action_view/helpers/record_tag_helper.rb +4 -4
  95. data/lib/action_view/helpers/tag_helper.rb +7 -24
  96. data/lib/action_view/helpers/text_helper.rb +8 -7
  97. data/lib/action_view/helpers/translation_helper.rb +7 -5
  98. data/lib/action_view/helpers/url_helper.rb +19 -16
  99. data/lib/action_view/locale/en.yml +45 -6
  100. data/lib/action_view/lookup_context.rb +190 -0
  101. data/lib/action_view/paths.rb +22 -63
  102. data/lib/action_view/railtie.rb +14 -4
  103. data/lib/action_view/railties/{subscriber.rb → log_subscriber.rb} +1 -1
  104. data/lib/action_view/render/layouts.rb +73 -0
  105. data/lib/action_view/render/partials.rb +15 -41
  106. data/lib/action_view/render/rendering.rb +27 -78
  107. data/lib/action_view/template.rb +20 -24
  108. data/lib/action_view/template/error.rb +22 -2
  109. data/lib/action_view/template/handlers/erb.rb +33 -9
  110. data/lib/action_view/template/handlers/rjs.rb +1 -2
  111. data/lib/action_view/template/resolver.rb +46 -104
  112. data/lib/action_view/template/text.rb +5 -12
  113. data/lib/action_view/test_case.rb +14 -23
  114. metadata +83 -40
  115. data/lib/abstract_controller/compatibility.rb +0 -18
  116. data/lib/abstract_controller/localized_cache.rb +0 -49
  117. data/lib/action_controller/metal/configuration.rb +0 -28
  118. data/lib/action_controller/url_rewriter.rb +0 -76
@@ -4,7 +4,7 @@ module ActionDispatch
4
4
  attr_reader :app, :conditions, :defaults, :name
5
5
  attr_reader :path, :requirements
6
6
 
7
- def initialize(app, conditions = {}, requirements = {}, defaults = {}, name = nil)
7
+ def initialize(app, conditions, requirements, defaults, name, anchor)
8
8
  @app = app
9
9
  @defaults = defaults
10
10
  @name = name
@@ -17,7 +17,7 @@ module ActionDispatch
17
17
 
18
18
  if path = conditions[:path_info]
19
19
  @path = path
20
- conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS)
20
+ conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS, anchor)
21
21
  end
22
22
 
23
23
  @conditions = conditions.inject({}) { |h, (k, v)|
@@ -1,5 +1,6 @@
1
1
  require 'rack/mount'
2
2
  require 'forwardable'
3
+ require 'action_dispatch/routing/deprecated_mapper'
3
4
 
4
5
  module ActionDispatch
5
6
  module Routing
@@ -11,8 +12,8 @@ module ActionDispatch
11
12
  PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
12
13
 
13
14
  class Dispatcher
14
- def initialize(options = {})
15
- defaults = options[:defaults]
15
+ def initialize(options={})
16
+ @defaults = options[:defaults]
16
17
  @glob_param = options.delete(:glob)
17
18
  end
18
19
 
@@ -20,7 +21,8 @@ module ActionDispatch
20
21
  params = env[PARAMETERS_KEY]
21
22
  prepare_params!(params)
22
23
 
23
- unless controller = controller(params)
24
+ # Just raise undefined constant errors if a controller was specified as default.
25
+ unless controller = controller(params, @defaults.key?(:controller))
24
26
  return [404, {'X-Cascade' => 'pass'}, []]
25
27
  end
26
28
 
@@ -39,13 +41,13 @@ module ActionDispatch
39
41
  end
40
42
  end
41
43
 
42
- def controller(params)
44
+ def controller(params, raise_error=true)
43
45
  if params && params.has_key?(:controller)
44
46
  controller = "#{params[:controller].camelize}Controller"
45
47
  ActiveSupport::Inflector.constantize(controller)
46
48
  end
47
- rescue NameError
48
- nil
49
+ rescue NameError => e
50
+ raise ActionController::RoutingError, e.message, e.backtrace if raise_error
49
51
  end
50
52
 
51
53
  private
@@ -58,13 +60,12 @@ module ActionDispatch
58
60
  end
59
61
  end
60
62
 
61
-
62
63
  # A NamedRouteCollection instance is a collection of named routes, and also
63
64
  # maintains an anonymous module that can be used to install helpers for the
64
65
  # named routes.
65
66
  class NamedRouteCollection #:nodoc:
66
67
  include Enumerable
67
- attr_reader :routes, :helpers
68
+ attr_reader :routes, :helpers, :module
68
69
 
69
70
  def initialize
70
71
  clear!
@@ -167,51 +168,13 @@ module ActionDispatch
167
168
  selector = url_helper_name(name, kind)
168
169
  hash_access_method = hash_access_name(name, kind)
169
170
 
170
- # We use module_eval to avoid leaks.
171
- #
172
- # def users_url(*args)
173
- # if args.empty? || Hash === args.first
174
- # options = hash_for_users_url(args.first || {})
175
- # else
176
- # options = hash_for_users_url(args.extract_options!)
177
- # default = default_url_options(options) if self.respond_to?(:default_url_options, true)
178
- # options = (default ||= {}).merge(options)
179
- #
180
- # keys = []
181
- # keys -= options.keys if args.size < keys.size - 1
182
- #
183
- # args = args.zip(keys).inject({}) do |h, (v, k)|
184
- # h[k] = v
185
- # h
186
- # end
187
- #
188
- # # Tell url_for to skip default_url_options
189
- # options[:use_defaults] = false
190
- # options.merge!(args)
191
- # end
192
- #
193
- # url_for(options)
194
- # end
195
171
  @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
196
172
  def #{selector}(*args)
197
- if args.empty? || Hash === args.first
198
- options = #{hash_access_method}(args.first || {})
199
- else
200
- options = #{hash_access_method}(args.extract_options!)
201
- default = default_url_options(options) if self.respond_to?(:default_url_options, true)
202
- options = (default ||= {}).merge(options)
203
-
204
- keys = #{route.segment_keys.inspect}
205
- keys -= options.keys if args.size < keys.size - 1 # take format into account
206
-
207
- args = args.zip(keys).inject({}) do |h, (v, k)|
208
- h[k] = v
209
- h
210
- end
211
-
212
- # Tell url_for to skip default_url_options
213
- options[:use_defaults] = false
214
- options.merge!(args)
173
+ options = #{hash_access_method}(args.extract_options!)
174
+
175
+ if args.any?
176
+ options[:_positional_args] = args
177
+ options[:_positional_keys] = #{route.segment_keys.inspect}
215
178
  end
216
179
 
217
180
  url_for(options)
@@ -222,8 +185,9 @@ module ActionDispatch
222
185
  end
223
186
  end
224
187
 
225
- attr_accessor :routes, :named_routes, :controller_namespaces
188
+ attr_accessor :routes, :named_routes
226
189
  attr_accessor :disable_clear_and_finalize, :resources_path_names
190
+ attr_accessor :default_url_options
227
191
 
228
192
  def self.default_resources_path_names
229
193
  { :new => 'new', :edit => 'edit' }
@@ -234,8 +198,10 @@ module ActionDispatch
234
198
  self.named_routes = NamedRouteCollection.new
235
199
  self.resources_path_names = self.class.default_resources_path_names.dup
236
200
  self.controller_namespaces = Set.new
201
+ self.default_url_options = {}
237
202
 
238
203
  @disable_clear_and_finalize = false
204
+ clear!
239
205
  end
240
206
 
241
207
  def draw(&block)
@@ -254,14 +220,16 @@ module ActionDispatch
254
220
  end
255
221
 
256
222
  def finalize!
223
+ return if @finalized
224
+ @finalized = true
257
225
  @set.add_route(NotFound)
258
- install_helpers
259
226
  @set.freeze
260
227
  end
261
228
 
262
229
  def clear!
263
230
  # Clear the controller cache so we may discover new ones
264
231
  @controller_constraints = nil
232
+ @finalized = false
265
233
  routes.clear
266
234
  named_routes.clear
267
235
  @set = ::Rack::Mount::RouteSet.new(:parameters_key => PARAMETERS_KEY)
@@ -272,61 +240,164 @@ module ActionDispatch
272
240
  named_routes.install(destinations, regenerate_code)
273
241
  end
274
242
 
275
- def empty?
276
- routes.empty?
277
- end
243
+ def url_helpers
244
+ @url_helpers ||= begin
245
+ routes = self
278
246
 
279
- CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/
247
+ helpers = Module.new do
248
+ extend ActiveSupport::Concern
249
+ include UrlFor
250
+
251
+ @routes = routes
252
+ class << self
253
+ delegate :url_for, :to => '@routes'
254
+ end
255
+ extend routes.named_routes.module
256
+
257
+ # ROUTES TODO: install_helpers isn't great... can we make a module with the stuff that
258
+ # we can include?
259
+ # Yes plz - JP
260
+ included do
261
+ routes.install_helpers(self)
262
+ singleton_class.send(:define_method, :_router) { routes }
263
+ end
264
+
265
+ define_method(:_router) { routes }
266
+ end
280
267
 
281
- def controller_constraints
282
- @controller_constraints ||= begin
283
- namespaces = controller_namespaces + in_memory_controller_namespaces
284
- source = namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" }
285
- source << CONTROLLER_REGEXP.source
286
- Regexp.compile(source.sort.reverse.join('|'))
268
+ helpers
287
269
  end
288
270
  end
289
271
 
290
- def in_memory_controller_namespaces
291
- namespaces = Set.new
292
- ActionController::Base.subclasses.each do |klass|
293
- controller_name = klass.underscore
294
- namespaces << controller_name.split('/')[0...-1].join('/')
295
- end
296
- namespaces.delete('')
297
- namespaces
272
+ def empty?
273
+ routes.empty?
298
274
  end
299
275
 
300
- def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil)
301
- route = Route.new(app, conditions, requirements, defaults, name)
276
+ def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
277
+ route = Route.new(app, conditions, requirements, defaults, name, anchor)
302
278
  @set.add_route(*route)
303
279
  named_routes[name] = route if name
304
280
  routes << route
305
281
  route
306
282
  end
307
283
 
308
- def options_as_params(options)
309
- # If an explicit :controller was given, always make :action explicit
310
- # too, so that action expiry works as expected for things like
311
- #
312
- # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
313
- #
314
- # (the above is from the unit tests). In the above case, because the
315
- # controller was explicitly given, but no action, the action is implied to
316
- # be "index", not the recalled action of "show".
317
- #
318
- # great fun, eh?
319
-
320
- options_as_params = options.clone
321
- options_as_params[:action] ||= 'index' if options[:controller]
322
- options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action]
323
- options_as_params
324
- end
284
+ class Generator
285
+ attr_reader :options, :recall, :set, :script_name, :named_route
286
+
287
+ def initialize(options, recall, set, extras = false)
288
+ @script_name = options.delete(:script_name)
289
+ @named_route = options.delete(:use_route)
290
+ @options = options.dup
291
+ @recall = recall.dup
292
+ @set = set
293
+ @extras = extras
294
+
295
+ normalize_options!
296
+ normalize_controller_action_id!
297
+ use_relative_controller!
298
+ controller.sub!(%r{^/}, '') if controller
299
+ handle_nil_action!
300
+ end
301
+
302
+ def controller
303
+ @controller ||= @options[:controller]
304
+ end
305
+
306
+ def current_controller
307
+ @recall[:controller]
308
+ end
309
+
310
+ def use_recall_for(key)
311
+ if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
312
+ @options[key] = @recall.delete(key)
313
+ end
314
+ end
315
+
316
+ def normalize_options!
317
+ # If an explicit :controller was given, always make :action explicit
318
+ # too, so that action expiry works as expected for things like
319
+ #
320
+ # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
321
+ #
322
+ # (the above is from the unit tests). In the above case, because the
323
+ # controller was explicitly given, but no action, the action is implied to
324
+ # be "index", not the recalled action of "show".
325
+
326
+ if options[:controller]
327
+ options[:action] ||= 'index'
328
+ options[:controller] = options[:controller].to_s
329
+ end
330
+
331
+ if options[:action]
332
+ options[:action] = options[:action].to_s
333
+ end
334
+ end
335
+
336
+ # This pulls :controller, :action, and :id out of the recall.
337
+ # The recall key is only used if there is no key in the options
338
+ # or if the key in the options is identical. If any of
339
+ # :controller, :action or :id is not found, don't pull any
340
+ # more keys from the recall.
341
+ def normalize_controller_action_id!
342
+ @recall[:action] ||= 'index' if current_controller
343
+
344
+ use_recall_for(:controller) or return
345
+ use_recall_for(:action) or return
346
+ use_recall_for(:id)
347
+ end
348
+
349
+ # if the current controller is "foo/bar/baz" and :controller => "baz/bat"
350
+ # is specified, the controller becomes "foo/baz/bat"
351
+ def use_relative_controller!
352
+ if !named_route && different_controller?
353
+ old_parts = current_controller.split('/')
354
+ size = controller.count("/") + 1
355
+ parts = old_parts[0...-size] << controller
356
+ @controller = @options[:controller] = parts.join("/")
357
+ end
358
+ end
359
+
360
+ # This handles the case of :action => nil being explicitly passed.
361
+ # It is identical to :action => "index"
362
+ def handle_nil_action!
363
+ if options.has_key?(:action) && options[:action].nil?
364
+ options[:action] = 'index'
365
+ end
366
+ recall[:action] = options.delete(:action) if options[:action] == 'index'
367
+ end
368
+
369
+ def generate
370
+ error = ActionController::RoutingError.new("No route matches #{options.inspect}")
371
+ path, params = @set.generate(:path_info, named_route, options, recall, opts)
372
+
373
+ raise error unless path
374
+
375
+ params.reject! {|k,v| !v }
376
+
377
+ return [path, params.keys] if @extras
325
378
 
326
- def build_expiry(options, recall)
327
- recall.inject({}) do |expiry, (key, recalled_value)|
328
- expiry[key] = (options.key?(key) && options[key].to_param != recalled_value.to_param)
329
- expiry
379
+ path << "?#{params.to_query}" if params.any?
380
+ "#{script_name}#{path}"
381
+ rescue Rack::Mount::RoutingError
382
+ raise error
383
+ end
384
+
385
+ def opts
386
+ parameterize = lambda do |name, value|
387
+ if name == :controller
388
+ value
389
+ elsif value.is_a?(Array)
390
+ value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
391
+ else
392
+ Rack::Mount::Utils.escape_uri(value.to_param)
393
+ end
394
+ end
395
+ {:parameterize => parameterize}
396
+ end
397
+
398
+ def different_controller?
399
+ return false unless current_controller
400
+ controller.to_param != current_controller.to_param
330
401
  end
331
402
  end
332
403
 
@@ -337,85 +408,49 @@ module ActionDispatch
337
408
  end
338
409
 
339
410
  def generate_extras(options, recall={})
340
- generate(options, recall, :generate_extras)
411
+ generate(options, recall, true)
341
412
  end
342
413
 
343
- def generate(options, recall = {}, method = :generate)
344
- options, recall = options.dup, recall.dup
345
- named_route = options.delete(:use_route)
414
+ def generate(options, recall = {}, extras = false)
415
+ Generator.new(options, recall, @set, extras).generate
416
+ end
346
417
 
347
- options = options_as_params(options)
348
- expire_on = build_expiry(options, recall)
418
+ RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash]
349
419
 
350
- recall[:action] ||= 'index' if options[:controller] || recall[:controller]
420
+ def url_for(options)
421
+ finalize!
422
+ options = default_url_options.merge(options || {})
351
423
 
352
- if recall[:controller] && (!options.has_key?(:controller) || options[:controller] == recall[:controller])
353
- options[:controller] = recall.delete(:controller)
424
+ handle_positional_args(options)
354
425
 
355
- if recall[:action] && (!options.has_key?(:action) || options[:action] == recall[:action])
356
- options[:action] = recall.delete(:action)
426
+ rewritten_url = ""
357
427
 
358
- if recall[:id] && (!options.has_key?(:id) || options[:id] == recall[:id])
359
- options[:id] = recall.delete(:id)
360
- end
361
- end
362
- end
363
-
364
- options[:controller] = options[:controller].to_s if options[:controller]
428
+ path_segments = options.delete(:_path_segments)
365
429
 
366
- if !named_route && expire_on[:controller] && options[:controller] && options[:controller][0] != ?/
367
- old_parts = recall[:controller].split('/')
368
- new_parts = options[:controller].split('/')
369
- parts = old_parts[0..-(new_parts.length + 1)] + new_parts
370
- options[:controller] = parts.join('/')
371
- end
430
+ unless options[:only_path]
431
+ rewritten_url << (options[:protocol] || "http")
432
+ rewritten_url << "://" unless rewritten_url.match("://")
433
+ rewritten_url << rewrite_authentication(options)
372
434
 
373
- options[:controller] = options[:controller][1..-1] if options[:controller] && options[:controller][0] == ?/
435
+ raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
374
436
 
375
- merged = options.merge(recall)
376
- if options.has_key?(:action) && options[:action].nil?
377
- options.delete(:action)
378
- recall[:action] = 'index'
437
+ rewritten_url << options[:host]
438
+ rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
379
439
  end
380
- recall[:action] = options.delete(:action) if options[:action] == 'index'
381
-
382
- opts = {}
383
- opts[:parameterize] = lambda { |name, value|
384
- if name == :controller
385
- value
386
- elsif value.is_a?(Array)
387
- value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
388
- else
389
- Rack::Mount::Utils.escape_uri(value.to_param)
390
- end
391
- }
392
440
 
393
- unless result = @set.generate(:path_info, named_route, options, recall, opts)
394
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
395
- end
441
+ path_options = options.except(*RESERVED_OPTIONS)
442
+ path_options = yield(path_options) if block_given?
443
+ path = generate(path_options, path_segments || {})
396
444
 
397
- path, params = result
398
- params.each do |k, v|
399
- if v
400
- params[k] = v
401
- else
402
- params.delete(k)
403
- end
404
- end
445
+ # ROUTES TODO: This can be called directly, so script_name should probably be set in the router
446
+ rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
447
+ rewritten_url << "##{Rack::Utils.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
405
448
 
406
- if path && method == :generate_extras
407
- [path, params.keys]
408
- elsif path
409
- path << "?#{params.to_query}" if params.any?
410
- path
411
- else
412
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
413
- end
414
- rescue Rack::Mount::RoutingError
415
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
449
+ rewritten_url
416
450
  end
417
451
 
418
452
  def call(env)
453
+ finalize!
419
454
  @set.call(env)
420
455
  end
421
456
 
@@ -430,9 +465,9 @@ module ActionDispatch
430
465
  end
431
466
 
432
467
  req = Rack::Request.new(env)
433
- @set.recognize(req) do |route, params|
468
+ @set.recognize(req) do |route, matches, params|
434
469
  dispatcher = route.app
435
- if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params)
470
+ if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
436
471
  dispatcher.prepare_params!(params)
437
472
  return params
438
473
  end
@@ -440,6 +475,30 @@ module ActionDispatch
440
475
 
441
476
  raise ActionController::RoutingError, "No route matches #{path.inspect}"
442
477
  end
478
+
479
+ private
480
+ def handle_positional_args(options)
481
+ return unless args = options.delete(:_positional_args)
482
+
483
+ keys = options.delete(:_positional_keys)
484
+ keys -= options.keys if args.size < keys.size - 1 # take format into account
485
+
486
+ args = args.zip(keys).inject({}) do |h, (v, k)|
487
+ h[k] = v
488
+ h
489
+ end
490
+
491
+ # Tell url_for to skip default_url_options
492
+ options.merge!(args)
493
+ end
494
+
495
+ def rewrite_authentication(options)
496
+ if options[:user] && options[:password]
497
+ "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
498
+ else
499
+ ""
500
+ end
501
+ end
443
502
  end
444
503
  end
445
504
  end