actionpack 4.2.8 → 5.2.4.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 (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  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/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,14 +1,14 @@
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/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"
12
12
 
13
13
  module ActionDispatch
14
14
  module Routing
@@ -20,67 +20,48 @@ module ActionDispatch
20
20
  # alias inspect to to_s.
21
21
  alias inspect to_s
22
22
 
23
- mattr_accessor :relative_url_root
24
-
25
23
  class Dispatcher < Routing::Endpoint
26
- def initialize(defaults)
27
- @defaults = defaults
24
+ def initialize(raise_on_name_error)
25
+ @raise_on_name_error = raise_on_name_error
28
26
  end
29
27
 
30
28
  def dispatcher?; true; end
31
29
 
32
30
  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'}, []]
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" }, []]
41
40
  end
42
-
43
- dispatch(controller, params[:action], req.env)
44
41
  end
45
42
 
46
- def prepare_params!(params)
47
- normalize_controller!(params)
48
- merge_default_action!(params)
49
- end
43
+ private
50
44
 
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)
61
- end
45
+ def controller(req)
46
+ req.controller_class
62
47
  rescue NameError => e
63
- raise ActionController::RoutingError, e.message, e.backtrace if default_controller
48
+ raise ActionController::RoutingError, e.message, e.backtrace
64
49
  end
65
50
 
66
- private
67
-
68
- def controller_reference(controller_param)
69
- const_name = "#{controller_param.camelize}Controller"
70
- ActiveSupport::Dependencies.constantize(const_name)
51
+ def dispatch(controller, action, req, res)
52
+ controller.dispatch(action, req, res)
71
53
  end
54
+ end
72
55
 
73
- def dispatch(controller, action, env)
74
- controller.action(action).call(env)
56
+ class StaticDispatcher < Dispatcher
57
+ def initialize(controller_class)
58
+ super(false)
59
+ @controller_class = controller_class
75
60
  end
76
61
 
77
- def normalize_controller!(params)
78
- params[:controller] = params[:controller].underscore if params.key?(:controller)
79
- end
62
+ private
80
63
 
81
- def merge_default_action!(params)
82
- params[:action] ||= 'index'
83
- end
64
+ def controller(_); @controller_class; end
84
65
  end
85
66
 
86
67
  # A NamedRouteCollection instance is a collection of named routes, and also
@@ -88,10 +69,11 @@ module ActionDispatch
88
69
  # named routes.
89
70
  class NamedRouteCollection
90
71
  include Enumerable
91
- attr_reader :routes, :url_helpers_module
72
+ attr_reader :routes, :url_helpers_module, :path_helpers_module
73
+ private :routes
92
74
 
93
75
  def initialize
94
- @routes = {}
76
+ @routes = {}
95
77
  @path_helpers = Set.new
96
78
  @url_helpers = Set.new
97
79
  @url_helpers_module = Module.new
@@ -103,25 +85,17 @@ module ActionDispatch
103
85
  @path_helpers.include?(key) || @url_helpers.include?(key)
104
86
  end
105
87
 
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
88
  def helper_names
115
89
  @path_helpers.map(&:to_s) + @url_helpers.map(&:to_s)
116
90
  end
117
91
 
118
92
  def clear!
119
93
  @path_helpers.each do |helper|
120
- @path_helpers_module.send :undef_method, helper
94
+ @path_helpers_module.send :remove_method, helper
121
95
  end
122
96
 
123
97
  @url_helpers.each do |helper|
124
- @url_helpers_module.send :undef_method, helper
98
+ @url_helpers_module.send :remove_method, helper
125
99
  end
126
100
 
127
101
  @routes.clear
@@ -139,7 +113,7 @@ module ActionDispatch
139
113
  @url_helpers_module.send :undef_method, url_name
140
114
  end
141
115
  routes[key] = route
142
- define_url_helper @path_helpers_module, route, path_name, route.defaults, name, LEGACY
116
+ define_url_helper @path_helpers_module, route, path_name, route.defaults, name, PATH
143
117
  define_url_helper @url_helpers_module, route, url_name, route.defaults, name, UNKNOWN
144
118
 
145
119
  @path_helpers << path_name
@@ -151,6 +125,7 @@ module ActionDispatch
151
125
  end
