actionpack 3.2.22.5 → 5.2.4

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 (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -603
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -297
  5. data/lib/abstract_controller/asset_paths.rb +4 -2
  6. data/lib/abstract_controller/base.rb +82 -52
  7. data/lib/abstract_controller/caching/fragments.rb +166 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +117 -103
  10. data/lib/abstract_controller/collector.rb +18 -7
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +65 -38
  13. data/lib/abstract_controller/logger.rb +3 -2
  14. data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
  15. data/lib/abstract_controller/rendering.rb +77 -129
  16. data/lib/abstract_controller/translation.rb +21 -3
  17. data/lib/abstract_controller/url_for.rb +9 -7
  18. data/lib/abstract_controller.rb +12 -13
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +81 -40
  22. data/lib/action_controller/caching.rb +22 -62
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +30 -18
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +190 -47
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +40 -65
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  32. data/lib/action_controller/metal/exceptions.rb +19 -12
  33. data/lib/action_controller/metal/flash.rb +42 -9
  34. data/lib/action_controller/metal/force_ssl.rb +79 -19
  35. data/lib/action_controller/metal/head.rb +35 -10
  36. data/lib/action_controller/metal/helpers.rb +31 -21
  37. data/lib/action_controller/metal/http_authentication.rb +182 -134
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +28 -26
  40. data/lib/action_controller/metal/live.rb +312 -0
  41. data/lib/action_controller/metal/mime_responds.rb +159 -163
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +146 -93
  44. data/lib/action_controller/metal/redirecting.rb +80 -56
  45. data/lib/action_controller/metal/renderers.rb +119 -47
  46. data/lib/action_controller/metal/rendering.rb +89 -32
  47. data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +39 -45
  50. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  51. data/lib/action_controller/metal/testing.rb +8 -29
  52. data/lib/action_controller/metal/url_for.rb +43 -32
  53. data/lib/action_controller/metal.rb +112 -106
  54. data/lib/action_controller/railtie.rb +56 -18
  55. data/lib/action_controller/railties/helpers.rb +24 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +402 -347
  59. data/lib/action_controller.rb +31 -30
  60. data/lib/action_dispatch/http/cache.rb +133 -34
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +40 -24
  63. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  64. data/lib/action_dispatch/http/headers.rb +117 -16
  65. data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
  66. data/lib/action_dispatch/http/mime_type.rb +198 -146
  67. data/lib/action_dispatch/http/mime_types.rb +22 -7
  68. data/lib/action_dispatch/http/parameter_filter.rb +61 -49
  69. data/lib/action_dispatch/http/parameters.rb +94 -51
  70. data/lib/action_dispatch/http/rack_cache.rb +4 -3
  71. data/lib/action_dispatch/http/request.rb +262 -117
  72. data/lib/action_dispatch/http/response.rb +400 -86
  73. data/lib/action_dispatch/http/upload.rb +66 -29
  74. data/lib/action_dispatch/http/url.rb +232 -60
  75. data/lib/action_dispatch/journey/formatter.rb +189 -0
  76. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  79. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  80. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  83. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  84. data/lib/action_dispatch/journey/parser.rb +199 -0
  85. data/lib/action_dispatch/journey/parser.y +50 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +199 -0
  88. data/lib/action_dispatch/journey/route.rb +203 -0
  89. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  90. data/lib/action_dispatch/journey/router.rb +156 -0
  91. data/lib/action_dispatch/journey/routes.rb +82 -0
  92. data/lib/action_dispatch/journey/scanner.rb +64 -0
  93. data/lib/action_dispatch/journey/visitors.rb +268 -0
  94. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  95. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  96. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  97. data/lib/action_dispatch/journey.rb +7 -0
  98. data/lib/action_dispatch/middleware/callbacks.rb +17 -13
  99. data/lib/action_dispatch/middleware/cookies.rb +494 -162
  100. data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
  101. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  102. data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
  103. data/lib/action_dispatch/middleware/executor.rb +21 -0
  104. data/lib/action_dispatch/middleware/flash.rb +128 -91
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
  106. data/lib/action_dispatch/middleware/reloader.rb +6 -83
  107. data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
  108. data/lib/action_dispatch/middleware/request_id.rb +19 -15
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
  114. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  115. data/lib/action_dispatch/middleware/stack.rb +33 -41
  116. data/lib/action_dispatch/middleware/static.rb +92 -48
  117. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  136. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  138. data/lib/action_dispatch/railtie.rb +29 -8
  139. data/lib/action_dispatch/request/session.rb +234 -0
  140. data/lib/action_dispatch/request/utils.rb +78 -0
  141. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  142. data/lib/action_dispatch/routing/inspector.rb +225 -0
  143. data/lib/action_dispatch/routing/mapper.rb +1329 -582
  144. data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
  145. data/lib/action_dispatch/routing/redirection.rb +120 -50
  146. data/lib/action_dispatch/routing/route_set.rb +545 -322
  147. data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
  148. data/lib/action_dispatch/routing/url_for.rb +103 -34
  149. data/lib/action_dispatch/routing.rb +66 -99
  150. data/lib/action_dispatch/system_test_case.rb +147 -0
  151. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  152. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  153. data/lib/action_dispatch/system_testing/server.rb +31 -0
  154. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  158. data/lib/action_dispatch/testing/assertions/response.rb +53 -42
  159. data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
  160. data/lib/action_dispatch/testing/assertions.rb +15 -9
  161. data/lib/action_dispatch/testing/integration.rb +361 -207
  162. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  163. data/lib/action_dispatch/testing/test_process.rb +28 -19
  164. data/lib/action_dispatch/testing/test_request.rb +30 -33
  165. data/lib/action_dispatch/testing/test_response.rb +35 -11
  166. data/lib/action_dispatch.rb +42 -32
  167. data/lib/action_pack/gem_version.rb +17 -0
  168. data/lib/action_pack/version.rb +7 -7
  169. data/lib/action_pack.rb +4 -2
  170. metadata +116 -175
  171. data/lib/abstract_controller/layouts.rb +0 -423
  172. data/lib/abstract_controller/view_paths.rb +0 -96
  173. data/lib/action_controller/caching/actions.rb +0 -185
  174. data/lib/action_controller/caching/fragments.rb +0 -127
  175. data/lib/action_controller/caching/pages.rb +0 -187
  176. data/lib/action_controller/caching/sweeping.rb +0 -97
  177. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  178. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  179. data/lib/action_controller/deprecated.rb +0 -3
  180. data/lib/action_controller/metal/compatibility.rb +0 -65
  181. data/lib/action_controller/metal/hide_actions.rb +0 -41
  182. data/lib/action_controller/metal/rack_delegation.rb +0 -26
  183. data/lib/action_controller/metal/responder.rb +0 -286
  184. data/lib/action_controller/metal/session_management.rb +0 -14
  185. data/lib/action_controller/middleware.rb +0 -39
  186. data/lib/action_controller/railties/paths.rb +0 -25
  187. data/lib/action_controller/record_identifier.rb +0 -85
  188. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  189. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  190. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  191. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  192. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  193. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  194. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  195. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  196. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  197. data/lib/action_dispatch/middleware/head.rb +0 -18
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -75
  199. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  200. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  201. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  202. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  203. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  204. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  205. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  206. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  207. data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
  208. data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
  209. data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
  210. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  211. data/lib/action_view/asset_paths.rb +0 -142
  212. data/lib/action_view/base.rb +0 -220
  213. data/lib/action_view/buffers.rb +0 -43
  214. data/lib/action_view/context.rb +0 -36
  215. data/lib/action_view/flows.rb +0 -79
  216. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  217. data/lib/action_view/helpers/asset_paths.rb +0 -7
  218. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  219. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  220. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  221. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  222. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  223. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  224. data/lib/action_view/helpers/cache_helper.rb +0 -64
  225. data/lib/action_view/helpers/capture_helper.rb +0 -203
  226. data/lib/action_view/helpers/controller_helper.rb +0 -25
  227. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  228. data/lib/action_view/helpers/date_helper.rb +0 -1062
  229. data/lib/action_view/helpers/debug_helper.rb +0 -40
  230. data/lib/action_view/helpers/form_helper.rb +0 -1486
  231. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  232. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  233. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  234. data/lib/action_view/helpers/number_helper.rb +0 -622
  235. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  236. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  237. data/lib/action_view/helpers/rendering_helper.rb +0 -92
  238. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  239. data/lib/action_view/helpers/tag_helper.rb +0 -167
  240. data/lib/action_view/helpers/text_helper.rb +0 -426
  241. data/lib/action_view/helpers/translation_helper.rb +0 -91
  242. data/lib/action_view/helpers/url_helper.rb +0 -693
  243. data/lib/action_view/helpers.rb +0 -60
  244. data/lib/action_view/locale/en.yml +0 -160
  245. data/lib/action_view/log_subscriber.rb +0 -28
  246. data/lib/action_view/lookup_context.rb +0 -258
  247. data/lib/action_view/path_set.rb +0 -101
  248. data/lib/action_view/railtie.rb +0 -55
  249. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  250. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  251. data/lib/action_view/renderer/renderer.rb +0 -61
  252. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  253. data/lib/action_view/renderer/template_renderer.rb +0 -95
  254. data/lib/action_view/template/error.rb +0 -128
  255. data/lib/action_view/template/handlers/builder.rb +0 -26
  256. data/lib/action_view/template/handlers/erb.rb +0 -125
  257. data/lib/action_view/template/handlers.rb +0 -50
  258. data/lib/action_view/template/resolver.rb +0 -298
  259. data/lib/action_view/template/text.rb +0 -30
  260. data/lib/action_view/template.rb +0 -337
  261. data/lib/action_view/test_case.rb +0 -246
  262. data/lib/action_view/testing/resolvers.rb +0 -49
  263. data/lib/action_view.rb +0 -84
  264. data/lib/sprockets/assets.rake +0 -99
  265. data/lib/sprockets/bootstrap.rb +0 -37
  266. data/lib/sprockets/compressors.rb +0 -83
  267. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  268. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  269. data/lib/sprockets/helpers.rb +0 -6
  270. data/lib/sprockets/railtie.rb +0 -62
  271. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,127 +1,134 @@
1
- require 'journey'
2
- require 'forwardable'
3
- require 'active_support/core_ext/object/blank'
4
- require 'active_support/core_ext/object/to_query'
5
- require 'active_support/core_ext/hash/slice'
6
- require 'active_support/core_ext/module/remove_method'
7
- require 'action_controller/metal/exceptions'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/journey"
4
+ require "active_support/core_ext/object/to_query"
5
+ require "active_support/core_ext/hash/slice"
6
+ require "active_support/core_ext/module/redefine_method"
7
+ require "active_support/core_ext/module/remove_method"
8
+ require "active_support/core_ext/array/extract_options"
9
+ require "action_controller/metal/exceptions"
10
+ require "action_dispatch/http/request"
11
+ require "action_dispatch/routing/endpoint"
8
12
 
9
13
  module ActionDispatch
10
14
  module Routing
11
- class RouteSet #:nodoc:
15
+ # :stopdoc:
16
+ class RouteSet
12
17
  # Since the router holds references to many parts of the system
13
18
  # like engines, controllers and the application itself, inspecting
14
19
  # the route set can actually be really slow, therefore we default
15
20
  # alias inspect to to_s.
16
21
  alias inspect to_s
17
22
 
18
- PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
19
-
20
- class Dispatcher #:nodoc:
21
- def initialize(options={})
22
- @defaults = options[:defaults]
23
- @glob_param = options.delete(:glob)
24
- @controllers = {}
25
- end
26
-
27
- def call(env)
28
- params = env[PARAMETERS_KEY]
29
- prepare_params!(params)
30
-
31
- # Just raise undefined constant errors if a controller was specified as default.
32
- unless controller = controller(params, @defaults.key?(:controller))
33
- return [404, {'X-Cascade' => 'pass'}, []]
34
- end
35
-
36
- dispatch(controller, params[:action], env)
23
+ class Dispatcher < Routing::Endpoint
24
+ def initialize(raise_on_name_error)
25
+ @raise_on_name_error = raise_on_name_error
37
26
  end
38
27
 
39
- def prepare_params!(params)
40
- normalize_controller!(params)
41
- merge_default_action!(params)
42
- split_glob_param!(params) if @glob_param
43
- end
28
+ def dispatcher?; true; end
44
29
 
45
- # If this is a default_controller (i.e. a controller specified by the user)
46
- # we should raise an error in case it's not found, because it usually means
47
- # a user error. However, if the controller was retrieved through a dynamic
48
- # segment, as in :controller(/:action), we should simply return nil and
49
- # delegate the control back to Rack cascade. Besides, if this is not a default
50
- # controller, it means we should respect the @scope[:module] parameter.
51
- def controller(params, default_controller=true)
52
- if params && params.key?(:controller)
53
- controller_param = params[:controller]
54
- controller_reference(controller_param)
30
+ def serve(req)
31
+ params = req.path_parameters
32
+ controller = controller req
33
+ res = controller.make_response! req
34
+ dispatch(controller, params[:action], req, res)
35
+ rescue ActionController::RoutingError
36
+ if @raise_on_name_error
37
+ raise
38
+ else
39
+ return [404, { "X-Cascade" => "pass" }, []]
55
40
  end
56
- rescue NameError => e
57
- raise ActionController::RoutingError, e.message, e.backtrace if default_controller
58
41
  end
59
42
 
60
43
  private
61
44
 
62
- 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)
45
+ def controller(req)
46
+ req.controller_class
47
+ rescue NameError => e
48
+ raise ActionController::RoutingError, e.message, e.backtrace
70
49
  end
