actionpack 3.0.20 → 3.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (161) hide show
  1. data/CHANGELOG +88 -142
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +5 -6
  4. data/lib/abstract_controller.rb +1 -0
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +24 -19
  7. data/lib/abstract_controller/callbacks.rb +19 -19
  8. data/lib/abstract_controller/helpers.rb +11 -13
  9. data/lib/abstract_controller/layouts.rb +4 -5
  10. data/lib/abstract_controller/railties/routes_helpers.rb +18 -0
  11. data/lib/abstract_controller/rendering.rb +34 -31
  12. data/lib/abstract_controller/url_for.rb +27 -0
  13. data/lib/abstract_controller/view_paths.rb +31 -6
  14. data/lib/action_controller.rb +5 -3
  15. data/lib/action_controller/base.rb +15 -16
  16. data/lib/action_controller/caching.rb +2 -2
  17. data/lib/action_controller/caching/actions.rb +11 -12
  18. data/lib/action_controller/caching/fragments.rb +41 -19
  19. data/lib/action_controller/caching/pages.rb +3 -9
  20. data/lib/action_controller/caching/sweeping.rb +0 -1
  21. data/lib/action_controller/deprecated.rb +1 -1
  22. data/lib/action_controller/log_subscriber.rb +1 -1
  23. data/lib/action_controller/metal.rb +78 -20
  24. data/lib/action_controller/metal/compatibility.rb +0 -9
  25. data/lib/action_controller/metal/conditional_get.rb +9 -9
  26. data/lib/action_controller/metal/data_streaming.rb +145 -0
  27. data/lib/action_controller/metal/force_ssl.rb +35 -0
  28. data/lib/action_controller/metal/head.rb +1 -1
  29. data/lib/action_controller/metal/helpers.rb +37 -44
  30. data/lib/action_controller/metal/hide_actions.rb +2 -3
  31. data/lib/action_controller/metal/http_authentication.rb +41 -38
  32. data/lib/action_controller/metal/implicit_render.rb +13 -13
  33. data/lib/action_controller/metal/instrumentation.rb +2 -2
  34. data/lib/action_controller/metal/mime_responds.rb +25 -19
  35. data/lib/action_controller/metal/params_wrapper.rb +224 -0
  36. data/lib/action_controller/metal/redirecting.rb +6 -2
  37. data/lib/action_controller/metal/renderers.rb +50 -36
  38. data/lib/action_controller/metal/rendering.rb +34 -25
  39. data/lib/action_controller/metal/request_forgery_protection.rb +18 -36
  40. data/lib/action_controller/metal/responder.rb +47 -12
  41. data/lib/action_controller/metal/streaming.rb +244 -138
  42. data/lib/action_controller/metal/testing.rb +0 -9
  43. data/lib/action_controller/metal/url_for.rb +12 -14
  44. data/lib/action_controller/railtie.rb +19 -37
  45. data/lib/action_controller/railties/paths.rb +24 -0
  46. data/lib/action_controller/record_identifier.rb +4 -10
  47. data/lib/action_controller/test_case.rb +36 -19
  48. data/lib/action_controller/vendor/html-scanner/html/node.rb +5 -5
  49. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +3 -3
  50. data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -0
  51. data/lib/action_dispatch.rb +4 -1
  52. data/lib/action_dispatch/http/cache.rb +5 -32
  53. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  54. data/lib/action_dispatch/http/mime_negotiation.rb +22 -3
  55. data/lib/action_dispatch/http/mime_type.rb +45 -5
  56. data/lib/action_dispatch/http/rack_cache.rb +58 -0
  57. data/lib/action_dispatch/http/request.rb +27 -41
  58. data/lib/action_dispatch/http/response.rb +56 -54
  59. data/lib/action_dispatch/http/upload.rb +1 -11
  60. data/lib/action_dispatch/http/url.rb +102 -42
  61. data/lib/action_dispatch/middleware/callbacks.rb +8 -25
  62. data/lib/action_dispatch/middleware/closed_error.rb +7 -0
  63. data/lib/action_dispatch/middleware/cookies.rb +37 -15
  64. data/lib/action_dispatch/middleware/flash.rb +80 -11
  65. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  66. data/lib/action_dispatch/middleware/reloader.rb +76 -0
  67. data/lib/action_dispatch/middleware/session/abstract_store.rb +56 -226
  68. data/lib/action_dispatch/middleware/session/cookie_store.rb +20 -44
  69. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -46
  70. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -2
  71. data/lib/action_dispatch/middleware/stack.rb +50 -17
  72. data/lib/action_dispatch/middleware/static.rb +41 -29
  73. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +3 -3
  74. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  75. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +3 -3
  76. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +4 -2
  77. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +2 -6
  78. data/lib/action_dispatch/railtie.rb +8 -0
  79. data/lib/action_dispatch/routing.rb +13 -1
  80. data/lib/action_dispatch/routing/mapper.rb +345 -227
  81. data/lib/action_dispatch/routing/polymorphic_routes.rb +33 -13
  82. data/lib/action_dispatch/routing/redirection.rb +110 -0
  83. data/lib/action_dispatch/routing/route.rb +15 -13
  84. data/lib/action_dispatch/routing/route_set.rb +116 -90
  85. data/lib/action_dispatch/routing/routes_proxy.rb +35 -0
  86. data/lib/action_dispatch/routing/url_for.rb +25 -1
  87. data/lib/action_dispatch/testing/assertions/response.rb +8 -10
  88. data/lib/action_dispatch/testing/assertions/routing.rb +15 -15
  89. data/lib/action_dispatch/testing/assertions/selector.rb +13 -220
  90. data/lib/action_dispatch/testing/integration.rb +37 -28
  91. data/lib/action_dispatch/testing/performance_test.rb +1 -3
  92. data/lib/action_dispatch/testing/test_process.rb +1 -1
  93. data/lib/action_dispatch/testing/test_request.rb +9 -3
  94. data/lib/action_dispatch/testing/test_response.rb +4 -111
  95. data/lib/action_pack.rb +1 -1
  96. data/lib/action_pack/version.rb +3 -3
  97. data/lib/action_view.rb +39 -24
  98. data/lib/action_view/base.rb +61 -86
  99. data/lib/action_view/buffers.rb +43 -0
  100. data/lib/action_view/context.rb +21 -24
  101. data/lib/action_view/flows.rb +79 -0
  102. data/lib/action_view/helpers.rb +8 -6
  103. data/lib/action_view/helpers/active_model_helper.rb +0 -23
  104. data/lib/action_view/helpers/asset_paths.rb +79 -0
  105. data/lib/action_view/helpers/asset_tag_helper.rb +30 -500
  106. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +147 -0
  107. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +101 -0
  108. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +200 -0
  109. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +152 -0
  110. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  111. data/lib/action_view/helpers/cache_helper.rb +11 -19
  112. data/lib/action_view/helpers/capture_helper.rb +19 -8
  113. data/lib/action_view/helpers/controller_helper.rb +21 -0
  114. data/lib/action_view/helpers/csrf_helper.rb +22 -4
  115. data/lib/action_view/helpers/date_helper.rb +36 -22
  116. data/lib/action_view/helpers/form_helper.rb +199 -113
  117. data/lib/action_view/helpers/form_options_helper.rb +10 -11
  118. data/lib/action_view/helpers/form_tag_helper.rb +94 -22
  119. data/lib/action_view/helpers/javascript_helper.rb +24 -107
  120. data/lib/action_view/helpers/number_helper.rb +36 -33
  121. data/lib/action_view/helpers/output_safety_helper.rb +38 -0
  122. data/lib/action_view/helpers/record_tag_helper.rb +6 -6
  123. data/lib/action_view/helpers/rendering_helper.rb +90 -0
  124. data/lib/action_view/helpers/sanitize_helper.rb +2 -2
  125. data/lib/action_view/helpers/sprockets_helper.rb +69 -0
  126. data/lib/action_view/helpers/tag_helper.rb +34 -12
  127. data/lib/action_view/helpers/text_helper.rb +30 -145
  128. data/lib/action_view/helpers/translation_helper.rb +10 -17
  129. data/lib/action_view/helpers/url_helper.rb +70 -67
  130. data/lib/action_view/locale/en.yml +1 -1
  131. data/lib/action_view/lookup_context.rb +36 -14
  132. data/lib/action_view/{paths.rb → path_set.rb} +9 -8
  133. data/lib/action_view/railtie.rb +12 -4
  134. data/lib/action_view/renderer/abstract_renderer.rb +36 -0
  135. data/lib/action_view/{render/partials.rb → renderer/partial_renderer.rb} +147 -146
  136. data/lib/action_view/renderer/renderer.rb +54 -0
  137. data/lib/action_view/renderer/streaming_template_renderer.rb +106 -0
  138. data/lib/action_view/renderer/template_renderer.rb +74 -0
  139. data/lib/action_view/template.rb +91 -54
  140. data/lib/action_view/template/error.rb +11 -8
  141. data/lib/action_view/template/handler.rb +9 -1
  142. data/lib/action_view/template/handlers.rb +9 -9
  143. data/lib/action_view/template/handlers/builder.rb +4 -4
  144. data/lib/action_view/template/handlers/erb.rb +21 -41
  145. data/lib/action_view/template/resolver.rb +171 -57
  146. data/lib/action_view/template/text.rb +0 -4
  147. data/lib/action_view/test_case.rb +32 -16
  148. data/lib/action_view/testing/resolvers.rb +16 -10
  149. data/lib/sprockets/railtie.rb +100 -0
  150. metadata +162 -140
  151. checksums.yaml +0 -7
  152. data/lib/action_controller/deprecated/base.rb +0 -143
  153. data/lib/action_controller/deprecated/dispatcher.rb +0 -28
  154. data/lib/action_controller/deprecated/url_writer.rb +0 -14
  155. data/lib/action_dispatch/routing/deprecated_mapper.rb +0 -525
  156. data/lib/action_view/helpers/prototype_helper.rb +0 -851
  157. data/lib/action_view/helpers/raw_output_helper.rb +0 -18
  158. data/lib/action_view/helpers/scriptaculous_helper.rb +0 -263
  159. data/lib/action_view/render/layouts.rb +0 -83
  160. data/lib/action_view/render/rendering.rb +0 -67
  161. data/lib/action_view/template/handlers/rjs.rb +0 -17
