actionpack 4.2.11.1 → 6.1.3.2

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