152
126
 
153
127
  def key?(name)
128
+ return unless name
154
129
  routes.key? name.to_sym
155
130
  end
156
131
 
@@ -171,23 +146,29 @@ module ActionDispatch
171
146
  routes.length
172
147
  end
173
148
 
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
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"
180
155
 
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
156
+ @path_helpers_module.module_eval do
157
+ define_method(path_name) do |*args|
158
+ helper.call(self, args, true)
159
+ end
160
+ end
161
+
162
+ @url_helpers_module.module_eval do
163
+ define_method(url_name) do |*args|
164
+ helper.call(self, args, false)
187
165
  end
188
- else
189
- @path_helpers_module
190
166
  end
167
+
168
+ @path_helpers << path_name
169
+ @url_helpers << url_name
170
+
171
+ self
191
172
  end
192
173
 
193
174
  class UrlHelper
@@ -218,6 +199,16 @@ module ActionDispatch
218
199
  if args.size == arg_size && !inner_options && optimize_routes_generation?(t)
219
200
  options = t.url_options.merge @options
220
201
  options[:path] = optimized_helper(args)
202
+
203
+ original_script_name = options.delete(:original_script_name)
204
+ script_name = t._routes.find_script_name(options)
205
+
206
+ if original_script_name
207
+ script_name = original_script_name + script_name
208
+ end
209
+
210
+ options[:script_name] = script_name
211
+
221
212
  url_strategy.call options
222
213
  else
223
214
  super
@@ -226,38 +217,40 @@ module ActionDispatch
226
217
 
227
218
  private
228
219
 
229
- def optimized_helper(args)
230
- params = parameterize_args(args)
231
- missing_keys = missing_keys(params)
220
+ def optimized_helper(args)
221
+ params = parameterize_args(args) do
222
+ raise_generation_error(args)
223
+ end
232
224
 
233
- unless missing_keys.empty?
234
- raise_generation_error(params, missing_keys)
225
+ @route.format params
235
226
  end
236
227
 
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
228
+ def optimize_routes_generation?(t)
229
+ t.send(:optimize_routes_generation?)
230
+ end
249
231
 
250
- def missing_keys(args)
251
- args.select{ |part, arg| arg.nil? || arg.empty? }.keys
252
- end
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
253
242
 
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}"
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}"
258
251
 
259
- raise ActionController::UrlGenerationError, message
260
- end
252
+ raise ActionController::UrlGenerationError, message
253
+ end
261
254
  end
262
255
 
263
256
  def initialize(route, options, route_name, url_strategy)
@@ -272,7 +265,7 @@ module ActionDispatch
272
265
  controller_options = t.url_options
273
266
  options = controller_options.merge @options
274
267
  hash = handle_positional_args(controller_options,
275
- deprecate_string_options(inner_options) || {},
268
+ inner_options || {},
276
269
  args,
277
270
  options,
278
271
  @segment_keys)
@@ -292,119 +285,130 @@ module ActionDispatch
292
285
  if args.size < path_params_size
293
286
  path_params -= controller_options.keys
294
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)
295
293
  end
296
- path_params.each { |param|
297
- value = inner_options.fetch(param) { args.shift }
298
294
 
299
- unless param == :format && value.nil?
300
- result[param] = value
301
- end
302
- }
295
+ args.each_with_index do |arg, index|
296
+ param = path_params[index]
297
+ result[param] = arg if param
298
+ end
303
299
  end
304
300
 
305
301
  result.merge!(inner_options)
306
302
  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
303
  end
324
304
 
325
305
  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
306
+ # Create a URL helper allowing ordered parameters to be associated
307
+ # with corresponding dynamic segments, so you can do:
308
+ #
309
+ # foo_url(bar, baz, bang)
310
+ #
311
+ # Instead of:
312
+ #
313
+ # foo_url(bar: bar, baz: baz, bang: bang)
314
+ #
315
+ # Also allow options hash, so you can do:
316
+ #
317
+ # foo_url(bar, baz, bang, sort_by: 'baz')
318
+ #
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
332
+ end
346
333
  end
347
334
  end
348
- end
349
335
  end
350
336
 
351
337
  # strategy for building urls to send to the client
352
338
  PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