@@ -42,6 +42,18 @@ module ActionDispatch
42
42
  #
43
43
  # edit_polymorphic_path(@post) # => "/posts/1/edit"
44
44
  # polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf"
45
+ #
46
+ # == Using with mounted engines
47
+ #
48
+ # If you use mounted engine, there is a possibility that you will need to use
49
+ # polymorphic_url pointing at engine's routes. To do that, just pass proxy used
50
+ # to reach engine's routes as a first argument:
51
+ #
52
+ # For example:
53
+ #
54
+ # polymorphic_url([blog, @post]) # it will call blog.post_path(@post)
55
+ # form_for([blog, @post]) # => "/blog/posts/1
56
+ #
45
57
  module PolymorphicRoutes
46
58
  # Constructs a call to a named RESTful route for the given record and returns the
47
59
  # resulting URL string. For example:
@@ -78,19 +90,20 @@ module ActionDispatch
78
90
  def polymorphic_url(record_or_hash_or_array, options = {})
79
91
  if record_or_hash_or_array.kind_of?(Array)
80
92
  record_or_hash_or_array = record_or_hash_or_array.compact
93
+ if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
94
+ proxy = record_or_hash_or_array.shift
95
+ end
81
96
  record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
82
97
  end
83
98
 
84
99
  record = extract_record(record_or_hash_or_array)
85
100
  record = record.to_model if record.respond_to?(:to_model)
86
101
 
87
- args = case record_or_hash_or_array
88
- when Hash; [ record_or_hash_or_array ]
89
- when Array; record_or_hash_or_array.dup
90
- else [ record_or_hash_or_array ]
91
- end
102
+ args = Array === record_or_hash_or_array ?
103
+ record_or_hash_or_array.dup :
104
+ [ record_or_hash_or_array ]
92
105
 
93
- inflection = if options[:action].to_s == "new"
106
+ inflection = if options[:action] && options[:action].to_s == "new"
94
107
  args.pop
95
108
  :singular
96
109
  elsif (record.respond_to?(:persisted?) && !record.persisted?)
@@ -111,7 +124,14 @@ module ActionDispatch
111
124
  args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
112
125
  end
113
126
 
114
- send(named_route, *args)
127
+ if proxy
128
+ proxy.send(named_route, *args)
129
+ else
130
+ # we need to use url_for, because polymorphic_url can be used in context of other than
131
+ # current routes (e.g. engine's routes). As named routes from engine are not included
132
+ # calling engine's named route directly would fail.
133
+ url_for _routes.url_helpers.__send__("hash_for_#{named_route}", *args)
134
+ end
115
135
  end
116
136
 
117
137
  # Returns the path component of a URL for the given record. It uses
@@ -146,24 +166,24 @@ module ActionDispatch
146
166
  end
147
167
 
148
168
  def build_named_route_call(records, inflection, options = {})
149
- unless records.is_a?(Array)
150
- record = extract_record(records)
151
- route = []
152
- else
169
+ if records.is_a?(Array)
153
170
  record = records.pop
154
171
  route = records.map do |parent|
155
172
  if parent.is_a?(Symbol) || parent.is_a?(String)
156
173
  parent
157
174
  else
158
- ActiveModel::Naming.plural(parent).singularize
175
+ ActiveModel::Naming.route_key(parent).singularize
159
176
  end
160
177
  end
178
+ else
179
+ record = extract_record(records)
180
+ route = []
161
181
  end
162
182
 
163
183
  if record.is_a?(Symbol) || record.is_a?(String)
164
184
  route << record
165
185
  else
166
- route << ActiveModel::Naming.plural(record)
186
+ route << ActiveModel::Naming.route_key(record)
167
187
  route = [route.join("_").singularize] if inflection == :singular
168
188
  route << "index" if ActiveModel::Naming.uncountable?(record) && inflection == :plural
169
189
  end
@@ -0,0 +1,110 @@
1
+ require 'action_dispatch/http/request'
2
+
3
+ module ActionDispatch
4
+ module Routing
5
+ module Redirection
6
+
7
+ # Redirect any path to another path:
8
+ #
9
+ # match "/stories" => redirect("/posts")
10
+ #
11
+ # You can also use interpolation in the supplied redirect argument:
12
+ #
13
+ # match 'docs/:article', :to => redirect('/wiki/%{article}')
14
+ #
15
+ # Alternatively you can use one of the other syntaxes:
16
+ #
17
+ # The block version of redirect allows for the easy encapsulation of any logic associated with
18
+ # the redirect in question. Either the params and request are supplied as arguments, or just
19
+ # params, depending of how many arguments your block accepts. A string is required as a
20
+ # return value.
21
+ #
22
+ # match 'jokes/:number', :to => redirect do |params, request|
23
+ # path = (params[:number].to_i.even? ? "/wheres-the-beef" : "/i-love-lamp")
24
+ # "http://#{request.host_with_port}/#{path}"
25
+ # end
26
+ #
27
+ # The options version of redirect allows you to supply only the parts of the url which need
28
+ # to change, it also supports interpolation of the path similar to the first example.
29
+ #
30
+ # match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
31
+ # match 'stores/:name(*all)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{all}')
32
+ #
33
+ # Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
34
+ # common redirect routes. The call method must accept two arguments, params and request, and return
35
+ # a string.
36
+ #
37
+ # match 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
38
+ #
39
+ def redirect(*args, &block)
40
+ options = args.last.is_a?(Hash) ? args.pop : {}
41
+ status = options.delete(:status) || 301
42
+
43
+ path = args.shift
44
+
45
+ path_proc = if path.is_a?(String)
46
+ proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
47
+ elsif options.any?
48
+ options_proc(options)
49
+ elsif path.respond_to?(:call)
50
+ proc { |params, request| path.call(params, request) }
51
+ elsif block
52
+ block
53
+ else
54
+ raise ArgumentError, "redirection argument not supported"
55
+ end
56
+
57
+ redirection_proc(status, path_proc)
58
+ end
59
+
60
+ private
61
+
62
+ def options_proc(options)
63
+ proc do |params, request|
64
+ path = if options[:path].nil?
65
+ request.path
66
+ elsif params.empty? || !options[:path].match(/%\{\w*\}/)
67
+ options.delete(:path)
68
+ else
69
+ (options.delete(:path) % params)
70
+ end
71
+
72
+ default_options = {
73
+ :protocol => request.protocol,
74
+ :host => request.host,
75
+ :port => request.optional_port,
76
+ :path => path,
77
+ :params => request.query_parameters
78
+ }
79
+
80
+ ActionDispatch::Http::URL.url_for(options.reverse_merge(default_options))
81
+ end
82
+ end
83
+
84
+ def redirection_proc(status, path_proc)
85
+ lambda do |env|
86
+ req = Request.new(env)
87
+
88
+ params = [req.symbolized_path_parameters]
89
+ params << req if path_proc.arity > 1
90
+
91
+ uri = URI.parse(path_proc.call(*params))
92
+ uri.scheme ||= req.scheme
93
+ uri.host ||= req.host
94
+ uri.port ||= req.port unless req.standard_port?
95
+
96
+ body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
97
+
98
+ headers = {
99
+ 'Location' => uri.to_s,
100
+ 'Content-Type' => 'text/html',
101
+ 'Content-Length' => body.length.to_s
102
+ }
103
+
104
+ [ status, headers, [body] ]
105
+ end
106
+ end
107
+
108
+ end
109
+ end
110
+ end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/module/deprecation'
2
+
1
3
  module ActionDispatch
2
4
  module Routing
3
5
  class Route #:nodoc:
@@ -10,6 +12,8 @@ module ActionDispatch
10
12
  @defaults = defaults
11
13
  @name = name
12
14
 
15
+ # FIXME: we should not be doing this much work in a constructor.
16
+
13
17
  @requirements = requirements.merge(defaults)
14
18
  @requirements.delete(:controller) if @requirements[:controller].is_a?(Regexp)
15
19
  @requirements.delete_if { |k, v|
@@ -21,25 +25,22 @@ module ActionDispatch
21
25
  conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS, anchor)
22
26
  end
23
27
 
24
- @conditions = conditions.inject({}) { |h, (k, v)|
25
- h[k] = Rack::Mount::RegexpWithNamedGroups.new(v)
26
- h
27
- }
28
+ @verbs = conditions[:request_method] || []
28
29
 
30
+ @conditions = conditions.dup
31
+
32
+ # Rack-Mount requires that :request_method be a regular expression.
33
+ # :request_method represents the HTTP verb that matches this route.
34
+ #
35
+ # Here we munge values before they get sent on to rack-mount.
36
+ @conditions[:request_method] = %r[^#{verb}$] unless @verbs.empty?
37
+ @conditions[:path_info] = Rack::Mount::RegexpWithNamedGroups.new(@conditions[:path_info]) if @conditions[:path_info]
29
38
  @conditions.delete_if{ |k,v| k != :path_info && !valid_condition?(k) }
30
39
  @requirements.delete_if{ |k,v| !valid_condition?(k) }
31
40
  end
32
41
 
33
42
  def verb
34
- if method = conditions[:request_method]
35
- case method
36
- when Regexp
37
- source = method.source.upcase
38
- source =~ /\A\^[-A-Z|]+\$\Z/ ? source[1..-2] : source
39
- else
40
- method.to_s.upcase
41
- end
42
- end
43
+ @verbs.join '|'
43
44
  end
44
45
 
45
46
  def segment_keys
@@ -49,6 +50,7 @@ module ActionDispatch
49
50
  def to_a
50
51
  [@app, @conditions, @defaults, @name]
51
52
  end
53
+ deprecate :to_a
52
54
 
53
55
  def to_s
54
56
  @to_s ||= begin
@@ -1,17 +1,13 @@
1
1
  require 'rack/mount'
2
2
  require 'forwardable'
3
+ require 'active_support/core_ext/object/blank'
3
4
  require 'active_support/core_ext/object/to_query'
4
- require 'action_dispatch/routing/deprecated_mapper'
5
+ require 'active_support/core_ext/hash/slice'
6
+ require 'active_support/core_ext/module/remove_method'
5
7
 
6
8
  module ActionDispatch
7
9
  module Routing
8
10
  class RouteSet #:nodoc:
9
- # Since the router holds references to many parts of the system
10
- # like engines, controllers and the application itself, inspecting
11
- # the route set can actually be really slow, therefore we default
12
- # alias inspect to to_s.
13
- alias inspect to_s
14
-
15
11
  PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
16
12
 
17
13
  class Dispatcher #:nodoc:
@@ -56,12 +52,13 @@ module ActionDispatch
56
52
  private
57
53
 
58
54
  def controller_reference(controller_param)
55
+ controller_name = "#{controller_param.camelize}Controller"
56
+
59
57
  unless controller = @controllers[controller_param]
60
- controller_name = "#{controller_param.camelize}Controller"
61
58
  controller = @controllers[controller_param] =
62
- ActiveSupport::Dependencies.ref(controller_name)
59
+ ActiveSupport::Dependencies.reference(controller_name)
63
60
  end
64
- controller.get
61
+ controller.get(controller_name)
65
62
  end
66
63
 
67
64
  def dispatch(controller, action, env)
@@ -73,7 +70,7 @@ module ActionDispatch
73
70
  end
74
71
 
75
72
  def split_glob_param!(params)
76
- params[@glob_param] = params[@glob_param].split('/').map { |v| URI.unescape(v) }
73
+ params[@glob_param] = params[@glob_param].split('/').map { |v| URI.parser.unescape(v) }
77
74
  end
78
75
  end
79
76
 
@@ -164,10 +161,18 @@ module ActionDispatch
164
161
 
165
162
  # We use module_eval to avoid leaks
166
163
  @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
167
- def #{selector}(options = nil) # def hash_for_users_url(options = nil)
168
- options ? #{options.inspect}.merge(options) : #{options.inspect} # options ? {:only_path=>false}.merge(options) : {:only_path=>false}
169
- end # end
170
- protected :#{selector} # protected :hash_for_users_url
164
+ remove_possible_method :#{selector}
165
+ def #{selector}(*args)
166
+ options = args.extract_options!
167
+
168
+ if args.any?
169
+ options[:_positional_args] = args
170
+ options[:_positional_keys] = #{route.segment_keys.inspect}
171
+ end
172
+
173
+ options ? #{options.inspect}.merge(options) : #{options.inspect}
174
+ end
175
+ protected :#{selector}
171
176
  END_EVAL
172
177
  helpers << selector
173
178
  end
@@ -190,22 +195,16 @@ module ActionDispatch
190
195
  hash_access_method = hash_access_name(name, kind)
191
196
 
192
197
  @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
198
+ remove_possible_method :#{selector}
193
199
  def #{selector}(*args)
194
- options = #{hash_access_method}(args.extract_options!)
195
-
196
- if args.any?
197
- options[:_positional_args] = args
198
- options[:_positional_keys] = #{route.segment_keys.inspect}
199
- end
200
-
201
- url_for(options)
200
+ url_for(#{hash_access_method}(*args))
202
201
  end
203
202
  END_EVAL
204
203
  helpers << selector
205
204
  end
206
205
  end
207
206
 
208
- attr_accessor :set, :routes, :named_routes
207
+ attr_accessor :set, :routes, :named_routes, :default_scope
209
208
  attr_accessor :disable_clear_and_finalize, :resources_path_names
210
209
  attr_accessor :default_url_options, :request_class, :valid_conditions
211
210
 
@@ -217,7 +216,6 @@ module ActionDispatch
217
216
  self.routes = []
218
217
  self.named_routes = NamedRouteCollection.new
219
218
  self.resources_path_names = self.class.default_resources_path_names.dup
220
- self.controller_namespaces = Set.new
221
219
  self.default_url_options = {}
222
220
 
223
221
  self.request_class = request_class
@@ -225,27 +223,40 @@ module ActionDispatch
225
223
  self.valid_conditions.delete(:id)
226
224
  self.valid_conditions.push(:controller, :action)
227
225
 
226
+ @append = []
228
227
  @disable_clear_and_finalize = false
229
228
  clear!
230
229
  end
231
230
 
232
231
  def draw(&block)
233
232
  clear! unless @disable_clear_and_finalize
233
+ eval_block(block)
234
+ finalize! unless @disable_clear_and_finalize
234
235
 
235
- mapper = Mapper.new(self)
236
+ nil
237
+ end
238
+
239
+ def append(&block)
240
+ @append << block
241
+ end
242
+
243
+ def eval_block(block)
236
244
  if block.arity == 1
237
- mapper.instance_exec(DeprecatedMapper.new(self), &block)
245
+ raise "You are using the old router DSL which has been removed in Rails 3.1. " <<
246
+ "Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/ " <<
247
+ "or add the rails_legacy_mapper gem to your Gemfile"
248
+ end
249
+ mapper = Mapper.new(self)
250
+ if default_scope
251
+ mapper.with_default_scope(default_scope, &block)
238
252
  else
239
253
  mapper.instance_exec(&block)
240
254
  end
241
-
242
- finalize! unless @disable_clear_and_finalize
243
-
244
- nil
245
255
  end
246
256
 
247
257
  def finalize!
248
258
  return if @finalized
259
+ @append.each { |blk| eval_block(blk) }
249
260
  @finalized = true
250
261
  @set.freeze
251
262
  end
@@ -267,6 +278,30 @@ module ActionDispatch
267
278
  named_routes.install(destinations, regenerate_code)
268
279
  end
269
280
 
281
+ module MountedHelpers
282
+ end
283
+
284
+ def mounted_helpers
285
+ MountedHelpers
286
+ end
287
+
288
+ def define_mounted_helper(name)
289
+ return if MountedHelpers.method_defined?(name)
290
+
291
+ routes = self
292
+ MountedHelpers.class_eval do
293
+ define_method "_#{name}" do
294
+ RoutesProxy.new(routes, self._routes_context)
295
+ end
296
+ end
297
+
298
+ MountedHelpers.class_eval <<-RUBY
299
+ def #{name}
300
+ @#{name} ||= _#{name}
301
+ end
302
+ RUBY
303
+ end
304
+
270
305
  def url_helpers
271
306
  @url_helpers ||= begin
272
307
  routes = self
@@ -275,9 +310,9 @@ module ActionDispatch
275
310
  extend ActiveSupport::Concern
276
311
  include UrlFor
277
312
 
278
- @routes = routes
313
+ @_routes = routes
279
314
  class << self
280
- delegate :url_for, :to => '@routes'
315
+ delegate :url_for, :to => '@_routes'
281
316
  end
282
317
  extend routes.named_routes.module
283
318
 
@@ -286,10 +321,10 @@ module ActionDispatch
286
321
  # Yes plz - JP
287
322
  included do
288
323
  routes.install_helpers(self)
289
- singleton_class.send(:define_method, :_routes) { routes }
324
+ singleton_class.send(:redefine_method, :_routes) { routes }
290
325
  end
291
326
 
292
- define_method(:_routes) { routes }
327
+ define_method(:_routes) { @_routes || routes }
293
328
  end
294
329
 
295
330
  helpers
@@ -303,17 +338,29 @@ module ActionDispatch
303
338
  def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
304
339
  raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
305
340
  route = Route.new(self, app, conditions, requirements, defaults, name, anchor)
306
- @set.add_route(*route)
341
+ @set.add_route(route.app, route.conditions, route.defaults, route.name)
307
342
  named_routes[name] = route if name
308
343
  routes << route
309
344
  route
310
345
  end
311
346
 
312
347
  class Generator #:nodoc:
313
- attr_reader :options, :recall, :set, :script_name, :named_route
348
+ PARAMETERIZE = {
349
+ :parameterize => lambda do |name, value|
350
+ if name == :controller
351
+ value
352
+ elsif value.is_a?(Array)
353
+ value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
354
+ else
355
+ return nil unless param = value.to_param
356
+ param.split('/').map { |v| Rack::Mount::Utils.escape_uri(v) }.join("/")
357
+ end
358
+ end
359
+ }
360
+
361
+ attr_reader :options, :recall, :set, :named_route
314
362
 
315
363
  def initialize(options, recall, set, extras = false)
316
- @script_name = options.delete(:script_name)
317
364
  @named_route = options.delete(:use_route)
318
365
  @options = options.dup
319
366
  @recall = recall.dup
@@ -399,34 +446,17 @@ module ActionDispatch
399
446
  end
400
447
 
401
448
  def generate
402
- path, params = @set.set.generate(:path_info, named_route, options, recall, opts)
449
+ path, params = @set.set.generate(:path_info, named_route, options, recall, PARAMETERIZE)
403
450
 
404
451
  raise_routing_error unless path
405
452
 
406
- params.reject! {|k,v| !v.to_param}
407
-
408
453
  return [path, params.keys] if @extras
409
454
 
410
- path << "?#{params.to_query}" if params.any?
411
- "#{script_name}#{path}"
455
+ [path, params]
412
456
  rescue Rack::Mount::RoutingError
413
457
  raise_routing_error
414
458
  end
415
459
 
416
- def opts
417
- parameterize = lambda do |name, value|
418
- if name == :controller
419
- value
420
- elsif value.is_a?(Array)
421
- value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
422
- else
423
- return nil unless param = value.to_param
424
- param.split('/').map { |v| Rack::Mount::Utils.escape_uri(v) }.join("/")
425
- end
426
- end
427
- {:parameterize => parameterize}
428
- end
429
-
430
460
  def raise_routing_error
431
461
  raise ActionController::RoutingError, "No route matches #{options.inspect}"
432
462
  end
@@ -460,7 +490,12 @@ module ActionDispatch
460
490
  Generator.new(options, recall, self, extras).generate
461
491
  end
462
492
 
463
- RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash]
493
+ RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
494
+ :trailing_slash, :anchor, :params, :only_path, :script_name]
495
+
496
+ def _generate_prefix(options = {})
497
+ nil
498
+ end
464
499
 
465
500
  def url_for(options)
466
501
  finalize!
@@ -468,30 +503,24 @@ module ActionDispatch
468
503
 
469
504
  handle_positional_args(options)
470
505
 
471
- rewritten_url = ""
472
-
473
- path_segments = options.delete(:_path_segments)
506
+ user, password = extract_authentication(options)
507
+ path_segments = options.delete(:_path_segments)
508
+ script_name = options.delete(:script_name)
474
509
 
475
- unless options[:only_path]
476
- rewritten_url << (options[:protocol] || "http")
477
- rewritten_url << "://" unless rewritten_url.match("://")
478
- rewritten_url << rewrite_authentication(options)
479
-
480
- raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
481
-
482
- rewritten_url << options[:host]
483
- rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
484
- end
510
+ path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
485
511
 
486
512
  path_options = options.except(*RESERVED_OPTIONS)
487
513
  path_options = yield(path_options) if block_given?
488
- path = generate(path_options, path_segments || {})
489
514
 
490
- # ROUTES TODO: This can be called directly, so script_name should probably be set in the routes
491
- rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
492
- rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
515
+ path_addition, params = generate(path_options, path_segments || {})
516
+ path << path_addition
493
517
 
494
- rewritten_url
518
+ ActionDispatch::Http::URL.url_for(options.merge({
519
+ :path => path,
520
+ :params => params,
521
+ :user => user,
522
+ :password => password
523
+ }))
495
524
  end
496
525
 
497
526
  def call(env)
@@ -514,7 +543,7 @@ module ActionDispatch
514
543
  params.each do |key, value|
515
544
  if value.is_a?(String)
516
545
  value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware?
517
- params[key] = URI.unescape(value)
546
+ params[key] = URI.parser.unescape(value)
518
547
  end
519
548
  end
520
549
 
@@ -533,28 +562,25 @@ module ActionDispatch
533
562
  end
534
563
 
535
564
  private
565
+
566
+ def extract_authentication(options)
567
+ if options[:user] && options[:password]
568
+ [options.delete(:user), options.delete(:password)]
569
+ else
570
+ nil
571
+ end
572
+ end
573
+
536
574
  def handle_positional_args(options)
537
575
  return unless args = options.delete(:_positional_args)
538
576
 
539
577
  keys = options.delete(:_positional_keys)
540
578
  keys -= options.keys if args.size < keys.size - 1 # take format into account
541
579
 
542
- args = args.zip(keys).inject({}) do |h, (v, k)|
543
- h[k] = v
544
- h
545
- end
546
-
547
580
  # Tell url_for to skip default_url_options
548
- options.merge!(args)
581
+ options.merge!(Hash[args.zip(keys).map { |v, k| [k, v] }])
549
582
  end
550
583
 
551
- def rewrite_authentication(options)
552
- if options[:user] && options[:password]
553
- "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
554
- else
555
- ""
556
- end
557
- end
558
584
  end
559
585
  end
560
586
  end