actionpack 3.2.22.5 → 4.0.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 (265) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +641 -418
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -288
  5. data/lib/abstract_controller.rb +1 -8
  6. data/lib/abstract_controller/asset_paths.rb +2 -2
  7. data/lib/abstract_controller/base.rb +39 -37
  8. data/lib/abstract_controller/callbacks.rb +101 -82
  9. data/lib/abstract_controller/collector.rb +7 -3
  10. data/lib/abstract_controller/helpers.rb +23 -11
  11. data/lib/abstract_controller/layouts.rb +68 -73
  12. data/lib/abstract_controller/logger.rb +1 -2
  13. data/lib/abstract_controller/rendering.rb +22 -13
  14. data/lib/abstract_controller/translation.rb +16 -1
  15. data/lib/abstract_controller/url_for.rb +6 -6
  16. data/lib/abstract_controller/view_paths.rb +1 -1
  17. data/lib/action_controller.rb +15 -6
  18. data/lib/action_controller/base.rb +46 -22
  19. data/lib/action_controller/caching.rb +46 -33
  20. data/lib/action_controller/caching/fragments.rb +23 -53
  21. data/lib/action_controller/deprecated.rb +5 -1
  22. data/lib/action_controller/deprecated/integration_test.rb +3 -0
  23. data/lib/action_controller/log_subscriber.rb +11 -8
  24. data/lib/action_controller/metal.rb +16 -30
  25. data/lib/action_controller/metal/conditional_get.rb +76 -32
  26. data/lib/action_controller/metal/data_streaming.rb +20 -26
  27. data/lib/action_controller/metal/exceptions.rb +19 -6
  28. data/lib/action_controller/metal/flash.rb +24 -9
  29. data/lib/action_controller/metal/force_ssl.rb +32 -9
  30. data/lib/action_controller/metal/head.rb +25 -4
  31. data/lib/action_controller/metal/helpers.rb +6 -9
  32. data/lib/action_controller/metal/hide_actions.rb +1 -2
  33. data/lib/action_controller/metal/http_authentication.rb +105 -87
  34. data/lib/action_controller/metal/implicit_render.rb +1 -1
  35. data/lib/action_controller/metal/instrumentation.rb +2 -1
  36. data/lib/action_controller/metal/live.rb +141 -0
  37. data/lib/action_controller/metal/mime_responds.rb +161 -47
  38. data/lib/action_controller/metal/params_wrapper.rb +112 -74
  39. data/lib/action_controller/metal/rack_delegation.rb +9 -3
  40. data/lib/action_controller/metal/redirecting.rb +15 -20
  41. data/lib/action_controller/metal/renderers.rb +11 -9
  42. data/lib/action_controller/metal/rendering.rb +8 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
  44. data/lib/action_controller/metal/responder.rb +20 -19
  45. data/lib/action_controller/metal/streaming.rb +12 -18
  46. data/lib/action_controller/metal/strong_parameters.rb +516 -0
  47. data/lib/action_controller/metal/testing.rb +13 -18
  48. data/lib/action_controller/metal/url_for.rb +27 -25
  49. data/lib/action_controller/model_naming.rb +12 -0
  50. data/lib/action_controller/railtie.rb +33 -17
  51. data/lib/action_controller/railties/helpers.rb +22 -0
  52. data/lib/action_controller/record_identifier.rb +18 -72
  53. data/lib/action_controller/test_case.rb +215 -123
  54. data/lib/action_controller/vendor/html-scanner.rb +4 -19
  55. data/lib/action_dispatch.rb +27 -19
  56. data/lib/action_dispatch/http/cache.rb +63 -11
  57. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  58. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  59. data/lib/action_dispatch/http/headers.rb +27 -19
  60. data/lib/action_dispatch/http/mime_negotiation.rb +25 -2
  61. data/lib/action_dispatch/http/mime_type.rb +145 -113
  62. data/lib/action_dispatch/http/mime_types.rb +1 -1
  63. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  64. data/lib/action_dispatch/http/parameters.rb +12 -5
  65. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  66. data/lib/action_dispatch/http/request.rb +49 -18
  67. data/lib/action_dispatch/http/response.rb +129 -35
  68. data/lib/action_dispatch/http/upload.rb +60 -17
  69. data/lib/action_dispatch/http/url.rb +53 -31
  70. data/lib/action_dispatch/journey.rb +5 -0
  71. data/lib/action_dispatch/journey/backwards.rb +5 -0
  72. data/lib/action_dispatch/journey/formatter.rb +146 -0
  73. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  74. data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
  75. data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
  76. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  77. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  78. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  79. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  80. data/lib/action_dispatch/journey/nodes/node.rb +124 -0
  81. data/lib/action_dispatch/journey/parser.rb +206 -0
  82. data/lib/action_dispatch/journey/parser.y +47 -0
  83. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  84. data/lib/action_dispatch/journey/path/pattern.rb +196 -0
  85. data/lib/action_dispatch/journey/route.rb +116 -0
  86. data/lib/action_dispatch/journey/router.rb +164 -0
  87. data/lib/action_dispatch/journey/router/strexp.rb +24 -0
  88. data/lib/action_dispatch/journey/router/utils.rb +54 -0
  89. data/lib/action_dispatch/journey/routes.rb +75 -0
  90. data/lib/action_dispatch/journey/scanner.rb +61 -0
  91. data/lib/action_dispatch/journey/visitors.rb +189 -0
  92. data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
  93. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  94. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  95. data/lib/action_dispatch/middleware/callbacks.rb +9 -4
  96. data/lib/action_dispatch/middleware/cookies.rb +168 -57
  97. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
  98. data/lib/action_dispatch/middleware/exception_wrapper.rb +27 -3
  99. data/lib/action_dispatch/middleware/flash.rb +58 -58
  100. data/lib/action_dispatch/middleware/params_parser.rb +14 -29
  101. data/lib/action_dispatch/middleware/public_exceptions.rb +31 -14
  102. data/lib/action_dispatch/middleware/reloader.rb +6 -6
  103. data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
  104. data/lib/action_dispatch/middleware/request_id.rb +2 -6
  105. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  106. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  107. data/lib/action_dispatch/middleware/session/cookie_store.rb +81 -7
  108. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  109. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
  110. data/lib/action_dispatch/middleware/ssl.rb +70 -0
  111. data/lib/action_dispatch/middleware/stack.rb +6 -1
  112. data/lib/action_dispatch/middleware/static.rb +5 -24
  113. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
  114. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
  115. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  116. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
  117. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
  119. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
  122. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  123. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
  124. data/lib/action_dispatch/railtie.rb +16 -6
  125. data/lib/action_dispatch/request/session.rb +181 -0
  126. data/lib/action_dispatch/routing.rb +41 -40
  127. data/lib/action_dispatch/routing/inspector.rb +240 -0
  128. data/lib/action_dispatch/routing/mapper.rb +501 -273
  129. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
  130. data/lib/action_dispatch/routing/redirection.rb +46 -29
  131. data/lib/action_dispatch/routing/route_set.rb +203 -164
  132. data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
  133. data/lib/action_dispatch/routing/url_for.rb +48 -33
  134. data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
  135. data/lib/action_dispatch/testing/assertions/response.rb +32 -40
  136. data/lib/action_dispatch/testing/assertions/routing.rb +40 -39
  137. data/lib/action_dispatch/testing/assertions/selector.rb +15 -20
  138. data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
  139. data/lib/action_dispatch/testing/integration.rb +41 -22
  140. data/lib/action_dispatch/testing/test_process.rb +9 -6
  141. data/lib/action_dispatch/testing/test_request.rb +7 -3
  142. data/lib/action_pack.rb +1 -1
  143. data/lib/action_pack/version.rb +4 -4
  144. data/lib/action_view.rb +17 -8
  145. data/lib/action_view/base.rb +15 -34
  146. data/lib/action_view/buffers.rb +1 -1
  147. data/lib/action_view/context.rb +4 -4
  148. data/lib/action_view/dependency_tracker.rb +91 -0
  149. data/lib/action_view/digestor.rb +85 -0
  150. data/lib/action_view/flows.rb +1 -4
  151. data/lib/action_view/helpers.rb +2 -4
  152. data/lib/action_view/helpers/active_model_helper.rb +3 -4
  153. data/lib/action_view/helpers/asset_tag_helper.rb +211 -353
  154. data/lib/action_view/helpers/asset_url_helper.rb +354 -0
  155. data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
  156. data/lib/action_view/helpers/cache_helper.rb +150 -18
  157. data/lib/action_view/helpers/capture_helper.rb +42 -29
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -2
  159. data/lib/action_view/helpers/date_helper.rb +268 -247
  160. data/lib/action_view/helpers/debug_helper.rb +10 -11
  161. data/lib/action_view/helpers/form_helper.rb +904 -547
  162. data/lib/action_view/helpers/form_options_helper.rb +341 -166
  163. data/lib/action_view/helpers/form_tag_helper.rb +188 -88
  164. data/lib/action_view/helpers/javascript_helper.rb +23 -16
  165. data/lib/action_view/helpers/number_helper.rb +148 -354
  166. data/lib/action_view/helpers/output_safety_helper.rb +3 -3
  167. data/lib/action_view/helpers/record_tag_helper.rb +17 -22
  168. data/lib/action_view/helpers/rendering_helper.rb +2 -4
  169. data/lib/action_view/helpers/sanitize_helper.rb +3 -6
  170. data/lib/action_view/helpers/tag_helper.rb +43 -37
  171. data/lib/action_view/helpers/tags.rb +39 -0
  172. data/lib/action_view/helpers/tags/base.rb +148 -0
  173. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  174. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  175. data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
  176. data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
  177. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  178. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  179. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  180. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  181. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  182. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  183. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  184. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  185. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  186. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  187. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  188. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  189. data/lib/action_view/helpers/tags/label.rb +65 -0
  190. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  191. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  192. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  193. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  194. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  195. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  196. data/lib/action_view/helpers/tags/select.rb +41 -0
  197. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  198. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  199. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  200. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  201. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  202. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  203. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  204. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  205. data/lib/action_view/helpers/text_helper.rb +126 -113
  206. data/lib/action_view/helpers/translation_helper.rb +32 -16
  207. data/lib/action_view/helpers/url_helper.rb +200 -271
  208. data/lib/action_view/locale/en.yml +1 -105
  209. data/lib/action_view/log_subscriber.rb +6 -4
  210. data/lib/action_view/lookup_context.rb +15 -39
  211. data/lib/action_view/model_naming.rb +12 -0
  212. data/lib/action_view/path_set.rb +9 -39
  213. data/lib/action_view/railtie.rb +6 -22
  214. data/lib/action_view/record_identifier.rb +84 -0
  215. data/lib/action_view/renderer/abstract_renderer.rb +10 -19
  216. data/lib/action_view/renderer/partial_renderer.rb +144 -81
  217. data/lib/action_view/renderer/renderer.rb +2 -19
  218. data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
  219. data/lib/action_view/renderer/template_renderer.rb +14 -13
  220. data/lib/action_view/routing_url_for.rb +107 -0
  221. data/lib/action_view/template.rb +22 -21
  222. data/lib/action_view/template/error.rb +22 -12
  223. data/lib/action_view/template/handlers.rb +12 -9
  224. data/lib/action_view/template/handlers/builder.rb +1 -1
  225. data/lib/action_view/template/handlers/erb.rb +11 -16
  226. data/lib/action_view/template/handlers/raw.rb +11 -0
  227. data/lib/action_view/template/resolver.rb +111 -83
  228. data/lib/action_view/template/text.rb +12 -8
  229. data/lib/action_view/template/types.rb +57 -0
  230. data/lib/action_view/test_case.rb +66 -43
  231. data/lib/action_view/testing/resolvers.rb +3 -2
  232. data/lib/action_view/vendor/html-scanner.rb +20 -0
  233. data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
  234. data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
  235. data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +18 -7
  236. data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +1 -1
  237. data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
  238. data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
  239. metadata +135 -125
  240. data/lib/action_controller/caching/actions.rb +0 -185
  241. data/lib/action_controller/caching/pages.rb +0 -187
  242. data/lib/action_controller/caching/sweeping.rb +0 -97
  243. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  244. data/lib/action_controller/metal/compatibility.rb +0 -65
  245. data/lib/action_controller/metal/session_management.rb +0 -14
  246. data/lib/action_controller/railties/paths.rb +0 -25
  247. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  248. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  249. data/lib/action_dispatch/middleware/head.rb +0 -18
  250. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  251. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  252. data/lib/action_view/asset_paths.rb +0 -142
  253. data/lib/action_view/helpers/asset_paths.rb +0 -7
  254. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  255. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  256. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  257. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  258. data/lib/sprockets/assets.rake +0 -99
  259. data/lib/sprockets/bootstrap.rb +0 -37
  260. data/lib/sprockets/compressors.rb +0 -83
  261. data/lib/sprockets/helpers.rb +0 -6
  262. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  263. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  264. data/lib/sprockets/railtie.rb +0 -62
  265. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,3 +1,5 @@