353
- FULL = ->(options) { ActionDispatch::Http::URL.full_url_for(options) }
354
339
  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
340
 
382
341
  attr_accessor :formatter, :set, :named_routes, :default_scope, :router
383
342
  attr_accessor :disable_clear_and_finalize, :resources_path_names
384
- attr_accessor :default_url_options, :request_class
343
+ attr_accessor :default_url_options
344
+ attr_reader :env_key, :polymorphic_mappings
385
345
 
386
346
  alias :routes :set
387
347
 
388
348
  def self.default_resources_path_names
389
- { :new => 'new', :edit => 'edit' }
349
+ { new: "new", edit: "edit" }
390
350
  end
391
351
 
392
- def initialize(request_class = ActionDispatch::Request)
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
363
+
364
+ new route_set_config
365
+ end
366
+
367
+ Config = Struct.new :relative_url_root, :api_only
368
+
369
+ DEFAULT_CONFIG = Config.new(nil, false)
370
+
371
+ def initialize(config = DEFAULT_CONFIG)
393
372
  self.named_routes = NamedRouteCollection.new
394
373
  self.resources_path_names = self.class.default_resources_path_names
395
374
  self.default_url_options = {}
396
- self.request_class = request_class
397
375
 
376
+ @config = config
398
377
  @append = []
399
378
  @prepend = []
400
379
  @disable_clear_and_finalize = false
401
380
  @finalized = false
381
+ @env_key = "ROUTES_#{object_id}_SCRIPT_NAME".freeze
402
382
 
403
383
  @set = Journey::Routes.new
404
384
  @router = Journey::Router.new @set
405
- @formatter = Journey::Formatter.new @set
385
+ @formatter = Journey::Formatter.new self
386
+ @polymorphic_mappings = {}
387
+ end
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
406
405
  end
407
406
 
407
+ def make_request(env)
408
+ request_class.new env
409
+ end
410
+ private :make_request
411
+
408
412
  def draw(&block)
409
413
  clear! unless @disable_clear_and_finalize
410
414
  eval_block(block)
@@ -421,10 +425,6 @@ module ActionDispatch
421
425
  end
422
426
 
423
427
  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
428
  mapper = Mapper.new(self)
429
429
  if default_scope
430
430
  mapper.with_default_scope(default_scope, &block)
@@ -445,13 +445,10 @@ module ActionDispatch
445
445
  named_routes.clear
446
446
  set.clear
447
447
  formatter.clear
448
+ @polymorphic_mappings.clear
448
449
  @prepend.each { |blk| eval_block(blk) }
449
450
  end
450
451
 
451
- def dispatcher(defaults)
452
- Routing::RouteSet::Dispatcher.new(defaults)
453
- end
454
-
455
452
  module MountedHelpers
456
453
  extend ActiveSupport::Concern
457
454
  include UrlFor
@@ -465,7 +462,7 @@ module ActionDispatch
465
462
  MountedHelpers
466
463
  end
467
464
 
468
- def define_mounted_helper(name)
465
+ def define_mounted_helper(name, script_namer = nil)
469
466
  return if MountedHelpers.method_defined?(name)
470
467
 
471
468
  routes = self
@@ -473,7 +470,7 @@ module ActionDispatch
473
470
 
474
471
  MountedHelpers.class_eval do
475
472
  define_method "_#{name}" do
476
- RoutesProxy.new(routes, _routes_context, helpers)
473
+ RoutesProxy.new(routes, _routes_context, helpers, script_namer)
477
474
  end
478
475
  end
479
476
 
@@ -493,10 +490,50 @@ module ActionDispatch
493
490
 
494
491
  # Define url_for in the singleton level so one can do:
495
492
  # Rails.application.routes.url_helpers.url_for(args)
496
- @_routes = routes
497
- class << self
498
- delegate :url_for, :optimize_routes_generation?, to: '@_routes'
493
+ proxy_class = Class.new do
494
+ include UrlFor
495
+ include routes.named_routes.path_helpers_module
496
+ include routes.named_routes.url_helpers_module
497
+
499
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)
510
+
511
+ class << self
512
+ def url_for(options)
513
+ @_proxy.url_for(options)
514
+ end
515
+
516
+ def full_url_for(options)
517
+ @_proxy.full_url_for(options)
518
+ end
519
+
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
500
537
  def url_options; {}; end