71
50
 
72
- def dispatch(controller, action, env)
73
- controller.action(action).call(env)
51
+ def dispatch(controller, action, req, res)
52
+ controller.dispatch(action, req, res)
74
53
  end
54
+ end
75
55
 
76
- def normalize_controller!(params)
77
- params[:controller] = params[:controller].underscore if params.key?(:controller)
56
+ class StaticDispatcher < Dispatcher
57
+ def initialize(controller_class)
58
+ super(false)
59
+ @controller_class = controller_class
78
60
  end
79
61
 
80
- def merge_default_action!(params)
81
- params[:action] ||= 'index'
82
- end
62
+ private
83
63
 
84
- def split_glob_param!(params)
85
- params[@glob_param] = params[@glob_param].split('/').map { |v| URI.parser.unescape(v) }
86
- end
64
+ def controller(_); @controller_class; end
87
65
  end
88
66
 
89
67
  # A NamedRouteCollection instance is a collection of named routes, and also
90
68
  # maintains an anonymous module that can be used to install helpers for the
91
69
  # named routes.
92
- class NamedRouteCollection #:nodoc:
70
+ class NamedRouteCollection
93
71
  include Enumerable
94
- attr_reader :routes, :helpers, :module
72
+ attr_reader :routes, :url_helpers_module, :path_helpers_module
73
+ private :routes
95
74
 
96
75
  def initialize
97
76
  @routes = {}
98
- @helpers = []
77
+ @path_helpers = Set.new
78
+ @url_helpers = Set.new
79
+ @url_helpers_module = Module.new
80
+ @path_helpers_module = Module.new
81
+ end
99
82
 
100
- @module = Module.new
83
+ def route_defined?(name)
84
+ key = name.to_sym
85
+ @path_helpers.include?(key) || @url_helpers.include?(key)
101
86
  end
102
87
 
103
88
  def helper_names
104
- self.module.instance_methods.map(&:to_s)
89
+ @path_helpers.map(&:to_s) + @url_helpers.map(&:to_s)
105
90
  end
106
91
 
107
92
  def clear!
108
- @helpers.each do |helper|
109
- @module.remove_possible_method helper
93
+ @path_helpers.each do |helper|
94
+ @path_helpers_module.send :remove_method, helper
95
+ end
96
+
97
+ @url_helpers.each do |helper|
98
+ @url_helpers_module.send :remove_method, helper
110
99
  end
111
100
 
112
101
  @routes.clear
113
- @helpers.clear
102
+ @path_helpers.clear
103
+ @url_helpers.clear
114
104
  end
115
105
 
116
106
  def add(name, route)
117
- routes[name.to_sym] = route
118
- define_named_route_methods(name, route)
107
+ key = name.to_sym
108
+ path_name = :"#{name}_path"
109
+ url_name = :"#{name}_url"
110
+
111
+ if routes.key? key
112
+ @path_helpers_module.send :undef_method, path_name
113
+ @url_helpers_module.send :undef_method, url_name
114
+ end
115
+ routes[key] = route
116
+ define_url_helper @path_helpers_module, route, path_name, route.defaults, name, PATH
117
+ define_url_helper @url_helpers_module, route, url_name, route.defaults, name, UNKNOWN
118
+
119
+ @path_helpers << path_name
120
+ @url_helpers << url_name
119
121
  end
120
122
 
121
123
  def get(name)
122
124
  routes[name.to_sym]
123
125
  end
124
126
 
127
+ def key?(name)
128
+ return unless name
129
+ routes.key? name.to_sym
130
+ end
131
+
125
132
  alias []= add
126
133
  alias [] get
127
134
  alias clear clear!
@@ -139,125 +146,269 @@ module ActionDispatch
139
146
  routes.length
140
147
  end
141
148
 
142
- def reset!
143
- old_routes = routes.dup
144
- clear!
145
- old_routes.each do |name, route|
146
- add(name, route)
149
+ # Given a +name+, defines name_path and name_url helpers.
150
+ # Used by 'direct', 'resolve', and 'polymorphic' route helpers.
151
+ def add_url_helper(name, defaults, &block)
152
+ helper = CustomUrlHelper.new(name, defaults, &block)
153
+ path_name = :"#{name}_path"
154
+ url_name = :"#{name}_url"
155
+
156
+ @path_helpers_module.module_eval do
157
+ define_method(path_name) do |*args|
158
+ helper.call(self, args, true)
159
+ end
147
160
  end