1
+ require 'action_controller/model_naming'
2
+
1
3
  module ActionDispatch
2
4
  module Routing
3
5
  # Polymorphic URL helpers are methods for smart resolution to a named route call when
@@ -32,7 +34,7 @@ module ActionDispatch
32
34
  # == Prefixed polymorphic helpers
33
35
  #
34
36
  # In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
35
- # number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt>
37
+ # number of prefixed helpers are available as a shorthand to <tt>action: "..."</tt>
36
38
  # in options. Those are:
37
39
  #
38
40
  # * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
@@ -41,20 +43,20 @@ module ActionDispatch
41
43
  # Example usage:
42
44
  #
43
45
  # edit_polymorphic_path(@post) # => "/posts/1/edit"
44
- # polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf"
45
- #
46
- # == Using with mounted engines
46
+ # polymorphic_path(@post, format: :pdf) # => "/posts/1.pdf"
47
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:
48
+ # == Usage with mounted engines
51
49
  #
52
- # For example:
50
+ # If you are using a mounted engine and you need to use a polymorphic_url
51
+ # pointing at the engine's routes, pass in the engine's route proxy as the first
52
+ # argument to the method. For example:
53
53
  #
54
- # polymorphic_url([blog, @post]) # it will call blog.post_path(@post)
55
- # form_for([blog, @post]) # => "/blog/posts/1
54
+ # polymorphic_url([blog, @post]) # calls blog.post_path(@post)
55
+ # form_for([blog, @post]) # => "/blog/posts/1"
56
56
  #
57
57
  module PolymorphicRoutes
58
+ include ActionController::ModelNaming
59
+
58
60
  # Constructs a call to a named RESTful route for the given record and returns the
59
61
  # resulting URL string. For example:
60
62
  #
@@ -72,8 +74,6 @@ module ActionDispatch
72
74
  # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
73
75
  # Default is <tt>:url</tt>.
74
76
  #
75
- # ==== Examples
76
- #
77
77
  # # an Article record
78
78
  # polymorphic_url(record) # same as article_url(record)
79
79
  #
@@ -130,7 +130,7 @@ module ActionDispatch
130
130
  end
131
131
 
132
132
  # Returns the path component of a URL for the given record. It uses
133
- # <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
133
+ # <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>.
134
134
  def polymorphic_path(record_or_hash_or_array, options = {})
135
135
  polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
136
136
  end
@@ -156,10 +156,6 @@ module ActionDispatch
156
156
  options[:action] ? "#{options[:action]}_" : ''
157
157
  end
158
158
 
159
- def convert_to_model(object)
160
- object.respond_to?(:to_model) ? object.to_model : object
161
- end
162
-
163
159
  def routing_type(options)
164
160
  options[:routing_type] || :url
165
161
  end
@@ -171,7 +167,7 @@ module ActionDispatch
171
167
  if parent.is_a?(Symbol) || parent.is_a?(String)
172
168
  parent
173
169
  else
174
- ActiveModel::Naming.singular_route_key(parent)
170
+ model_name_from_record_or_class(parent).singular_route_key
175
171
  end
176
172
  end
177
173
  else
@@ -183,9 +179,9 @@ module ActionDispatch
183
179
  route << record
184
180
  elsif record
185
181
  if inflection == :singular
186
- route << ActiveModel::Naming.singular_route_key(record)
182
+ route << model_name_from_record_or_class(record).singular_route_key
187
183
  else
188
- route << ActiveModel::Naming.route_key(record)
184
+ route << model_name_from_record_or_class(record).route_key
189
185
  end
190
186
  else
191
187
  raise ArgumentError, "Nil location provided. Can't build URI."
@@ -1,6 +1,8 @@
1
1
  require 'action_dispatch/http/request'
2
2
  require 'active_support/core_ext/uri'
3
+ require 'active_support/core_ext/array/extract_options'
3
4
  require 'rack/utils'
5
+ require 'action_controller/metal/exceptions'
4
6
 
5
7
  module ActionDispatch
6
8
  module Routing
@@ -15,6 +17,14 @@ module ActionDispatch
15
17
  def call(env)
16
18
  req = Request.new(env)
17
19
 
20
+ # If any of the path parameters has a invalid encoding then
21
+ # raise since it's likely to trigger errors further on.
22
+ req.symbolized_path_parameters.each do |key, value|
23
+ unless value.valid_encoding?
24
+ raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
25
+ end
26
+ end
27
+
18
28
  uri = URI.parse(path(req.symbolized_path_parameters, req))
19
29
  uri.scheme ||= req.scheme
20
30
  uri.host ||= req.host
@@ -34,6 +44,25 @@ module ActionDispatch
34
44
  def path(params, request)
35
45
  block.call params, request
36
46
  end
47
+
48
+ def inspect
49
+ "redirect(#{status})"
50
+ end
51
+ end
52
+
53
+ class PathRedirect < Redirect
54
+ def path(params, request)
55
+ (params.empty? || !block.match(/%\{\w*\}/)) ? block : (block % escape(params))
56
+ end
57
+
58
+ def inspect
59
+ "redirect(#{status}, #{block})"
60
+ end
61
+
62
+ private
63
+ def escape(params)
64
+ Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
65
+ end
37
66
  end
38
67
 
39
68
  class OptionRedirect < Redirect # :nodoc:
@@ -46,7 +75,7 @@ module ActionDispatch
46
75
  :port => request.optional_port,
47
76
  :path => request.path,
48
77
  :params => request.query_parameters
49
- }.merge options
78
+ }.merge! options
50
79
 
51
80
  if !params.empty? && url_options[:path].match(/%\{\w*\}/)
52
81
  url_options[:path] = (url_options[:path] % escape_path(params))
@@ -55,6 +84,10 @@ module ActionDispatch
55
84
  ActionDispatch::Http::URL.url_for url_options
56
85
  end
57
86
 
87
+ def inspect
88
+ "redirect(#{status}, #{options.map{ |k,v| "#{k}: #{v}" }.join(', ')})"
89
+ end
90
+
58
91
  private
59
92
  def escape_path(params)
60
93
  Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }]
@@ -65,11 +98,11 @@ module ActionDispatch
65
98
 
66
99
  # Redirect any path to another path:
67
100
  #
68
- # match "/stories" => redirect("/posts")
101
+ # get "/stories" => redirect("/posts")
69
102
  #
70
103
  # You can also use interpolation in the supplied redirect argument:
71
104
  #
72
- # match 'docs/:article', :to => redirect('/wiki/%{article}')
105
+ # get 'docs/:article', to: redirect('/wiki/%{article}')
73
106
  #
74
107
  # Alternatively you can use one of the other syntaxes:
75
108
  #
@@ -78,54 +111,38 @@ module ActionDispatch
78
111
  # params, depending of how many arguments your block accepts. A string is required as a
79
112
  # return value.
80
113
  #
81
- # match 'jokes/:number', :to => redirect { |params, request|
114
+ # get 'jokes/:number', to: redirect { |params, request|
82
115
  # path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
83
116
  # "http://#{request.host_with_port}/#{path}"
84
117
  # }
85
118
  #
119
+ # Note that the +do end+ syntax for the redirect block wouldn't work, as Ruby would pass
120
+ # the block to +get+ instead of +redirect+. Use <tt>{ ... }</tt> instead.
121
+ #
86
122
  # The options version of redirect allows you to supply only the parts of the url which need
87
123
  # to change, it also supports interpolation of the path similar to the first example.
88
124
  #
89
- # match 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
90
- # match 'stores/:name(*all)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{all}')
125
+ # get 'stores/:name', to: redirect(subdomain: 'stores', path: '/%{name}')
126
+ # get 'stores/:name(*all)', to: redirect(subdomain: 'stores', path: '/%{name}%{all}')
91
127
  #
92
128
  # Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
93
129
  # common redirect routes. The call method must accept two arguments, params and request, and return
94
130
  # a string.
95
131
  #
96
- # match 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
132
+ # get 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
97
133
  #
98
134
  def redirect(*args, &block)
99
- options = args.last.is_a?(Hash) ? args.pop : {}
135
+ options = args.extract_options!
100
136
  status = options.delete(:status) || 301
137
+ path = args.shift
101
138
 
102
139
  return OptionRedirect.new(status, options) if options.any?
103
-
104
- path = args.shift
105
-
106
- block = lambda { |params, request|
107
- (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % escape(params))
108
- } if String === path
140
+ return PathRedirect.new(status, path) if String === path
109
141
 
110
142
  block = path if path.respond_to? :call
111
-
112
- # :FIXME: remove in Rails 4.0
113
- if block && block.respond_to?(:arity) && block.arity < 2
114
- msg = "redirect blocks with arity of #{block.arity} are deprecated. Your block must take 2 parameters: the environment, and a request object"
115
- ActiveSupport::Deprecation.warn msg
116
- deprecated_block = block
117
- block = lambda { |params, _| deprecated_block.call(params) }
118
- end
119
-
120
143
  raise ArgumentError, "redirection argument not supported" unless block
121
-
122
144
  Redirect.new status, block
123
145
  end
124
-
125
- private
126
- def escape(params)
127
- Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
128
- end
129
146
  end
130
147
  end
131
148
  end
@@ -1,9 +1,10 @@
1
- require 'journey'
1
+ require 'action_dispatch/journey'
2
2
  require 'forwardable'
3
- require 'active_support/core_ext/object/blank'
3
+ require 'thread_safe'
4
4
  require 'active_support/core_ext/object/to_query'
5
5
  require 'active_support/core_ext/hash/slice'
6
6
  require 'active_support/core_ext/module/remove_method'
7
+ require 'active_support/core_ext/array/extract_options'
7
8
  require 'action_controller/metal/exceptions'
8
9
 
9
10
  module ActionDispatch
@@ -21,11 +22,20 @@ module ActionDispatch
21
22
  def initialize(options={})
22
23
  @defaults = options[:defaults]
23
24
  @glob_param = options.delete(:glob)
24
- @controllers = {}
25
+ @controller_class_names = ThreadSafe::Cache.new
25
26
  end
26
27
 
27
28
  def call(env)
28
29
  params = env[PARAMETERS_KEY]
30
+
31
+ # If any of the path parameters has a invalid encoding then
32
+ # raise since it's likely to trigger errors further on.
33
+ params.each do |key, value|
34
+ unless value.valid_encoding?
35
+ raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
36
+ end
37
+ end
38
+
29
39
  prepare_params!(params)
30
40
 
31
41
  # Just raise undefined constant errors if a controller was specified as default.
@@ -60,13 +70,8 @@ module ActionDispatch
60
70
  private
61
71
 
62
72
  def controller_reference(controller_param)
63
- controller_name = "#{controller_param.camelize}Controller"
64
-
65
- unless controller = @controllers[controller_param]
66
- controller = @controllers[controller_param] =
67
- ActiveSupport::Dependencies.reference(controller_name)
68
- end
69
- controller.get(controller_name)
73
+ const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
74
+ ActiveSupport::Dependencies.constantize(const_name)
70
75
  end
71
76
 
72
77
  def dispatch(controller, action, env)
@@ -94,14 +99,13 @@ module ActionDispatch
94
99
  attr_reader :routes, :helpers, :module
95
100
 
96
101
  def initialize
97
- @routes = {}
102
+ @routes = {}
98
103
  @helpers = []
99
-
100
- @module = Module.new
104
+ @module = Module.new
101
105
  end
102
106
 
103
107
  def helper_names
104
- self.module.instance_methods.map(&:to_s)
108
+ @helpers.map(&:to_s)
105
109
  end
106
110
 
107
111
  def clear!
@@ -139,90 +143,131 @@ module ActionDispatch
139
143
  routes.length
140
144
  end
141
145
 
142
- def reset!
143
- old_routes = routes.dup
144
- clear!
145
- old_routes.each do |name, route|
146
- add(name, route)
146
+ class UrlHelper # :nodoc:
147
+ def self.create(route, options)
148
+ if optimize_helper?(route)
149
+ OptimizedUrlHelper.new(route, options)
150
+ else
151
+ new route, options
152
+ end
147
153
  end
148
- end
149
154
 
150
- def install(destinations = [ActionController::Base, ActionView::Base], regenerate = false)
151
- reset! if regenerate
152
- Array(destinations).each do |dest|
153
- dest.__send__(:include, @module)
155
+ def self.optimize_helper?(route)
156
+ route.requirements.except(:controller, :action).empty?
154
157
  end
155
- end
156
158
 
157
- private
158
- def url_helper_name(name, kind = :url)
159
- :"#{name}_#{kind}"
160
- end
159
+ class OptimizedUrlHelper < UrlHelper # :nodoc:
160
+ attr_reader :arg_size
161
161
 
162
- def hash_access_name(name, kind = :url)
163
- :"hash_for_#{name}_#{kind}"
164
- end
162
+ def initialize(route, options)
163
+ super
164
+ @path_parts = @route.required_parts
165
+ @arg_size = @path_parts.size
166
+ @string_route = string_route(route)
167
+ end
165
168
 
166
- def define_named_route_methods(name, route)
167
- {:url => {:only_path => false}, :path => {:only_path => true}}.each do |kind, opts|
168
- hash = route.defaults.merge(:use_route => name).merge(opts)
169
- define_hash_access route, name, kind, hash
170
- define_url_helper route, name, kind, hash
169
+ def call(t, args)
170
+ if args.size == arg_size && !args.last.is_a?(Hash) && optimize_routes_generation?(t)
171
+ @options.merge!(t.url_options) if t.respond_to?(:url_options)
172
+ @options[:path] = optimized_helper(args)
173
+ ActionDispatch::Http::URL.url_for(@options)
174
+ else
175
+ super
176
+ end
171
177
  end
172
- end
173
178
 
174
- def define_hash_access(route, name, kind, options)
175
- selector = hash_access_name(name, kind)
179
+ private
176
180
 
177
- # We use module_eval to avoid leaks
178
- @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
179
- remove_possible_method :#{selector}
180
- def #{selector}(*args)
181
- options = args.extract_options!
182
- result = #{options.inspect}
181
+ def string_route(route)
182
+ string_route = route.ast.to_s.dup
183
+ while string_route.gsub!(/\([^\)]*\)/, "")
184
+ true
185
+ end
186
+ string_route
187
+ end
183
188
 
184
- if args.size > 0
185
- result[:_positional_args] = args
186
- result[:_positional_keys] = #{route.segment_keys.inspect}
187
- end
189
+ def optimized_helper(args)
190
+ path = @string_route.dup
191
+ klass = Journey::Router::Utils
188
192
 
189
- result.merge(options)
193
+ @path_parts.zip(args) do |part, arg|
194
+ # Replace each route parameter
195
+ # e.g. :id for regular parameter or *path for globbing
196
+ # with ruby string interpolation code
197
+ path.gsub!(/(\*|:)#{part}/, klass.escape_fragment(arg.to_param))
190
198
  end
191
- protected :#{selector}
192
- END_EVAL
193
- helpers << selector
199
+ path
200
+ end
201
+
202
+ def optimize_routes_generation?(t)
203
+ t.send(:optimize_routes_generation?)
204
+ end
194
205
  end
195
206
 
196
- # Create a url helper allowing ordered parameters to be associated
197
- # with corresponding dynamic segments, so you can do:
198
- #
199
- # foo_url(bar, baz, bang)
200
- #
201
- # Instead of:
202
- #
203
- # foo_url(:bar => bar, :baz => baz, :bang => bang)
204
- #
205
- # Also allow options hash, so you can do:
206
- #
207
- # foo_url(bar, baz, bang, :sort_by => 'baz')
208
- #
209
- def define_url_helper(route, name, kind, options)
210
- selector = url_helper_name(name, kind)
211
- hash_access_method = hash_access_name(name, kind)
212
-
213
- @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
214
- remove_possible_method :#{selector}
215
- def #{selector}(*args)
216
- url_for(#{hash_access_method}(*args))
207
+ def initialize(route, options)
208
+ @options = options
209
+ @segment_keys = route.segment_keys
210
+ @route = route
211
+ end
212
+
213
+ def call(t, args)
214
+ t.url_for(handle_positional_args(t, args, @options, @segment_keys))
215
+ end
216
+
217
+ def handle_positional_args(t, args, options, keys)
218
+ inner_options = args.extract_options!
219
+ result = options.dup
220
+
221
+ if args.size > 0
222
+ if args.size < keys.size - 1 # take format into account
223
+ keys -= t.url_options.keys if t.respond_to?(:url_options)
224
+ keys -= options.keys
217
225
  end
218
- END_EVAL
219
- helpers << selector
226
+ result.merge!(Hash[keys.zip(args)])
227
+ end
228
+
229
+ result.merge!(inner_options)
220
230
  end
231
+ end
232
+
233
+ private
234
+ # Create a url helper allowing ordered parameters to be associated
235
+ # with corresponding dynamic segments, so you can do:
236
+ #
237
+ # foo_url(bar, baz, bang)
238
+ #
239
+ # Instead of:
240
+ #
241
+ # foo_url(bar: bar, baz: baz, bang: bang)
242
+ #
243
+ # Also allow options hash, so you can do:
244
+ #
245
+ # foo_url(bar, baz, bang, sort_by: 'baz')
246
+ #
247
+ def define_url_helper(route, name, options)
248
+ helper = UrlHelper.create(route, options.dup)
249
+
250
+ @module.remove_possible_method name
251
+ @module.module_eval do
252
+ define_method(name) do |*args|
253
+ helper.call self, args
254
+ end
255
+ end
256
+
257
+ helpers << name
258
+ end
259
+
260
+ def define_named_route_methods(name, route)
261
+ define_url_helper route, :"#{name}_path",
262
+ route.defaults.merge(:use_route => name, :only_path => true)
263
+ define_url_helper route, :"#{name}_url",
264
+ route.defaults.merge(:use_route => name, :only_path => false)
265
+ end
221
266
  end
222
267
 
223
268
  attr_accessor :formatter, :set, :named_routes, :default_scope, :router
224
269
  attr_accessor :disable_clear_and_finalize, :resources_path_names
225
- attr_accessor :default_url_options, :request_class, :valid_conditions
270
+ attr_accessor :default_url_options, :request_class
226
271
 
227
272
  alias :routes :set
228
273
 
@@ -234,17 +279,7 @@ module ActionDispatch
234
279
  self.named_routes = NamedRouteCollection.new
235
280
  self.resources_path_names = self.class.default_resources_path_names.dup
236
281
  self.default_url_options = {}
237
-
238
282
  self.request_class = request_class
239
- @valid_conditions = {}
240
-
241
- request_class.public_instance_methods.each { |m|
242
- @valid_conditions[m.to_sym] = true
243
- }
244
- @valid_conditions[:controller] = true
245
- @valid_conditions[:action] = true
246
-
247
- self.valid_conditions.delete(:id)
248
283
 
249
284
  @append = []
250
285
  @prepend = []
@@ -300,14 +335,15 @@ module ActionDispatch
300
335
  @prepend.each { |blk| eval_block(blk) }
301
336
  end
302
337
 
303
- def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
304
- Array(destinations).each { |d| d.module_eval { include Helpers } }
305
- named_routes.install(destinations, regenerate_code)
306
- end
307
-
308
- module MountedHelpers
338
+ module MountedHelpers #:nodoc:
339
+ extend ActiveSupport::Concern
340
+ include UrlFor
309
341
  end
310
342
 
343
+ # Contains all the mounted helpers accross different
344
+ # engines and the `main_app` helper for the application.
345
+ # You can include this in your classes if you want to
346
+ # access routes for other engines.
311
347
  def mounted_helpers
312
348
  MountedHelpers
313
349
  end
@@ -318,13 +354,13 @@ module ActionDispatch
318
354
  routes = self
319
355
  MountedHelpers.class_eval do
320
356
  define_method "_#{name}" do
321
- RoutesProxy.new(routes, self._routes_context)
357
+ RoutesProxy.new(routes, _routes_context)
322
358
  end
323
359
  end
324
360
 
325
- MountedHelpers.class_eval <<-RUBY
361
+ MountedHelpers.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
326
362
  def #{name}
327
- @#{name} ||= _#{name}
363
+ @_#{name} ||= _#{name}
328
364
  end
329
365
  RUBY
330
366
  end
@@ -333,28 +369,36 @@ module ActionDispatch
333
369
  @url_helpers ||= begin
334
370
  routes = self
335
371
 
336
- helpers = Module.new do
372
+ Module.new do
337
373
  extend ActiveSupport::Concern
338
374
  include UrlFor
339
375
 
376
+ # Define url_for in the singleton level so one can do:
377
+ # Rails.application.routes.url_helpers.url_for(args)
340
378
  @_routes = routes
341
379
  class << self
342
- delegate :url_for, :to => '@_routes'
380
+ delegate :url_for, :optimize_routes_generation?, :to => '@_routes'
343
381
  end
382
+
383
+ # Make named_routes available in the module singleton
384
+ # as well, so one can do:
385
+ # Rails.application.routes.url_helpers.posts_path
344
386
  extend routes.named_routes.module
345
387
 
346
- # ROUTES TODO: install_helpers isn't great... can we make a module with the stuff that
347
- # we can include?
348
- # Yes plz - JP
388
+ # Any class that includes this module will get all
389
+ # named routes...
390
+ include routes.named_routes.module
391
+
392
+ # plus a singleton class method called _routes ...
349
393
  included do
350
- routes.install_helpers(self)
351
394
  singleton_class.send(:redefine_method, :_routes) { routes }
352
395
  end
353
396
 
397
+ # And an instance method _routes. Note that
398
+ # UrlFor (included in this module) add extra
399
+ # conveniences for working with @_routes.
354
400
  define_method(:_routes) { @_routes || routes }
355
401
  end
356
-
357
- helpers
358
402
  end
359
403
  end
360
404
 
@@ -366,10 +410,10 @@ module ActionDispatch
366
410
  raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
367
411
 
368
412
  path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor)
369
- conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym })
413
+ conditions = build_conditions(conditions, path.names.map { |x| x.to_sym })
370
414
 
371
415
  route = @set.add_route(app, path, conditions, defaults, name)
372
- named_routes[name] = route if name
416
+ named_routes[name] = route if name && !named_routes[name]
373
417
  route
374
418
  end
375
419
 
@@ -403,21 +447,22 @@ module ActionDispatch
403
447
  end
404
448
  private :build_path
405
449
 
406
- def build_conditions(current_conditions, req_predicates, path_values)
450
+ def build_conditions(current_conditions, path_values)
407
451
  conditions = current_conditions.dup
408
452
 
409
- verbs = conditions[:request_method] || []
410
-
411
453
  # Rack-Mount requires that :request_method be a regular expression.
412
454
  # :request_method represents the HTTP verb that matches this route.
413
455
  #
414
456
  # Here we munge values before they get sent on to rack-mount.
457
+ verbs = conditions[:request_method] || []
415
458
  unless verbs.empty?
416
459
  conditions[:request_method] = %r[^#{verbs.join('|')}$]
417
460
  end
418
- conditions.delete_if { |k,v| !(req_predicates.include?(k) || path_values.include?(k)) }
419
461
 
420
- conditions
462
+ conditions.keep_if do |k, _|
463
+ k == :action || k == :controller || k == :required_defaults ||
464
+ @request_class.public_method_defined?(k) || path_values.include?(k)
465
+ end
421
466
  end
422
467
  private :build_conditions
423
468
 
@@ -434,12 +479,11 @@ module ActionDispatch
434
479
 
435
480
  attr_reader :options, :recall, :set, :named_route
436
481
 
437
- def initialize(options, recall, set, extras = false)
482
+ def initialize(options, recall, set)
438
483
  @named_route = options.delete(:use_route)
439
484
  @options = options.dup
440
485
  @recall = recall.dup
441
486
  @set = set
442
- @extras = extras
443
487
 
444
488
  normalize_options!
445
489
  normalize_controller_action_id!
@@ -458,9 +502,7 @@ module ActionDispatch
458
502
 
459
503
  def use_recall_for(key)
460
504
  if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
461
- if named_route_exists?
462
- @options[key] = @recall.delete(key) if segment_keys.include?(key)
463
- else
505
+ if !named_route_exists? || segment_keys.include?(key)
464
506
  @options[key] = @recall.delete(key)
465
507
  end
466
508
  end
@@ -470,7 +512,7 @@ module ActionDispatch
470
512
  # If an explicit :controller was given, always make :action explicit
471
513
  # too, so that action expiry works as expected for things like
472
514
  #
473
- # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
515
+ # generate({controller: 'content'}, {controller: 'content', action: 'show'})
474
516
  #
475
517
  # (the above is from the unit tests). In the above case, because the
476
518
  # controller was explicitly given, but no action, the action is implied to
@@ -499,7 +541,7 @@ module ActionDispatch
499
541
  use_recall_for(:id)
500
542
  end
501
543
 
502
- # if the current controller is "foo/bar/baz" and :controller => "baz/bat"
544
+ # if the current controller is "foo/bar/baz" and controller: "baz/bat"
503
545
  # is specified, the controller becomes "foo/baz/bat"
504
546
  def use_relative_controller!
505
547
  if !named_route && different_controller? && !controller.start_with?("/")
@@ -515,8 +557,8 @@ module ActionDispatch
515
557
  @options[:controller] = controller.sub(%r{^/}, '') if controller
516
558
  end
517
559
 
518
- # This handles the case of :action => nil being explicitly passed.
519
- # It is identical to :action => "index"
560
+ # This handles the case of action: nil being explicitly passed.
561
+ # It is identical to action: "index"
520
562
  def handle_nil_action!
521
563
  if options.has_key?(:action) && options[:action].nil?
522
564
  options[:action] = 'index'
@@ -524,20 +566,10 @@ module ActionDispatch
524
566
  recall[:action] = options.delete(:action) if options[:action] == 'index'
525
567
  end
526
568
 
569
+ # Generates a path from routes, returns [path, params].
570
+ # If no route is generated the formatter will raise ActionController::UrlGenerationError
527
571
  def generate
528
- path, params = @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
529
-
530
- raise_routing_error unless path
531
-
532
- return [path, params.keys] if @extras
533
-
534
- [path, params]
535
- rescue Journey::Router::RoutingError
536
- raise_routing_error
537
- end
538
-
539
- def raise_routing_error
540
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
572
+ @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
541
573
  end
542
574
 
543
575
  def different_controller?
@@ -562,41 +594,53 @@ module ActionDispatch
562
594
  end
563
595
 
564
596
  def generate_extras(options, recall={})
565
- generate(options, recall, true)
597
+ path, params = generate(options, recall)
598
+ return path, params.keys
566
599
  end
567
600
 
568
- def generate(options, recall = {}, extras = false)
569
- Generator.new(options, recall, self, extras).generate
601
+ def generate(options, recall = {})
602
+ Generator.new(options, recall, self).generate
570
603
  end
571
604
 
572
605
  RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
573
- :trailing_slash, :anchor, :params, :only_path, :script_name]
606
+ :trailing_slash, :anchor, :params, :only_path, :script_name,
607
+ :original_script_name]
608
+
609
+ def mounted?
610
+ false
611
+ end
612
+
613
+ def optimize_routes_generation?
614
+ !mounted? && default_url_options.empty?
615
+ end
574
616
 