501
538
  end
502
539
 
@@ -513,16 +550,14 @@ module ActionDispatch
513
550
 
514
551
  if supports_path
515
552
  path_helpers = routes.named_routes.path_helpers_module
516
- else
517
- path_helpers = routes.named_routes.path_helpers_module(true)
518
- end
519
553
 
520
- include path_helpers
521
- extend path_helpers
554
+ include path_helpers
555
+ extend path_helpers
556
+ end
522
557
 
523
558
  # plus a singleton class method called _routes ...
524
559
  included do
525
- singleton_class.send(:redefine_method, :_routes) { routes }
560
+ redefine_singleton_method(:_routes) { routes }
526
561
  end
527
562
 
528
563
  # And an instance method _routes. Note that
@@ -542,7 +577,7 @@ module ActionDispatch
542
577
  routes.empty?
543
578
  end
544
579
 
545
- def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
580
+ def add_route(mapping, name)
546
581
  raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
547
582
 
548
583
  if name && named_routes[name]
@@ -553,74 +588,70 @@ module ActionDispatch
553
588
  "http://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
554
589
  end
555
590
 
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)
591
+ route = @set.add_route(name, mapping)
562
592
  named_routes[name] = route if name
563
- route
564
- end
565
593
 
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
-
576
- 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
577
600
 
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
- }
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
583
607
 
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?)
608
+ route
609
+ end
588
610
 
589
- symbols.each { |x|
590
- x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
591
- }
611
+ def add_polymorphic_mapping(klass, options, &block)
612
+ @polymorphic_mappings[klass] = CustomUrlHelper.new(klass, options, &block)
613
+ end
592
614
 
593
- pattern
615
+ def add_url_helper(name, options, &block)
616
+ named_routes.add_url_helper(name, options, &block)
594
617
  end
595
- private :build_path
596
618
 
597
- def build_conditions(current_conditions, path_values)
598
- conditions = current_conditions.dup
619
+ class CustomUrlHelper
620
+ attr_reader :name, :defaults, :block
599
621
 
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('|')}$]
622
+ def initialize(name, defaults, &block)
623
+ @name = name
624
+ @defaults = defaults
625
+ @block = block
607
626
  end
608
627
 
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)
628
+ def call(t, args, only_path = false)
629
+ options = args.extract_options!
630
+ url = t.full_url_for(eval_block(t, args, options))
631
+
632
+ if only_path
633
+ "/" + url.partition(%r{(?<!/)/(?!/)}).last
634
+ else
635
+ url
636
+ end
612
637
  end
638
+
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
613
647
  end
614
- private :build_conditions
615
648
 
616
649
  class Generator
617
650
  PARAMETERIZE = lambda do |name, value|
618
651
  if name == :controller
619
652
  value
620
- elsif value.is_a?(Array)
621
- value.map { |v| v.to_param }.join('/')
622
- elsif param = value.to_param
623
- param
653
+ else
654
+ value.to_param
624
655
  end
625
656
  end
626
657
 
@@ -628,16 +659,14 @@ module ActionDispatch
628
659
 
629
660
  def initialize(named_route, options, recall, set)
630
661
  @named_route = named_route
631
- @options = options.dup
632
- @recall = recall.dup
662
+ @options = options
663
+ @recall = recall
633
664
  @set = set
634
665
 
635
- normalize_recall!
636
666
  normalize_options!
637
667
  normalize_controller_action_id!
638
668
  use_relative_controller!
639
669
  normalize_controller!
640
- normalize_action!
641
670
  end
642
671
 
643
672
  def controller
@@ -651,16 +680,11 @@ module ActionDispatch
651
680
  def use_recall_for(key)
652
681
  if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
653
682
  if !named_route_exists? || segment_keys.include?(key)
654
- @options[key] = @recall.delete(key)
683
+ @options[key] = @recall[key]
655
684
  end
656
685
  end
657
686
  end
658
687
 
659
- # Set 'index' as default action for recall
660
- def normalize_recall!
661
- @recall[:action] ||= 'index'
662
- end
663
-
664
688
  def normalize_options!
665
689
  # If an explicit :controller was given, always make :action explicit