148
- end
149
161
 
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)
162
+ @url_helpers_module.module_eval do
163
+ define_method(url_name) do |*args|
164
+ helper.call(self, args, false)
165
+ end
154
166
  end
167
+
168
+ @path_helpers << path_name
169
+ @url_helpers << url_name
170
+
171
+ self
155
172
  end
156
173
 
157
- private
158
- def url_helper_name(name, kind = :url)
159
- :"#{name}_#{kind}"
174
+ class UrlHelper
175
+ def self.create(route, options, route_name, url_strategy)
176
+ if optimize_helper?(route)
177
+ OptimizedUrlHelper.new(route, options, route_name, url_strategy)
178
+ else
179
+ new route, options, route_name, url_strategy
180
+ end
160
181
  end
161
182
 
162
- def hash_access_name(name, kind = :url)
163
- :"hash_for_#{name}_#{kind}"
183
+ def self.optimize_helper?(route)
184
+ !route.glob? && route.path.requirements.empty?
164
185
  end
165
186
 
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
187
+ attr_reader :url_strategy, :route_name
188
+
189
+ class OptimizedUrlHelper < UrlHelper
190
+ attr_reader :arg_size
191
+
192
+ def initialize(route, options, route_name, url_strategy)
193
+ super
194
+ @required_parts = @route.required_parts
195
+ @arg_size = @required_parts.size
171
196
  end
172
- end
173
197
 
174
- def define_hash_access(route, name, kind, options)
175
- selector = hash_access_name(name, kind)
198
+ def call(t, args, inner_options)
199
+ if args.size == arg_size && !inner_options && optimize_routes_generation?(t)
200
+ options = t.url_options.merge @options
201
+ options[:path] = optimized_helper(args)
176
202
 
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}
203
+ original_script_name = options.delete(:original_script_name)
204
+ script_name = t._routes.find_script_name(options)
183
205
 
184
- if args.size > 0
185
- result[:_positional_args] = args
186
- result[:_positional_keys] = #{route.segment_keys.inspect}
206
+ if original_script_name
207
+ script_name = original_script_name + script_name
187
208
  end
188
209
 
189
- result.merge(options)
210
+ options[:script_name] = script_name
211
+
212
+ url_strategy.call options
213
+ else
214
+ super
190
215
  end
191
- protected :#{selector}
192
- END_EVAL
193
- helpers << selector
216
+ end
217
+
218
+ private
219
+
220
+ def optimized_helper(args)
221
+ params = parameterize_args(args) do
222
+ raise_generation_error(args)
223
+ end
224
+
225
+ @route.format params
226
+ end
227
+
228
+ def optimize_routes_generation?(t)
229
+ t.send(:optimize_routes_generation?)
230
+ end
231
+
232
+ def parameterize_args(args)
233
+ params = {}
234
+ @arg_size.times { |i|
235
+ key = @required_parts[i]
236
+ value = args[i].to_param
237
+ yield key if value.nil? || value.empty?
238
+ params[key] = value
239
+ }
240
+ params
241
+ end
242
+
243
+ def raise_generation_error(args)
244
+ missing_keys = []
245
+ params = parameterize_args(args) { |missing_key|
246
+ missing_keys << missing_key
247
+ }
248
+ constraints = Hash[@route.requirements.merge(params).sort_by { |k, v| k.to_s }]
249
+ message = "No route matches #{constraints.inspect}".dup
250
+ message << ", missing required keys: #{missing_keys.sort.inspect}"
251
+
252
+ raise ActionController::UrlGenerationError, message
253
+ end
254
+ end
255
+
256
+ def initialize(route, options, route_name, url_strategy)
257
+ @options = options
258
+ @segment_keys = route.segment_keys.uniq
259
+ @route = route
260
+ @url_strategy = url_strategy
261
+ @route_name = route_name
194
262
  end
195
263
 
196
- # Create a url helper allowing ordered parameters to be associated
264
+ def call(t, args, inner_options)
265
+ controller_options = t.url_options
266
+ options = controller_options.merge @options
267
+ hash = handle_positional_args(controller_options,
268
+ inner_options || {},
269
+ args,
270
+ options,
271
+ @segment_keys)
272
+
273
+ t._routes.url_for(hash, route_name, url_strategy)
274
+ end
275
+
276
+ def handle_positional_args(controller_options, inner_options, args, result, path_params)
277
+ if args.size > 0
278
+ # take format into account
279
+ if path_params.include?(:format)
280
+ path_params_size = path_params.size - 1
281
+ else
282
+ path_params_size = path_params.size
283
+ end
284
+
285
+ if args.size < path_params_size
286
+ path_params -= controller_options.keys
287
+ path_params -= result.keys
288
+ else
289
+ path_params = path_params.dup
290
+ end
291
+ inner_options.each_key do |key|
292
+ path_params.delete(key)
293
+ end
294
+
295
+ args.each_with_index do |arg, index|
296
+ param = path_params[index]
297
+ result[param] = arg if param
298
+ end
299
+ end
300
+
301
+ result.merge!(inner_options)
302
+ end
303
+ end
304
+
305
+ private
306
+ # Create a URL helper allowing ordered parameters to be associated
197
307
  # with corresponding dynamic segments, so you can do:
198
308
  #
199
309
  # foo_url(bar, baz, bang)
200
310
  #
201
311
  # Instead of:
202
312
  #
203
- # foo_url(:bar => bar, :baz => baz, :bang => bang)
313
+ # foo_url(bar: bar, baz: baz, bang: bang)
204
314
  #
205
315
  # Also allow options hash, so you can do:
206
316
  #
207
- # foo_url(bar, baz, bang, :sort_by => 'baz')
317
+ # foo_url(bar, baz, bang, sort_by: 'baz')
208
318
  #
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))
319
+ def define_url_helper(mod, route, name, opts, route_key, url_strategy)
320
+ helper = UrlHelper.create(route, opts, route_key, url_strategy)
321
+ mod.module_eval do
322
+ define_method(name) do |*args|
323
+ last = args.last
324
+ options = \
325
+ case last
326
+ when Hash
327
+ args.pop
328
+ when ActionController::Parameters
329
+ args.pop.to_h
330
+ end
331
+ helper.call self, args, options
217
332
  end
218
- END_EVAL
219
- helpers << selector
333
+ end
220
334
  end
221
335
  end
222
336
 
337
+ # strategy for building urls to send to the client
338
+ PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
339
+ UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
340
+
223
341
  attr_accessor :formatter, :set, :named_routes, :default_scope, :router
224
342
  attr_accessor :disable_clear_and_finalize, :resources_path_names
225
- attr_accessor :default_url_options, :request_class, :valid_conditions
343
+ attr_accessor :default_url_options
344
+ attr_reader :env_key, :polymorphic_mappings
226
345
 
227
346
  alias :routes :set
228
347
 
229
348
  def self.default_resources_path_names
230
- { :new => 'new', :edit => 'edit' }
349
+ { new: "new", edit: "edit" }
231
350
  end
232
351
 
233
- def initialize(request_class = ActionDispatch::Request)
234
- self.named_routes = NamedRouteCollection.new
235
- self.resources_path_names = self.class.default_resources_path_names.dup
236
- self.default_url_options = {}
352
+ def self.new_with_config(config)
353
+ route_set_config = DEFAULT_CONFIG
354
+
355
+ # engines apparently don't have this set
356
+ if config.respond_to? :relative_url_root
357
+ route_set_config.relative_url_root = config.relative_url_root
358
+ end
359
+
360
+ if config.respond_to? :api_only
361
+ route_set_config.api_only = config.api_only
362
+ end
237
363
 
238
- self.request_class = request_class
239
- @valid_conditions = {}
364
+ new route_set_config
365
+ end
366
+
367
+ Config = Struct.new :relative_url_root, :api_only
240
368
 
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
369
+ DEFAULT_CONFIG = Config.new(nil, false)
246
370
 
247
- self.valid_conditions.delete(:id)
371
+ def initialize(config = DEFAULT_CONFIG)
372
+ self.named_routes = NamedRouteCollection.new
373
+ self.resources_path_names = self.class.default_resources_path_names
374
+ self.default_url_options = {}
248
375
 
376
+ @config = config
249
377
  @append = []
250
378
  @prepend = []
251
379
  @disable_clear_and_finalize = false
252
380
  @finalized = false
381
+ @env_key = "ROUTES_#{object_id}_SCRIPT_NAME".freeze
253
382
 
254
383
  @set = Journey::Routes.new
255
- @router = Journey::Router.new(@set, {
256
- :parameters_key => PARAMETERS_KEY,
257
- :request_class => request_class})
258
- @formatter = Journey::Formatter.new @set
384
+ @router = Journey::Router.new @set
385
+ @formatter = Journey::Formatter.new self
386
+ @polymorphic_mappings = {}
259
387
  end
260
388
 
389
+ def eager_load!
390
+ router.eager_load!
391
+ routes.each(&:eager_load!)
392
+ nil
393
+ end
394
+
395
+ def relative_url_root
396
+ @config.relative_url_root
397
+ end
398
+
399
+ def api_only?
400
+ @config.api_only
401
+ end
402
+
403
+ def request_class
404
+ ActionDispatch::Request
405
+ end
406
+
407
+ def make_request(env)
408
+ request_class.new env
409
+ end
410
+ private :make_request
411
+
261
412
  def draw(&block)
262
413
  clear! unless @disable_clear_and_finalize
263
414
  eval_block(block)
@@ -274,10 +425,6 @@ module ActionDispatch
274
425
  end
275
426
 
276
427
  def eval_block(block)
277
- if block.arity == 1
278
- raise "You are using the old router DSL which has been removed in Rails 3.1. " <<
279
- "Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
280
- end
281
428
  mapper = Mapper.new(self)
282
429
  if default_scope
283
430
  mapper.with_default_scope(default_scope, &block)
@@ -285,6 +432,7 @@ module ActionDispatch
285
432
  mapper.instance_exec(&block)
286
433
  end
287
434
  end
435
+ private :eval_block
288
436
 
289
437
  def finalize!
290
438
  return if @finalized
@@ -297,64 +445,131 @@ module ActionDispatch
297
445
  named_routes.clear
298
446
  set.clear
299
447
  formatter.clear
448
+ @polymorphic_mappings.clear
300
449
  @prepend.each { |blk| eval_block(blk) }
301
450
  end
302
451
 
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
452
  module MountedHelpers
453
+ extend ActiveSupport::Concern
454
+ include UrlFor
309
455
  end
310
456
 
457
+ # Contains all the mounted helpers across different
458
+ # engines and the `main_app` helper for the application.
459
+ # You can include this in your classes if you want to
460
+ # access routes for other engines.
311
461
  def mounted_helpers
312
462
  MountedHelpers
313
463
  end
314
464
 
315
- def define_mounted_helper(name)
465
+ def define_mounted_helper(name, script_namer = nil)
316
466
  return if MountedHelpers.method_defined?(name)
317
467
 
318
468
  routes = self
469
+ helpers = routes.url_helpers
470
+
319
471
  MountedHelpers.class_eval do
320
472
  define_method "_#{name}" do
321
- RoutesProxy.new(routes, self._routes_context)
473
+ RoutesProxy.new(routes, _routes_context, helpers, script_namer)
322
474
  end
323
475
  end
324
476
 
325
- MountedHelpers.class_eval <<-RUBY
477
+ MountedHelpers.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
326
478
  def #{name}
327
- @#{name} ||= _#{name}
479
+ @_#{name} ||= _#{name}
328
480
  end
329
481
  RUBY
330
482
  end
331
483
 
332
- def url_helpers
333
- @url_helpers ||= begin
334
- routes = self
484
+ def url_helpers(supports_path = true)
485
+ routes = self
486
+
487
+ Module.new do
488
+ extend ActiveSupport::Concern
489
+ include UrlFor
335
490
 
336
- helpers = Module.new do
337
- extend ActiveSupport::Concern
491
+ # Define url_for in the singleton level so one can do:
492
+ # Rails.application.routes.url_helpers.url_for(args)
493
+ proxy_class = Class.new do
338
494
  include UrlFor
495
+ include routes.named_routes.path_helpers_module
496
+ include routes.named_routes.url_helpers_module
497
+
498
+ attr_reader :_routes
499
+
500
+ def initialize(routes)
501
+ @_routes = routes
502
+ end
503
+
504
+ def optimize_routes_generation?
505
+ @_routes.optimize_routes_generation?
506
+ end
507
+ end
508
+
509
+ @_proxy = proxy_class.new(routes)
339
510
 
340
- @_routes = routes
341
- class << self
342
- delegate :url_for, :to => '@_routes'
511
+ class << self
512
+ def url_for(options)
513
+ @_proxy.url_for(options)
343
514
  end
344
- extend routes.named_routes.module
345
-
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
349
- included do
350
- routes.install_helpers(self)
351
- singleton_class.send(:redefine_method, :_routes) { routes }
515
+
516
+ def full_url_for(options)
517
+ @_proxy.full_url_for(options)
352
518
  end
353
519
 
354
- define_method(:_routes) { @_routes || routes }
520
+ def route_for(name, *args)
521
+ @_proxy.route_for(name, *args)
522
+ end
523
+
524
+ def optimize_routes_generation?
525
+ @_proxy.optimize_routes_generation?
526
+ end
527
+
528
+ def polymorphic_url(record_or_hash_or_array, options = {})
529
+ @_proxy.polymorphic_url(record_or_hash_or_array, options)
530
+ end
531
+
532
+ def polymorphic_path(record_or_hash_or_array, options = {})
533
+ @_proxy.polymorphic_path(record_or_hash_or_array, options)
534
+ end
535
+
536
+ def _routes; @_proxy._routes; end
537
+ def url_options; {}; end
538
+ end
539
+
540
+ url_helpers = routes.named_routes.url_helpers_module
541
+
542
+ # Make named_routes available in the module singleton
543
+ # as well, so one can do:
544
+ # Rails.application.routes.url_helpers.posts_path
545
+ extend url_helpers
546
+
547
+ # Any class that includes this module will get all
548
+ # named routes...
549
+ include url_helpers
550
+
551
+ if supports_path
552
+ path_helpers = routes.named_routes.path_helpers_module
553
+
554
+ include path_helpers
555
+ extend path_helpers
556
+ end
557
+
558
+ # plus a singleton class method called _routes ...
559
+ included do
560
+ redefine_singleton_method(:_routes) { routes }
561
+ end
562
+
563
+ # And an instance method _routes. Note that
564
+ # UrlFor (included in this module) add extra
565
+ # conveniences for working with @_routes.
566
+ define_method(:_routes) { @_routes || routes }
567
+
568
+ define_method(:_generate_paths_by_default) do
569
+ supports_path
355
570
  end
356
571
 
357
- helpers
572
+ private :_generate_paths_by_default
358
573
  end
359
574
  end
360
575
 
@@ -362,90 +577,96 @@ module ActionDispatch
362
577
  routes.empty?
363
578
  end
364
579
 
365
- def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
580
+ def add_route(mapping, name)
366
581
  raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
367
582
 
368
- path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor)
369
- conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym })
583
+ if name && named_routes[name]
584
+ raise ArgumentError, "Invalid route name, already in use: '#{name}' \n" \
585
+ "You may have defined two routes with the same name using the `:as` option, or " \
586
+ "you may be overriding a route already defined by a resource with the same naming. " \
587
+ "For the latter, you can restrict the routes created with `resources` as explained here: \n" \
588
+ "http://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
589
+ end
370
590
 
371
- route = @set.add_route(app, path, conditions, defaults, name)
591
+ route = @set.add_route(name, mapping)
372
592
  named_routes[name] = route if name
373
- route
374
- end
375
-
376
- def build_path(path, requirements, separators, anchor)
377
- strexp = Journey::Router::Strexp.new(
378
- path,
379
- requirements,
380
- SEPARATORS,
381
- anchor)
382
593
 
383
- pattern = Journey::Path::Pattern.new(strexp)
384
-
385
- builder = Journey::GTG::Builder.new pattern.spec
594
+ if route.segment_keys.include?(:controller)
595
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
596
+ Using a dynamic :controller segment in a route is deprecated and
597
+ will be removed in Rails 6.0.
598
+ MSG
599
+ end
386
600
 
387
- # Get all the symbol nodes followed by literals that are not the
388
- # dummy node.
389
- symbols = pattern.spec.grep(Journey::Nodes::Symbol).find_all { |n|
390
- builder.followpos(n).first.literal?
391
- }
601
+ if route.segment_keys.include?(:action)
602
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
603
+ Using a dynamic :action segment in a route is deprecated and
604
+ will be removed in Rails 6.0.
605
+ MSG
606
+ end
392
607
 
393
- # Get all the symbol nodes preceded by literals.
394
- symbols.concat pattern.spec.find_all(&:literal?).map { |n|
395
- builder.followpos(n).first
396
- }.find_all(&:symbol?)
608
+ route
609
+ end
397
610
 
398
- symbols.each { |x|
399
- x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
400
- }
611
+ def add_polymorphic_mapping(klass, options, &block)
612
+ @polymorphic_mappings[klass] = CustomUrlHelper.new(klass, options, &block)
613
+ end
401
614
 
402
- pattern
615
+ def add_url_helper(name, options, &block)
616
+ named_routes.add_url_helper(name, options, &block)
403
617
  end
404
- private :build_path
405
618
 
406
- def build_conditions(current_conditions, req_predicates, path_values)
407
- conditions = current_conditions.dup
619
+ class CustomUrlHelper
620
+ attr_reader :name, :defaults, :block
408
621
 
409
- verbs = conditions[:request_method] || []
622
+ def initialize(name, defaults, &block)
623
+ @name = name
624
+ @defaults = defaults
625
+ @block = block
626
+ end
627
+
628
+ def call(t, args, only_path = false)
629
+ options = args.extract_options!
630
+ url = t.full_url_for(eval_block(t, args, options))
410
631
 
411
- # Rack-Mount requires that :request_method be a regular expression.
412
- # :request_method represents the HTTP verb that matches this route.
413
- #
414
- # Here we munge values before they get sent on to rack-mount.
415
- unless verbs.empty?
416
- conditions[:request_method] = %r[^#{verbs.join('|')}$]
632
+ if only_path
633
+ "/" + url.partition(%r{(?<!/)/(?!/)}).last
634
+ else
635
+ url
636
+ end
417
637
  end
418
- conditions.delete_if { |k,v| !(req_predicates.include?(k) || path_values.include?(k)) }
419
638
 
420
- conditions
639
+ private
640
+ def eval_block(t, args, options)
641
+ t.instance_exec(*args, merge_defaults(options), &block)
642
+ end
643
+
644
+ def merge_defaults(options)
645
+ defaults ? defaults.merge(options) : options
646
+ end
421
647
  end
422
- private :build_conditions
423
648
 
424
- class Generator #:nodoc:
649
+ class Generator
425
650
  PARAMETERIZE = lambda do |name, value|
426
651
  if name == :controller
427
652
  value
428
- elsif value.is_a?(Array)
429
- value.map { |v| v.to_param }.join('/')
430
- elsif param = value.to_param
431
- param
653
+ else
654
+ value.to_param
432
655
  end
433
656
  end
434
657
 
435
658
  attr_reader :options, :recall, :set, :named_route
436
659
 
437
- def initialize(options, recall, set, extras = false)
438
- @named_route = options.delete(:use_route)
439
- @options = options.dup
440
- @recall = recall.dup
660
+ def initialize(named_route, options, recall, set)
661
+ @named_route = named_route
662
+ @options = options
663
+ @recall = recall
441
664
  @set = set
442
- @extras = extras
443
665
 
444
666
  normalize_options!
445
667
  normalize_controller_action_id!
446
668
  use_relative_controller!
447
669
  normalize_controller!
448
- handle_nil_action!
449
670
  end
450
671
 
451
672
  def controller
@@ -458,10 +679,8 @@ module ActionDispatch
458
679
 
459
680
  def use_recall_for(key)
460
681
  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
464
- @options[key] = @recall.delete(key)
682
+ if !named_route_exists? || segment_keys.include?(key)
683
+ @options[key] = @recall[key]
465
684
  end
466
685
  end
467
686
  end
@@ -470,19 +689,19 @@ module ActionDispatch
470
689
  # If an explicit :controller was given, always make :action explicit
471
690
  # too, so that action expiry works as expected for things like
472
691
  #
473
- # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
692
+ # generate({controller: 'content'}, {controller: 'content', action: 'show'})
474
693
  #
475
694
  # (the above is from the unit tests). In the above case, because the
476
695
  # controller was explicitly given, but no action, the action is implied to
477
696
  # be "index", not the recalled action of "show".
478
697
 
479
698
  if options[:controller]
480
- options[:action] ||= 'index'
699
+ options[:action] ||= "index"
481
700
  options[:controller] = options[:controller].to_s
482
701
  end
483
702
 
484
- if options[:action]
485
- options[:action] = options[:action].to_s
703
+ if options.key?(:action)
704
+ options[:action] = (options[:action] || "index").to_s
486
705
  end
487
706
  end
488
707
 
@@ -492,18 +711,16 @@ module ActionDispatch
492
711
  # :controller, :action or :id is not found, don't pull any
493
712
  # more keys from the recall.
494
713
  def normalize_controller_action_id!
495
- @recall[:action] ||= 'index' if current_controller
496
-
497
- use_recall_for(:controller) or return
498
- use_recall_for(:action) or return
714
+ use_recall_for(:controller) || return
715
+ use_recall_for(:action) || return
499
716
  use_recall_for(:id)
500
717
  end
501
718
 
502
- # if the current controller is "foo/bar/baz" and :controller => "baz/bat"
719
+ # if the current controller is "foo/bar/baz" and controller: "baz/bat"
503
720
  # is specified, the controller becomes "foo/baz/bat"
504
721
  def use_relative_controller!
505
722
  if !named_route && different_controller? && !controller.start_with?("/")
506
- old_parts = current_controller.split('/')
723
+ old_parts = current_controller.split("/")
507
724
  size = controller.count("/") + 1
508
725
  parts = old_parts[0...-size] << controller
509
726
  @options[:controller] = parts.join("/")
@@ -512,32 +729,19 @@ module ActionDispatch
512
729
 
513
730
  # Remove leading slashes from controllers
514
731
  def normalize_controller!
515
- @options[:controller] = controller.sub(%r{^/}, '') if controller
516
- end
517
-
518
- # This handles the case of :action => nil being explicitly passed.
519
- # It is identical to :action => "index"
520
- def handle_nil_action!
521
- if options.has_key?(:action) && options[:action].nil?
522
- options[:action] = 'index'
732
+ if controller
733
+ if controller.start_with?("/".freeze)
734
+ @options[:controller] = controller[1..-1]
735
+ else
736
+ @options[:controller] = controller
737
+ end
523
738
  end
524
- recall[:action] = options.delete(:action) if options[:action] == 'index'
525
739
  end
526
740
 
741
+ # Generates a path from routes, returns [path, params].
742
+ # If no route is generated the formatter will raise ActionController::UrlGenerationError
527
743
  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}"
744
+ @set.formatter.generate(named_route, options, recall, PARAMETERIZE)
541
745
  end
542
746
 
543
747
  def different_controller?
@@ -557,55 +761,83 @@ module ActionDispatch
557
761
 
558
762
  # Generate the path indicated by the arguments, and return an array of
559
763
  # the keys that were not used to generate it.
560
- def extra_keys(options, recall={})
764
+ def extra_keys(options, recall = {})
561
765
  generate_extras(options, recall).last
562
766
  end
563
767
 
564
- def generate_extras(options, recall={})
565
- generate(options, recall, true)
768
+ def generate_extras(options, recall = {})
769
+ route_key = options.delete :use_route
770
+ path, params = generate(route_key, options, recall)
771
+ return path, params.keys
566
772
  end
567
773
 
568
- def generate(options, recall = {}, extras = false)
569
- Generator.new(options, recall, self, extras).generate
774
+ def generate(route_key, options, recall = {})
775
+ Generator.new(route_key, options, recall, self).generate
570
776
  end
777
+ private :generate
571
778
 
572
779
  RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
573
- :trailing_slash, :anchor, :params, :only_path, :script_name]
780
+ :trailing_slash, :anchor, :params, :only_path, :script_name,
781
+ :original_script_name, :relative_url_root]
574
782
 
575
- def _generate_prefix(options = {})
576
- nil
783
+ def optimize_routes_generation?
784
+ default_url_options.empty?
785
+ end
786
+
787
+ def find_script_name(options)
788
+ options.delete(:script_name) || find_relative_url_root(options) || ""
577
789
  end
578
790
 
579
- def url_for(options)
580
- finalize!
581
- options = (options || {}).reverse_merge!(default_url_options)
791
+ def find_relative_url_root(options)
792
+ options.delete(:relative_url_root) || relative_url_root
793
+ end
794
+
795
+ def path_for(options, route_name = nil)
796
+ url_for(options, route_name, PATH)
797
+ end
798
+
799
+ # The +options+ argument must be a hash whose keys are *symbols*.
800
+ def url_for(options, route_name = nil, url_strategy = UNKNOWN)
801
+ options = default_url_options.merge options
802
+
803
+ user = password = nil
582
804
 
583
- handle_positional_args(options)
805
+ if options[:user] && options[:password]
806
+ user = options.delete :user
807
+ password = options.delete :password
808
+ end
809
+
810
+ recall = options.delete(:_recall) { {} }
811
+
812
+ original_script_name = options.delete(:original_script_name)
813
+ script_name = find_script_name options
584
814
 
585
- user, password = extract_authentication(options)
586
- path_segments = options.delete(:_path_segments)
587
- script_name = options.delete(:script_name)
815
+ if original_script_name
816
+ script_name = original_script_name + script_name
817
+ end
818
+
819
+ path_options = options.dup
820
+ RESERVED_OPTIONS.each { |ro| path_options.delete ro }
588
821
 
589
- path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
822
+ path, params = generate(route_name, path_options, recall)
590
823
 
591
- path_options = options.except(*RESERVED_OPTIONS)
592
- path_options = yield(path_options) if block_given?
824
+ if options.key? :params
825
+ params.merge! options[:params]
826
+ end
593
827
 
594
- path_addition, params = generate(path_options, path_segments || {})
595
- path << path_addition
596
- params.merge!(options[:params] || {})
828
+ options[:path] = path
829
+ options[:script_name] = script_name
830
+ options[:params] = params
831
+ options[:user] = user
832
+ options[:password] = password
597
833
 
598
- ActionDispatch::Http::URL.url_for(options.merge!({
599
- :path => path,
600
- :params => params,
601
- :user => user,
602
- :password => password
603
- }))
834
+ url_strategy.call options
604
835
  end
605
836
 
606
837
  def call(env)
607
- finalize!
608
- @router.call(env)
838
+ req = make_request(env)
839
+ req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
840
+ @router.serve(req)
609
841
  end
610
842
 
611
843
  def recognize_path(path, environment = {})
@@ -614,54 +846,45 @@ module ActionDispatch
614
846
  extras = environment[:extras] || {}
615
847
 
616
848
  begin
617
- env = Rack::MockRequest.env_for(path, {:method => method, :params => extras})
849
+ env = Rack::MockRequest.env_for(path, method: method)
618
850
  rescue URI::InvalidURIError => e
619
851
  raise ActionController::RoutingError, e.message
620
852
  end
621
853
 
622
- req = @request_class.new(env)
623
- @router.recognize(req) do |route, matches, params|
854
+ req = make_request(env)
855
+ recognize_path_with_request(req, path, extras)
856
+ end
857
+
858
+ def recognize_path_with_request(req, path, extras, raise_on_missing: true)
859
+ @router.recognize(req) do |route, params|
860
+ params.merge!(extras)
624
861
  params.each do |key, value|
625
862
  if value.is_a?(String)
626
- value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware?
863
+ value = value.dup.force_encoding(Encoding::BINARY)
627
864
  params[key] = URI.parser.unescape(value)
628
865
  end
629
866
  end
867
+ req.path_parameters = params
868
+ app = route.app
869
+ if app.matches?(req) && app.dispatcher?
870
+ begin
871
+ req.controller_class
872
+ rescue NameError
873
+ raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
874
+ end
630
875
 
631
- dispatcher = route.app
632
- while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
633
- dispatcher = dispatcher.app
634
- end
635
-
636
- if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
637
- dispatcher.prepare_params!(params)
638
- return params
639
- end
640
- end
641
-
642
- raise ActionController::RoutingError, "No route matches #{path.inspect}"
643
- end
644
-
645
- private
646
-
647
- def extract_authentication(options)
648
- if options[:user] && options[:password]
649
- [options.delete(:user), options.delete(:password)]
650
- else
651
- nil
876
+ return req.path_parameters
877
+ elsif app.matches?(req) && app.engine?
878
+ path_parameters = app.rack_app.routes.recognize_path_with_request(req, path, extras, raise_on_missing: false)
879
+ return path_parameters if path_parameters
652
880
  end
653
881
  end
654
882
 
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] }])
883
+ if raise_on_missing
884
+ raise ActionController::RoutingError, "No route matches #{path.inspect}"
663
885
  end
664
-
886
+ end
665
887
  end
888
+ # :startdoc:
666
889
  end
667
890
  end