575
617
  def _generate_prefix(options = {})
576
618
  nil
577
619
  end
578
620
 
621
+ # The +options+ argument must be +nil+ or a hash whose keys are *symbols*.
579
622
  def url_for(options)
580
- finalize!
581
- options = (options || {}).reverse_merge!(default_url_options)
582
-
583
- handle_positional_args(options)
623
+ options = default_url_options.merge(options || {})
584
624
 
585
625
  user, password = extract_authentication(options)
586
- path_segments = options.delete(:_path_segments)
587
- script_name = options.delete(:script_name)
626
+ recall = options.delete(:_recall)
627
+
628
+ original_script_name = options.delete(:original_script_name).presence
629
+ script_name = options.delete(:script_name).presence || _generate_prefix(options)
588
630
 
589
- path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
631
+ if script_name && original_script_name
632
+ script_name = original_script_name + script_name
633
+ end
590
634
 
591
635
  path_options = options.except(*RESERVED_OPTIONS)
592
636
  path_options = yield(path_options) if block_given?
593
637
 
594
- path_addition, params = generate(path_options, path_segments || {})
595
- path << path_addition
638
+ path, params = generate(path_options, recall || {})
596
639
  params.merge!(options[:params] || {})
597
640
 
598
641
  ActionDispatch::Http::URL.url_for(options.merge!({
599
642
  :path => path,
643
+ :script_name => script_name,
600
644
  :params => params,
601
645
  :user => user,
602
646
  :password => password
@@ -604,7 +648,6 @@ module ActionDispatch
604
648
  end
605
649
 
606
650
  def call(env)
607
- finalize!
608
651
  @router.call(env)
609
652
  end
610
653
 
@@ -614,28 +657,34 @@ module ActionDispatch
614
657
  extras = environment[:extras] || {}
615
658
 
616
659
  begin
617
- env = Rack::MockRequest.env_for(path, {:method => method, :params => extras})
660
+ env = Rack::MockRequest.env_for(path, {:method => method})
618
661
  rescue URI::InvalidURIError => e
619
662
  raise ActionController::RoutingError, e.message
620
663
  end
621
664
 
622
665
  req = @request_class.new(env)
623
666
  @router.recognize(req) do |route, matches, params|
667
+ params.merge!(extras)
624
668
  params.each do |key, value|
625
669
  if value.is_a?(String)
626
- value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware?
670
+ value = value.dup.force_encoding(Encoding::BINARY)
627
671
  params[key] = URI.parser.unescape(value)
628
672
  end
629
673
  end
630
-
674
+ old_params = env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
675
+ env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] = (old_params || {}).merge(params)
631
676
  dispatcher = route.app
632
677
  while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
633
678
  dispatcher = dispatcher.app
634
679
  end
635
680
 
636
- if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
637
- dispatcher.prepare_params!(params)
638
- return params
681
+ if dispatcher.is_a?(Dispatcher)
682
+ if dispatcher.controller(params, false)
683
+ dispatcher.prepare_params!(params)
684
+ return params
685
+ else
686
+ raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
687
+ end
639
688
  end
640
689
  end
641
690
 
@@ -652,16 +701,6 @@ module ActionDispatch
652
701
  end
653
702
  end
654
703
 
655
- def handle_positional_args(options)
656
- return unless args = options.delete(:_positional_args)
657
-
658
- keys = options.delete(:_positional_keys)
659
- keys -= options.keys if args.size < keys.size - 1 # take format into account
660
-
661
- # Tell url_for to skip default_url_options
662
- options.merge!(Hash[args.zip(keys).map { |v, k| [k, v] }])
663
- end
664
-
665
704
  end
666
705
  end
667
706
  end