666
690
  # too, so that action expiry works as expected for things like
@@ -672,12 +696,12 @@ module ActionDispatch
672
696
  # be "index", not the recalled action of "show".
673
697
 
674
698
  if options[:controller]
675
- options[:action] ||= 'index'
699
+ options[:action] ||= "index"
676
700
  options[:controller] = options[:controller].to_s
677
701
  end
678
702
 
679
703
  if options.key?(:action)
680
- options[:action] = (options[:action] || 'index').to_s
704
+ options[:action] = (options[:action] || "index").to_s
681
705
  end
682
706
  end
683
707
 
@@ -687,8 +711,8 @@ module ActionDispatch
687
711
  # :controller, :action or :id is not found, don't pull any
688
712
  # more keys from the recall.
689
713
  def normalize_controller_action_id!
690
- use_recall_for(:controller) or return
691
- use_recall_for(:action) or return
714
+ use_recall_for(:controller) || return
715
+ use_recall_for(:action) || return
692
716
  use_recall_for(:id)
693
717
  end
694
718
 
@@ -696,7 +720,7 @@ module ActionDispatch
696
720
  # is specified, the controller becomes "foo/baz/bat"
697
721
  def use_relative_controller!
698
722
  if !named_route && different_controller? && !controller.start_with?("/")
699
- old_parts = current_controller.split('/')
723
+ old_parts = current_controller.split("/")
700
724
  size = controller.count("/") + 1
701
725
  parts = old_parts[0...-size] << controller
702
726
  @options[:controller] = parts.join("/")
@@ -705,13 +729,12 @@ module ActionDispatch
705
729
 
706
730
  # Remove leading slashes from controllers
707
731
  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)
732
+ if controller
733
+ if controller.start_with?("/".freeze)
734
+ @options[:controller] = controller[1..-1]
735
+ else
736
+ @options[:controller] = controller
737
+ end
715
738
  end
716
739
  end
717
740
 
@@ -738,11 +761,11 @@ module ActionDispatch
738
761
 
739
762
  # Generate the path indicated by the arguments, and return an array of
740
763
  # the keys that were not used to generate it.
741
- def extra_keys(options, recall={})
764
+ def extra_keys(options, recall = {})
742
765
  generate_extras(options, recall).last
743
766
  end
744
767
 
745
- def generate_extras(options, recall={})
768
+ def generate_extras(options, recall = {})
746
769
  route_key = options.delete :use_route
747
770
  path, params = generate(route_key, options, recall)
748
771
  return path, params.keys
@@ -762,7 +785,7 @@ 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)
@@ -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
@@ -812,7 +835,7 @@ module ActionDispatch
812
835
  end
813
836
 
814
837
  def call(env)
815
- req = request_class.new(env)
838
+ req = make_request(env)
816
839
  req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
817
840
  @router.serve(req)
818
841
  end
@@ -823,12 +846,16 @@ module ActionDispatch
823
846
  extras = environment[:extras] || {}
824
847
 
825
848
  begin
826
- env = Rack::MockRequest.env_for(path, {:method => method})
849
+ env = Rack::MockRequest.env_for(path, method: method)
827
850
  rescue URI::InvalidURIError => e
828
851
  raise ActionController::RoutingError, e.message
829
852
  end
830
853
 
831
- req = request_class.new(env)
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)
832
859
  @router.recognize(req) do |route, params|
833
860
  params.merge!(extras)
834
861
  params.each do |key, value|
@@ -837,22 +864,25 @@ module ActionDispatch
837
864
  params[key] = URI.parser.unescape(value)
838
865
  end
839
866
  end
840
- old_params = req.path_parameters
841
- req.path_parameters = old_params.merge params
867
+ req.path_parameters = params
842
868
  app = route.app
843
869
  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
870
+ begin
871
+ req.controller_class
872
+ rescue NameError
850
873
  raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
851
874
  end
875
+
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
852
880
  end
853
881
  end
854
882
 
855
- raise ActionController::RoutingError, "No route matches #{path.inspect}"
883
+ if raise_on_missing
884
+ raise ActionController::RoutingError, "No route matches #{path.inspect}"
885
+ end
856
886
  end
857
887
  end
858
888
  # :startdoc: