actionpack 3.0.0.rc → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/CHANGELOG +11 -16
  2. data/README.rdoc +3 -3
  3. data/lib/abstract_controller/base.rb +28 -14
  4. data/lib/abstract_controller/callbacks.rb +22 -23
  5. data/lib/abstract_controller/helpers.rb +24 -7
  6. data/lib/abstract_controller/layouts.rb +12 -10
  7. data/lib/abstract_controller/view_paths.rb +7 -7
  8. data/lib/abstract_controller.rb +1 -0
  9. data/lib/action_controller/base.rb +165 -2
  10. data/lib/action_controller/deprecated/base.rb +11 -1
  11. data/lib/action_controller/deprecated/url_writer.rb +14 -0
  12. data/lib/action_controller/metal/http_authentication.rb +3 -3
  13. data/lib/action_controller/metal/responder.rb +2 -8
  14. data/lib/action_controller/middleware.rb +1 -1
  15. data/lib/action_controller/test_case.rb +2 -1
  16. data/lib/action_controller/vendor/html-scanner/html/document.rb +2 -2
  17. data/lib/action_controller/vendor/html-scanner/html/node.rb +29 -29
  18. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +25 -25
  19. data/lib/action_controller/vendor/html-scanner/html/selector.rb +1 -1
  20. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +8 -8
  21. data/lib/action_controller.rb +2 -1
  22. data/lib/action_dispatch/http/cache.rb +2 -2
  23. data/lib/action_dispatch/http/mime_negotiation.rb +1 -1
  24. data/lib/action_dispatch/http/mime_type.rb +10 -10
  25. data/lib/action_dispatch/http/parameters.rb +2 -2
  26. data/lib/action_dispatch/http/request.rb +7 -0
  27. data/lib/action_dispatch/http/url.rb +12 -2
  28. data/lib/action_dispatch/middleware/best_standards_support.rb +22 -0
  29. data/lib/action_dispatch/middleware/cookies.rb +29 -10
  30. data/lib/action_dispatch/middleware/flash.rb +7 -2
  31. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -5
  32. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +1 -0
  33. data/lib/action_dispatch/middleware/show_exceptions.rb +2 -9
  34. data/lib/action_dispatch/middleware/stack.rb +1 -1
  35. data/lib/action_dispatch/railtie.rb +2 -1
  36. data/lib/action_dispatch/routing/deprecated_mapper.rb +1 -0
  37. data/lib/action_dispatch/routing/mapper.rb +120 -137
  38. data/lib/action_dispatch/routing/polymorphic_routes.rb +10 -10
  39. data/lib/action_dispatch/routing/route_set.rb +8 -5
  40. data/lib/action_dispatch/routing/url_for.rb +1 -1
  41. data/lib/action_dispatch/routing.rb +4 -4
  42. data/lib/action_dispatch/testing/assertions/dom.rb +1 -1
  43. data/lib/action_dispatch/testing/assertions/response.rb +5 -5
  44. data/lib/action_dispatch/testing/assertions/routing.rb +39 -16
  45. data/lib/action_dispatch/testing/integration.rb +1 -0
  46. data/lib/action_dispatch.rb +1 -0
  47. data/lib/action_pack/version.rb +1 -2
  48. data/lib/action_view/base.rb +1 -0
  49. data/lib/action_view/helpers/asset_tag_helper.rb +12 -0
  50. data/lib/action_view/helpers/capture_helper.rb +1 -1
  51. data/lib/action_view/helpers/date_helper.rb +13 -17
  52. data/lib/action_view/helpers/debug_helper.rb +1 -1
  53. data/lib/action_view/helpers/form_helper.rb +16 -10
  54. data/lib/action_view/helpers/form_options_helper.rb +21 -18
  55. data/lib/action_view/helpers/form_tag_helper.rb +9 -9
  56. data/lib/action_view/helpers/number_helper.rb +1 -1
  57. data/lib/action_view/helpers/raw_output_helper.rb +1 -1
  58. data/lib/action_view/helpers/sanitize_helper.rb +4 -2
  59. data/lib/action_view/helpers/text_helper.rb +5 -2
  60. data/lib/action_view/helpers/translation_helper.rb +9 -9
  61. data/lib/action_view/helpers/url_helper.rb +2 -2
  62. data/lib/action_view/log_subscriber.rb +1 -1
  63. data/lib/action_view/render/layouts.rb +8 -4
  64. data/lib/action_view/render/partials.rb +1 -1
  65. data/lib/action_view/template/handler.rb +1 -1
  66. data/lib/action_view/template/handlers.rb +1 -1
  67. data/lib/action_view/template/resolver.rb +15 -0
  68. data/lib/action_view/template.rb +3 -3
  69. data/lib/action_view/test_case.rb +1 -1
  70. data/lib/action_view/testing/resolvers.rb +1 -1
  71. metadata +20 -23
@@ -1,3 +1,4 @@
1
+ require 'erb'
1
2
  require 'active_support/core_ext/hash/except'
2
3
  require 'active_support/core_ext/object/blank'
3
4
 
@@ -25,21 +26,27 @@ module ActionDispatch
25
26
  @constraints.each { |constraint|
26
27
  if constraint.respond_to?(:matches?) && !constraint.matches?(req)
27
28
  return [ 404, {'X-Cascade' => 'pass'}, [] ]
28
- elsif constraint.respond_to?(:call) && !constraint.call(req)
29
+ elsif constraint.respond_to?(:call) && !constraint.call(*constraint_args(constraint, req))
29
30
  return [ 404, {'X-Cascade' => 'pass'}, [] ]
30
31
  end
31
32
  }
32
33
 
33
34
  @app.call(env)
34
35
  end
36
+
37
+ private
38
+ def constraint_args(constraint, request)
39
+ constraint.arity == 1 ? [request] : [request.symbolized_path_parameters, request]
40
+ end
35
41
  end
36
42
 
37
43
  class Mapping #:nodoc:
38
44
  IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
39
45
 
40
- def initialize(set, scope, args)
41
- @set, @scope = set, scope
42
- @path, @options = extract_path_and_options(args)
46
+ def initialize(set, scope, path, options)
47
+ @set, @scope = set, scope
48
+ @options = (@scope[:options] || {}).merge(options)
49
+ @path = normalize_path(path)
43
50
  normalize_options!
44
51
  end
45
52
 
@@ -48,28 +55,6 @@ module ActionDispatch
48
55
  end
49
56
 
50
57
  private
51
- def extract_path_and_options(args)
52
- options = args.extract_options!
53
-
54
- if using_to_shorthand?(args, options)
55
- path, to = options.find { |name, value| name.is_a?(String) }
56
- options.merge!(:to => to).delete(path) if path
57
- else
58
- path = args.first
59
- end
60
-
61
- if path.match(':controller')
62
- raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
63
-
64
- # Add a default constraint for :controller path segments that matches namespaced
65
- # controllers with default routes like :controller/:action/:id(.:format), e.g:
66
- # GET /admin/products/show/1
67
- # => { :controller => 'admin/products', :action => 'show', :id => '1' }
68
- options.reverse_merge!(:controller => /.+?/)
69
- end
70
-
71
- [ normalize_path(path), options ]
72
- end
73
58
 
74
59
  def normalize_options!
75
60
  path_without_format = @path.sub(/\(\.:format\)$/, '')
@@ -77,25 +62,39 @@ module ActionDispatch
77
62
  if using_match_shorthand?(path_without_format, @options)
78
63
  to_shorthand = @options[:to].blank?
79
64
  @options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1')
80
- @options[:as] ||= path_without_format[1..-1].gsub("/", "_")
65
+ @options[:as] ||= Mapper.normalize_name(path_without_format)
81
66
  end
82
67
 
83
68
  @options.merge!(default_controller_and_action(to_shorthand))
84
69
  end
85
70
 
86
- # match "account" => "account#index"
87
- def using_to_shorthand?(args, options)
88
- args.empty? && options.present?
89
- end
90
-
91
71
  # match "account/overview"
92
72
  def using_match_shorthand?(path, options)
93
73
  path && options.except(:via, :anchor, :to, :as).empty? && path =~ %r{^/[\w\/]+$}
94
74
  end
95
75
 
96
76
  def normalize_path(path)
97
- raise ArgumentError, "path is required" if @scope[:path].blank? && path.blank?
98
- Mapper.normalize_path("#{@scope[:path]}/#{path}")
77
+ raise ArgumentError, "path is required" if path.blank?
78
+ path = Mapper.normalize_path(path)
79
+
80
+ if path.match(':controller')
81
+ raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
82
+
83
+ # Add a default constraint for :controller path segments that matches namespaced
84
+ # controllers with default routes like :controller/:action/:id(.:format), e.g:
85
+ # GET /admin/products/show/1
86
+ # => { :controller => 'admin/products', :action => 'show', :id => '1' }
87
+ @options.reverse_merge!(:controller => /.+?/)
88
+ end
89
+
90
+ if @options[:format] == false
91
+ @options.delete(:format)
92
+ path
93
+ elsif path.include?(":format")
94
+ path
95
+ else
96
+ "#{path}(.:format)"
97
+ end
99
98
  end
100
99
 
101
100
  def app
@@ -218,6 +217,10 @@ module ActionDispatch
218
217
  path
219
218
  end
220
219
 
220
+ def self.normalize_name(name)
221
+ normalize_path(name)[1..-1].gsub("/", "_")
222
+ end
223
+
221
224
  module Base
222
225
  def initialize(set) #:nodoc:
223
226
  @set = set
@@ -227,8 +230,8 @@ module ActionDispatch
227
230
  match '/', options.reverse_merge(:as => :root)
228
231
  end
229
232
 
230
- def match(*args)
231
- mapping = Mapping.new(@set, @scope, args).to_route
233
+ def match(path, options=nil)
234
+ mapping = Mapping.new(@set, @scope, path, options || {}).to_route
232
235
  @set.add_route(*mapping)
233
236
  self
234
237
  end
@@ -244,7 +247,7 @@ module ActionDispatch
244
247
 
245
248
  raise "A rack application must be specified" unless path
246
249
 
247
- match(path, options.merge(:to => app, :anchor => false))
250
+ match(path, options.merge(:to => app, :anchor => false, :format => false))
248
251
  self
249
252
  end
250
253
 
@@ -277,7 +280,6 @@ module ActionDispatch
277
280
  path = args.shift || block
278
281
  path_proc = path.is_a?(Proc) ? path : proc { |params| path % params }
279
282
  status = options[:status] || 301
280
- body = 'Moved Permanently'
281
283
 
282
284
  lambda do |env|
283
285
  req = Request.new(env)
@@ -288,13 +290,16 @@ module ActionDispatch
288
290
  uri = URI.parse(path_proc.call(*params))
289
291
  uri.scheme ||= req.scheme
290
292
  uri.host ||= req.host
291
- uri.port ||= req.port unless req.port == 80
293
+ uri.port ||= req.port unless req.standard_port?
294
+
295
+ body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
292
296
 
293
297
  headers = {
294
298
  'Location' => uri.to_s,
295
299
  'Content-Type' => 'text/html',
296
300
  'Content-Length' => body.length.to_s
297
301
  }
302
+
298
303
  [ status, headers, [body] ]
299
304
  end
300
305
  end
@@ -324,13 +329,7 @@ module ActionDispatch
324
329
  ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller
325
330
  end
326
331
 
327
- case args.first
328
- when String
329
- options[:path] = args.first
330
- when Symbol
331
- options[:controller] = args.first
332
- end
333
-
332
+ options[:path] = args.first if args.first.is_a?(String)
334
333
  recover = {}
335
334
 
336
335
  options[:constraints] ||= {}
@@ -362,8 +361,9 @@ module ActionDispatch
362
361
  @scope[:blocks] = recover[:block]
363
362
  end
364
363
 
365
- def controller(controller)
366
- scope(controller.to_sym) { yield }
364
+ def controller(controller, options={})
365
+ options[:controller] = controller
366
+ scope(options) { yield }
367
367
  end
368
368
 
369
369
  def namespace(path, options = {})
@@ -381,21 +381,6 @@ module ActionDispatch
381
381
  scope(:defaults => defaults) { yield }
382
382
  end
383
383
 
384
- def match(*args)
385
- options = args.extract_options!
386
-
387
- options = (@scope[:options] || {}).merge(options)
388
-
389
- if @scope[:as] && !options[:as].blank?
390
- options[:as] = "#{@scope[:as]}_#{options[:as]}"
391
- elsif @scope[:as] && options[:as] == ""
392
- options[:as] = @scope[:as].to_s
393
- end
394
-
395
- args.push(options)
396
- super(*args)
397
- end
398
-
399
384
  private
400
385
  def scope_options
401
386
  @scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
@@ -459,9 +444,9 @@ module ActionDispatch
459
444
  module Resources
460
445
  # CANONICAL_ACTIONS holds all actions that does not need a prefix or
461
446
  # a path appended since they fit properly in their scope level.
462
- VALID_ON_OPTIONS = [:new, :collection, :member]
463
- CANONICAL_ACTIONS = [:index, :create, :new, :show, :update, :destroy]
464
- RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except]
447
+ VALID_ON_OPTIONS = [:new, :collection, :member]
448
+ RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except]
449
+ CANONICAL_ACTIONS = %w(index create new show update destroy)
465
450
 
466
451
  class Resource #:nodoc:
467
452
  DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit]
@@ -470,7 +455,7 @@ module ActionDispatch
470
455
 
471
456
  def initialize(entities, options = {})
472
457
  @name = entities.to_s
473
- @path = options.delete(:path) || @name
458
+ @path = (options.delete(:path) || @name).to_s
474
459
  @controller = (options.delete(:controller) || @name).to_s
475
460
  @as = options.delete(:as)
476
461
  @options = options
@@ -495,16 +480,14 @@ module ActionDispatch
495
480
  end
496
481
 
497
482
  def plural
498
- name.to_s.pluralize
483
+ @plural ||= name.to_s
499
484
  end
500
485
 
501
486
  def singular
502
- name.to_s.singularize
487
+ @singular ||= name.to_s.singularize
503
488
  end
504
489
 
505
- def member_name
506
- singular
507
- end
490
+ alias :member_name :singular
508
491
 
509
492
  # Checks for uncountable plurals, and appends "_index" if they're.
510
493
  def collection_name
@@ -515,9 +498,7 @@ module ActionDispatch
515
498
  { :controller => controller }
516
499
  end
517
500
 
518
- def collection_scope
519
- path
520
- end
501
+ alias :collection_scope :path
521
502
 
522
503
  def member_scope
523
504
  "#{path}/:id"
@@ -538,21 +519,25 @@ module ActionDispatch
538
519
 
539
520
  def initialize(entities, options)
540
521
  @name = entities.to_s
541
- @path = options.delete(:path) || @name
522
+ @path = (options.delete(:path) || @name).to_s
542
523
  @controller = (options.delete(:controller) || plural).to_s
543
524
  @as = options.delete(:as)
544
525
  @options = options
545
526
  end
546
527
 
547
- def member_name
548
- name
528
+ def plural
529
+ @plural ||= name.to_s.pluralize
549
530
  end
550
- alias :collection_name :member_name
551
531
 
552
- def member_scope
553
- path
532
+ def singular
533
+ @singular ||= name.to_s
554
534
  end
555
- alias :nested_scope :member_scope
535
+
536
+ alias :member_name :singular
537
+ alias :collection_name :singular
538
+
539
+ alias :member_scope :path
540
+ alias :nested_scope :path
556
541
  end
557
542
 
558
543
  def initialize(*args) #:nodoc:
@@ -583,10 +568,10 @@ module ActionDispatch
583
568
  end if parent_resource.actions.include?(:new)
584
569
 
585
570
  member_scope do
571
+ get :edit if parent_resource.actions.include?(:edit)
586
572
  get :show if parent_resource.actions.include?(:show)
587
573
  put :update if parent_resource.actions.include?(:update)
588
574
  delete :destroy if parent_resource.actions.include?(:destroy)
589
- get :edit if parent_resource.actions.include?(:edit)
590
575
  end
591
576
  end
592
577
 
@@ -613,10 +598,10 @@ module ActionDispatch
613
598
  end if parent_resource.actions.include?(:new)
614
599
 
615
600
  member_scope do
601
+ get :edit if parent_resource.actions.include?(:edit)
616
602
  get :show if parent_resource.actions.include?(:show)
617
603
  put :update if parent_resource.actions.include?(:update)
618
604
  delete :destroy if parent_resource.actions.include?(:destroy)
619
- get :edit if parent_resource.actions.include?(:edit)
620
605
  end
621
606
  end
622
607
 
@@ -710,46 +695,31 @@ module ActionDispatch
710
695
  raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
711
696
  end
712
697
 
713
- if @scope[:scope_level] == :resource
698
+ if @scope[:scope_level] == :resources
699
+ args.push(options)
700
+ return nested { match(*args) }
701
+ elsif @scope[:scope_level] == :resource
714
702
  args.push(options)
715
703
  return member { match(*args) }
716
704
  end
717
705
 
718
- path = options.delete(:path)
719
706
  action = args.first
707
+ path = path_for_action(action, options.delete(:path))
720
708
 
721
- if action.is_a?(Symbol)
722
- path = path_for_action(action, path)
723
- options[:to] ||= action
724
- options[:as] = name_for_action(action, options[:as])
725
-
726
- with_exclusive_scope do
727
- return super(path, options)
728
- end
729
- elsif resource_method_scope?
730
- path = path_for_custom_action
731
- options[:as] = name_for_action(options[:as]) if options[:as]
732
- args.push(options)
733
-
734
- with_exclusive_scope do
735
- scope(path) do
736
- return super
737
- end
738
- end
739
- end
740
-
741
- if resource_scope?
742
- raise ArgumentError, "can't define route directly in resource(s) scope"
709
+ if action.to_s =~ /^[\w\/]+$/
710
+ options[:action] ||= action unless action.to_s.include?("/")
711
+ options[:as] = name_for_action(action, options[:as])
712
+ else
713
+ options[:as] = name_for_action(options[:as])
743
714
  end
744
715
 
745
- args.push(options)
746
- super
716
+ super(path, options)
747
717
  end
748
718
 
749
719
  def root(options={})
750
720
  if @scope[:scope_level] == :resources
751
- with_scope_level(:nested) do
752
- scope(parent_resource.path, :as => parent_resource.collection_name) do
721
+ with_scope_level(:root) do
722
+ scope(parent_resource.path) do
753
723
  super(options)
754
724
  end
755
725
  end
@@ -770,6 +740,10 @@ module ActionDispatch
770
740
  return true
771
741
  end
772
742
 
743
+ options.keys.each do |k|
744
+ (options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
745
+ end
746
+
773
747
  scope_options = options.slice!(*RESOURCE_OPTIONS)
774
748
  unless scope_options.empty?
775
749
  scope(scope_options) do
@@ -882,7 +856,7 @@ module ActionDispatch
882
856
  end
883
857
 
884
858
  def canonical_action?(action, flag)
885
- flag && CANONICAL_ACTIONS.include?(action)
859
+ flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
886
860
  end
887
861
 
888
862
  def shallow_scoping?
@@ -893,18 +867,10 @@ module ActionDispatch
893
867
  prefix = shallow_scoping? ?
894
868
  "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
895
869
 
896
- if canonical_action?(action, path.blank?)
897
- "#{prefix}(.:format)"
898
- else
899
- "#{prefix}/#{action_path(action, path)}(.:format)"
900
- end
901
- end
902
-
903
- def path_for_custom_action
904
- if shallow_scoping?
905
- "#{@scope[:shallow_path]}/#{parent_resource.path}/:id"
870
+ path = if canonical_action?(action, path.blank?)
871
+ prefix.to_s
906
872
  else
907
- @scope[:path]
873
+ "#{prefix}/#{action_path(action, path)}"
908
874
  end
909
875
  end
910
876
 
@@ -914,44 +880,61 @@ module ActionDispatch
914
880
 
915
881
  def prefix_name_for_action(action, as)
916
882
  if as.present?
917
- "#{as}_"
883
+ as.to_s
918
884
  elsif as
919
- ""
885
+ nil
920
886
  elsif !canonical_action?(action, @scope[:scope_level])
921
- "#{action}_"
887
+ action.to_s
922
888
  end
923
889
  end
924
890
 
925
891
  def name_for_action(action, as=nil)
926
892
  prefix = prefix_name_for_action(action, as)
893
+ prefix = Mapper.normalize_name(prefix) if prefix
927
894
  name_prefix = @scope[:as]
928
895
 
929
896
  if parent_resource
930
897
  collection_name = parent_resource.collection_name
931
898
  member_name = parent_resource.member_name
932
- name_prefix = "#{name_prefix}_" if name_prefix.present?
933
899
  end
934
900
 
935
- case @scope[:scope_level]
901
+ name = case @scope[:scope_level]
902
+ when :nested
903
+ [member_name, prefix]
936
904
  when :collection
937
- "#{prefix}#{name_prefix}#{collection_name}"
905
+ [prefix, name_prefix, collection_name]
938
906
  when :new
939
- "#{prefix}new_#{name_prefix}#{member_name}"
907
+ [prefix, :new, name_prefix, member_name]
908
+ when :member
909
+ [prefix, shallow_scoping? ? @scope[:shallow_prefix] : name_prefix, member_name]
910
+ when :root
911
+ [name_prefix, collection_name, prefix]
940
912
  else
941
- if shallow_scoping?
942
- shallow_prefix = "#{@scope[:shallow_prefix]}_" if @scope[:shallow_prefix].present?
943
- "#{prefix}#{shallow_prefix}#{member_name}"
944
- else
945
- "#{prefix}#{name_prefix}#{member_name}"
946
- end
913
+ [name_prefix, member_name, prefix]
947
914
  end
915
+
916
+ name.select(&:present?).join("_").presence
948
917
  end
949
918
  end
950
919
 
920
+ module Shorthand
921
+ def match(*args)
922
+ if args.size == 1 && args.last.is_a?(Hash)
923
+ options = args.pop
924
+ path, to = options.find { |name, value| name.is_a?(String) }
925
+ options.merge!(:to => to).delete(path)
926
+ super(path, options)
927
+ else
928
+ super
929
+ end
930
+ end
931
+ end
932
+
951
933
  include Base
952
934
  include HttpHelpers
953
935
  include Scoping
954
936
  include Resources
937
+ include Shorthand
955
938
  end
956
939
  end
957
940
  end
@@ -148,29 +148,29 @@ module ActionDispatch
148
148
  def build_named_route_call(records, inflection, options = {})
149
149
  unless records.is_a?(Array)
150
150
  record = extract_record(records)
151
- route = ''
151
+ route = []
152
152
  else
153
153
  record = records.pop
154
- route = records.inject("") do |string, parent|
154
+ route = records.map do |parent|
155
155
  if parent.is_a?(Symbol) || parent.is_a?(String)
156
- string << "#{parent}_"
156
+ parent
157
157
  else
158
- string << ActiveModel::Naming.plural(parent).singularize
159
- string << "_"
158
+ ActiveModel::Naming.plural(parent).singularize
160
159
  end
161
160
  end
162
161
  end
163
162
 
164
163
  if record.is_a?(Symbol) || record.is_a?(String)
165
- route << "#{record}_"
164
+ route << record
166
165
  else
167
166
  route << ActiveModel::Naming.plural(record)
168
- route = route.singularize if inflection == :singular
169
- route << "_"
170
- route << "index_" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
167
+ route = [route.join("_").singularize] if inflection == :singular
168
+ route << "index" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
171
169
  end
172
170
 
173
- action_prefix(options) + route + routing_type(options).to_s
171
+ route << routing_type(options)
172
+
173
+ action_prefix(options) + route.join("_")
174
174
  end
175
175
 
176
176
  def extract_record(record_or_hash_or_array)
@@ -392,10 +392,9 @@ module ActionDispatch
392
392
  end
393
393
 
394
394
  def generate
395
- error = ActionController::RoutingError.new("No route matches #{options.inspect}")
396
395
  path, params = @set.set.generate(:path_info, named_route, options, recall, opts)
397
396
 
398
- raise error unless path
397
+ raise_routing_error unless path
399
398
 
400
399
  params.reject! {|k,v| !v }
401
400
 
@@ -404,7 +403,7 @@ module ActionDispatch
404
403
  path << "?#{params.to_query}" if params.any?
405
404
  "#{script_name}#{path}"
406
405
  rescue Rack::Mount::RoutingError
407
- raise error
406
+ raise_routing_error
408
407
  end
409
408
 
410
409
  def opts
@@ -421,6 +420,10 @@ module ActionDispatch
421
420
  {:parameterize => parameterize}
422
421
  end
423
422
 
423
+ def raise_routing_error
424
+ raise ActionController::RoutingError.new("No route matches #{options.inspect}")
425
+ end
426
+
424
427
  def different_controller?
425
428
  return false unless current_controller
426
429
  controller.to_param != current_controller.to_param
@@ -491,7 +494,7 @@ module ActionDispatch
491
494
 
492
495
  def recognize_path(path, environment = {})
493
496
  method = (environment[:method] || "GET").to_s.upcase
494
- path = Rack::Mount::Utils.normalize_path(path)
497
+ path = Rack::Mount::Utils.normalize_path(path) unless path =~ %r{://}
495
498
 
496
499
  begin
497
500
  env = Rack::MockRequest.env_for(path, {:method => method})
@@ -499,7 +502,7 @@ module ActionDispatch
499
502
  raise ActionController::RoutingError, e.message
500
503
  end
501
504
 
502
- req = Rack::Request.new(env)
505
+ req = @request_class.new(env)
503
506
  @set.recognize(req) do |route, matches, params|
504
507
  params.each do |key, value|
505
508
  if value.is_a?(String)
@@ -20,7 +20,7 @@ module ActionDispatch
20
20
  # <%= link_to('Click here', :controller => 'users',
21
21
  # :action => 'new', :message => 'Welcome!') %>
22
22
  #
23
- # #=> Generates a link to: /users/new?message=Welcome%21
23
+ # # Generates a link to /users/new?message=Welcome%21
24
24
  #
25
25
  # link_to, and all other functions that require URL generation functionality,
26
26
  # actually use ActionController::UrlFor under the hood. And in particular,
@@ -15,7 +15,7 @@ module ActionDispatch
15
15
  # match ':controller(/:action(/:id(.:format)))'
16
16
  #
17
17
  # This route states that it expects requests to consist of a
18
- # <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
18
+ # <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
19
19
  # turn is followed optionally by an <tt>:id</tt>, which in turn is followed
20
20
  # optionally by a <tt>:format</tt>
21
21
  #
@@ -134,8 +134,8 @@ module ActionDispatch
134
134
  # == HTTP Methods
135
135
  #
136
136
  # Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method.
137
- # Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
138
- # If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
137
+ # Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
138
+ # If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
139
139
  # The default value is <tt>:any</tt> which means that the route will respond to any of the HTTP methods.
140
140
  #
141
141
  # Examples:
@@ -144,7 +144,7 @@ module ActionDispatch
144
144
  # match 'post/:id' => "posts#create_comment', :via => :post
145
145
  #
146
146
  # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
147
- # URL will route to the <tt>show</tt> action.
147
+ # URL will route to the <tt>show</tt> action.
148
148
  #
149
149
  # === HTTP helper methods
150
150
  #
@@ -3,7 +3,7 @@ require 'action_controller/vendor/html-scanner'
3
3
  module ActionDispatch
4
4
  module Assertions
5
5
  module DomAssertions
6
- # Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
6
+ # \Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
7
7
  #
8
8
  # ==== Examples
9
9
  #
@@ -1,6 +1,6 @@
1
1
  module ActionDispatch
2
2
  module Assertions
3
- # A small suite of assertions that test responses from Rails applications.
3
+ # A small suite of assertions that test responses from \Rails applications.
4
4
  module ResponseAssertions
5
5
  extend ActiveSupport::Concern
6
6
 
@@ -18,8 +18,8 @@ module ActionDispatch
18
18
  # * <tt>:missing</tt> - Status code was 404
19
19
  # * <tt>:error</tt> - Status code was in the 500-599 range
20
20
  #
21
- # You can also pass an explicit status number like assert_response(501)
22
- # or its symbolic equivalent assert_response(:not_implemented).
21
+ # You can also pass an explicit status number like <tt>assert_response(501)</tt>
22
+ # or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
23
23
  # See ActionDispatch::StatusCodes for a full list.
24
24
  #
25
25
  # ==== Examples
@@ -45,8 +45,8 @@ module ActionDispatch
45
45
  end
46
46
 
47
47
  # Assert that the redirection options passed in match those of the redirect called in the latest action.
48
- # This match can be partial, such that assert_redirected_to(:controller => "weblog") will also
49
- # match the redirection of redirect_to(:controller => "weblog", :action => "show") and so on.
48
+ # This match can be partial, such that <tt>assert_redirected_to(:controller => "weblog")</tt> will also
49
+ # match the redirection of <tt>redirect_to(:controller => "weblog", :action => "show")</tt> and so on.
50
50
  #
51
51
  # ==== Examples
52
52
  #