actionpack 5.2.3

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 (170) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +429 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller.rb +27 -0
  6. data/lib/abstract_controller/asset_paths.rb +12 -0
  7. data/lib/abstract_controller/base.rb +265 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +166 -0
  10. data/lib/abstract_controller/callbacks.rb +212 -0
  11. data/lib/abstract_controller/collector.rb +43 -0
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +194 -0
  14. data/lib/abstract_controller/logger.rb +14 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
  16. data/lib/abstract_controller/rendering.rb +127 -0
  17. data/lib/abstract_controller/translation.rb +31 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +66 -0
  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 +276 -0
  23. data/lib/action_controller/caching.rb +46 -0
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +78 -0
  26. data/lib/action_controller/metal.rb +256 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +274 -0
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +16 -0
  31. data/lib/action_controller/metal/data_streaming.rb +152 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  34. data/lib/action_controller/metal/exceptions.rb +53 -0
  35. data/lib/action_controller/metal/flash.rb +61 -0
  36. data/lib/action_controller/metal/force_ssl.rb +99 -0
  37. data/lib/action_controller/metal/head.rb +60 -0
  38. data/lib/action_controller/metal/helpers.rb +123 -0
  39. data/lib/action_controller/metal/http_authentication.rb +519 -0
  40. data/lib/action_controller/metal/implicit_render.rb +73 -0
  41. data/lib/action_controller/metal/instrumentation.rb +107 -0
  42. data/lib/action_controller/metal/live.rb +312 -0
  43. data/lib/action_controller/metal/mime_responds.rb +313 -0
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +293 -0
  46. data/lib/action_controller/metal/redirecting.rb +133 -0
  47. data/lib/action_controller/metal/renderers.rb +181 -0
  48. data/lib/action_controller/metal/rendering.rb +122 -0
  49. data/lib/action_controller/metal/request_forgery_protection.rb +445 -0
  50. data/lib/action_controller/metal/rescue.rb +28 -0
  51. data/lib/action_controller/metal/streaming.rb +223 -0
  52. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  53. data/lib/action_controller/metal/testing.rb +16 -0
  54. data/lib/action_controller/metal/url_for.rb +58 -0
  55. data/lib/action_controller/railtie.rb +89 -0
  56. data/lib/action_controller/railties/helpers.rb +24 -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 +629 -0
  60. data/lib/action_dispatch.rb +112 -0
  61. data/lib/action_dispatch/http/cache.rb +222 -0
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +84 -0
  64. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  65. data/lib/action_dispatch/http/headers.rb +132 -0
  66. data/lib/action_dispatch/http/mime_negotiation.rb +175 -0
  67. data/lib/action_dispatch/http/mime_type.rb +342 -0
  68. data/lib/action_dispatch/http/mime_types.rb +50 -0
  69. data/lib/action_dispatch/http/parameter_filter.rb +86 -0
  70. data/lib/action_dispatch/http/parameters.rb +126 -0
  71. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  72. data/lib/action_dispatch/http/request.rb +430 -0
  73. data/lib/action_dispatch/http/response.rb +519 -0
  74. data/lib/action_dispatch/http/upload.rb +84 -0
  75. data/lib/action_dispatch/http/url.rb +350 -0
  76. data/lib/action_dispatch/journey.rb +7 -0
  77. data/lib/action_dispatch/journey/formatter.rb +189 -0
  78. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  81. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  82. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  85. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  86. data/lib/action_dispatch/journey/parser.rb +199 -0
  87. data/lib/action_dispatch/journey/parser.y +50 -0
  88. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  89. data/lib/action_dispatch/journey/path/pattern.rb +198 -0
  90. data/lib/action_dispatch/journey/route.rb +203 -0
  91. data/lib/action_dispatch/journey/router.rb +156 -0
  92. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  93. data/lib/action_dispatch/journey/routes.rb +82 -0
  94. data/lib/action_dispatch/journey/scanner.rb +64 -0
  95. data/lib/action_dispatch/journey/visitors.rb +268 -0
  96. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  97. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  98. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  99. data/lib/action_dispatch/middleware/callbacks.rb +36 -0
  100. data/lib/action_dispatch/middleware/cookies.rb +685 -0
  101. data/lib/action_dispatch/middleware/debug_exceptions.rb +205 -0
  102. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  103. data/lib/action_dispatch/middleware/exception_wrapper.rb +147 -0
  104. data/lib/action_dispatch/middleware/executor.rb +21 -0
  105. data/lib/action_dispatch/middleware/flash.rb +300 -0
  106. data/lib/action_dispatch/middleware/public_exceptions.rb +57 -0
  107. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  108. data/lib/action_dispatch/middleware/remote_ip.rb +183 -0
  109. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  110. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  111. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  112. data/lib/action_dispatch/middleware/session/cookie_store.rb +118 -0
  113. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  114. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  115. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  116. data/lib/action_dispatch/middleware/stack.rb +116 -0
  117. data/lib/action_dispatch/middleware/static.rb +130 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +161 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  138. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  139. data/lib/action_dispatch/railtie.rb +55 -0
  140. data/lib/action_dispatch/request/session.rb +234 -0
  141. data/lib/action_dispatch/request/utils.rb +78 -0
  142. data/lib/action_dispatch/routing.rb +260 -0
  143. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  144. data/lib/action_dispatch/routing/inspector.rb +225 -0
  145. data/lib/action_dispatch/routing/mapper.rb +2267 -0
  146. data/lib/action_dispatch/routing/polymorphic_routes.rb +352 -0
  147. data/lib/action_dispatch/routing/redirection.rb +201 -0
  148. data/lib/action_dispatch/routing/route_set.rb +890 -0
  149. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  150. data/lib/action_dispatch/routing/url_for.rb +236 -0
  151. data/lib/action_dispatch/system_test_case.rb +147 -0
  152. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  153. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  154. data/lib/action_dispatch/system_testing/server.rb +31 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  158. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  159. data/lib/action_dispatch/testing/assertions.rb +24 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +107 -0
  161. data/lib/action_dispatch/testing/assertions/routing.rb +222 -0
  162. data/lib/action_dispatch/testing/integration.rb +652 -0
  163. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  164. data/lib/action_dispatch/testing/test_process.rb +50 -0
  165. data/lib/action_dispatch/testing/test_request.rb +71 -0
  166. data/lib/action_dispatch/testing/test_response.rb +53 -0
  167. data/lib/action_pack.rb +26 -0
  168. data/lib/action_pack/gem_version.rb +17 -0
  169. data/lib/action_pack/version.rb +10 -0
  170. metadata +318 -0
@@ -0,0 +1,890 @@
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
+
13
+ module ActionDispatch
14
+ module Routing
15
+ # :stopdoc:
16
+ class RouteSet
17
+ # Since the router holds references to many parts of the system
18
+ # like engines, controllers and the application itself, inspecting
19
+ # the route set can actually be really slow, therefore we default
20
+ # alias inspect to to_s.
21
+ alias inspect to_s
22
+
23
+ class Dispatcher < Routing::Endpoint
24
+ def initialize(raise_on_name_error)
25
+ @raise_on_name_error = raise_on_name_error
26
+ end
27
+
28
+ def dispatcher?; true; end
29
+
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" }, []]
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def controller(req)
46
+ req.controller_class
47
+ rescue NameError => e
48
+ raise ActionController::RoutingError, e.message, e.backtrace
49
+ end
50
+
51
+ def dispatch(controller, action, req, res)
52
+ controller.dispatch(action, req, res)
53
+ end
54
+ end
55
+
56
+ class StaticDispatcher < Dispatcher
57
+ def initialize(controller_class)
58
+ super(false)
59
+ @controller_class = controller_class
60
+ end
61
+
62
+ private
63
+
64
+ def controller(_); @controller_class; end
65
+ end
66
+
67
+ # A NamedRouteCollection instance is a collection of named routes, and also
68
+ # maintains an anonymous module that can be used to install helpers for the
69
+ # named routes.
70
+ class NamedRouteCollection
71
+ include Enumerable
72
+ attr_reader :routes, :url_helpers_module, :path_helpers_module
73
+ private :routes
74
+
75
+ def initialize
76
+ @routes = {}
77
+ @path_helpers = Set.new
78
+ @url_helpers = Set.new
79
+ @url_helpers_module = Module.new
80
+ @path_helpers_module = Module.new
81
+ end
82
+
83
+ def route_defined?(name)
84
+ key = name.to_sym
85
+ @path_helpers.include?(key) || @url_helpers.include?(key)
86
+ end
87
+
88
+ def helper_names
89
+ @path_helpers.map(&:to_s) + @url_helpers.map(&:to_s)
90
+ end
91
+
92
+ def clear!
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
99
+ end
100
+
101
+ @routes.clear
102
+ @path_helpers.clear
103
+ @url_helpers.clear
104
+ end
105
+
106
+ def add(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
121
+ end
122
+
123
+ def get(name)
124
+ routes[name.to_sym]
125
+ end
126
+
127
+ def key?(name)
128
+ return unless name
129
+ routes.key? name.to_sym
130
+ end
131
+
132
+ alias []= add
133
+ alias [] get
134
+ alias clear clear!
135
+
136
+ def each
137
+ routes.each { |name, route| yield name, route }
138
+ self
139
+ end
140
+
141
+ def names
142
+ routes.keys
143
+ end
144
+
145
+ def length
146
+ routes.length
147
+ end
148
+
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
160
+ end
161
+
162
+ @url_helpers_module.module_eval do
163
+ define_method(url_name) do |*args|
164
+ helper.call(self, args, false)
165
+ end
166
+ end
167
+
168
+ @path_helpers << path_name
169
+ @url_helpers << url_name
170
+
171
+ self
172
+ end
173
+
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
181
+ end
182
+
183
+ def self.optimize_helper?(route)
184
+ !route.glob? && route.path.requirements.empty?
185
+ end
186
+
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
196
+ end
197
+
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)
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
+
212
+ url_strategy.call options
213
+ else
214
+ super
215
+ end
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
262
+ end
263
+
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
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
333
+ end
334
+ end
335
+ end
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
+
341
+ attr_accessor :formatter, :set, :named_routes, :default_scope, :router
342
+ attr_accessor :disable_clear_and_finalize, :resources_path_names
343
+ attr_accessor :default_url_options
344
+ attr_reader :env_key, :polymorphic_mappings
345
+
346
+ alias :routes :set
347
+
348
+ def self.default_resources_path_names
349
+ { new: "new", edit: "edit" }
350
+ end
351
+
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)
372
+ self.named_routes = NamedRouteCollection.new
373
+ self.resources_path_names = self.class.default_resources_path_names
374
+ self.default_url_options = {}
375
+
376
+ @config = config
377
+ @append = []
378
+ @prepend = []
379
+ @disable_clear_and_finalize = false
380
+ @finalized = false
381
+ @env_key = "ROUTES_#{object_id}_SCRIPT_NAME".freeze
382
+
383
+ @set = Journey::Routes.new
384
+ @router = Journey::Router.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
405
+ end
406
+
407
+ def make_request(env)
408
+ request_class.new env
409
+ end
410
+ private :make_request
411
+
412
+ def draw(&block)
413
+ clear! unless @disable_clear_and_finalize
414
+ eval_block(block)
415
+ finalize! unless @disable_clear_and_finalize
416
+ nil
417
+ end
418
+
419
+ def append(&block)
420
+ @append << block
421
+ end
422
+
423
+ def prepend(&block)
424
+ @prepend << block
425
+ end
426
+
427
+ def eval_block(block)
428
+ mapper = Mapper.new(self)
429
+ if default_scope
430
+ mapper.with_default_scope(default_scope, &block)
431
+ else
432
+ mapper.instance_exec(&block)
433
+ end
434
+ end
435
+ private :eval_block
436
+
437
+ def finalize!
438
+ return if @finalized
439
+ @append.each { |blk| eval_block(blk) }
440
+ @finalized = true
441
+ end
442
+
443
+ def clear!
444
+ @finalized = false
445
+ named_routes.clear
446
+ set.clear
447
+ formatter.clear
448
+ @polymorphic_mappings.clear
449
+ @prepend.each { |blk| eval_block(blk) }
450
+ end
451
+
452
+ module MountedHelpers
453
+ extend ActiveSupport::Concern
454
+ include UrlFor
455
+ end
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.
461
+ def mounted_helpers
462
+ MountedHelpers
463
+ end
464
+
465
+ def define_mounted_helper(name, script_namer = nil)
466
+ return if MountedHelpers.method_defined?(name)
467
+
468
+ routes = self
469
+ helpers = routes.url_helpers
470
+
471
+ MountedHelpers.class_eval do
472
+ define_method "_#{name}" do
473
+ RoutesProxy.new(routes, _routes_context, helpers, script_namer)
474
+ end
475
+ end
476
+
477
+ MountedHelpers.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
478
+ def #{name}
479
+ @_#{name} ||= _#{name}
480
+ end
481
+ RUBY
482
+ end
483
+
484
+ def url_helpers(supports_path = true)
485
+ routes = self
486
+
487
+ Module.new do
488
+ extend ActiveSupport::Concern
489
+ include UrlFor
490
+
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
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)
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
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
570
+ end
571
+
572
+ private :_generate_paths_by_default
573
+ end
574
+ end
575
+
576
+ def empty?
577
+ routes.empty?
578
+ end
579
+
580
+ def add_route(mapping, name)
581
+ raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
582
+
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
590
+
591
+ route = @set.add_route(name, mapping)
592
+ named_routes[name] = route if name
593
+
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
600
+
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
607
+
608
+ route
609
+ end
610
+
611
+ def add_polymorphic_mapping(klass, options, &block)
612
+ @polymorphic_mappings[klass] = CustomUrlHelper.new(klass, options, &block)
613
+ end
614
+
615
+ def add_url_helper(name, options, &block)
616
+ named_routes.add_url_helper(name, options, &block)
617
+ end
618
+
619
+ class CustomUrlHelper
620
+ attr_reader :name, :defaults, :block
621
+
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))
631
+
632
+ if only_path
633
+ "/" + url.partition(%r{(?<!/)/(?!/)}).last
634
+ else
635
+ url
636
+ end
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
647
+ end
648
+
649
+ class Generator
650
+ PARAMETERIZE = lambda do |name, value|
651
+ if name == :controller
652
+ value
653
+ else
654
+ value.to_param
655
+ end
656
+ end
657
+
658
+ attr_reader :options, :recall, :set, :named_route
659
+
660
+ def initialize(named_route, options, recall, set)
661
+ @named_route = named_route
662
+ @options = options
663
+ @recall = recall
664
+ @set = set
665
+
666
+ normalize_options!
667
+ normalize_controller_action_id!
668
+ use_relative_controller!
669
+ normalize_controller!
670
+ end
671
+
672
+ def controller
673
+ @options[:controller]
674
+ end
675
+
676
+ def current_controller
677
+ @recall[:controller]
678
+ end
679
+
680
+ def use_recall_for(key)
681
+ if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
682
+ if !named_route_exists? || segment_keys.include?(key)
683
+ @options[key] = @recall[key]
684
+ end
685
+ end
686
+ end
687
+
688
+ def normalize_options!
689
+ # If an explicit :controller was given, always make :action explicit
690
+ # too, so that action expiry works as expected for things like
691
+ #
692
+ # generate({controller: 'content'}, {controller: 'content', action: 'show'})
693
+ #
694
+ # (the above is from the unit tests). In the above case, because the
695
+ # controller was explicitly given, but no action, the action is implied to
696
+ # be "index", not the recalled action of "show".
697
+
698
+ if options[:controller]
699
+ options[:action] ||= "index"
700
+ options[:controller] = options[:controller].to_s
701
+ end
702
+
703
+ if options.key?(:action)
704
+ options[:action] = (options[:action] || "index").to_s
705
+ end
706
+ end
707
+
708
+ # This pulls :controller, :action, and :id out of the recall.
709
+ # The recall key is only used if there is no key in the options
710
+ # or if the key in the options is identical. If any of
711
+ # :controller, :action or :id is not found, don't pull any
712
+ # more keys from the recall.
713
+ def normalize_controller_action_id!
714
+ use_recall_for(:controller) || return
715
+ use_recall_for(:action) || return
716
+ use_recall_for(:id)
717
+ end
718
+
719
+ # if the current controller is "foo/bar/baz" and controller: "baz/bat"
720
+ # is specified, the controller becomes "foo/baz/bat"
721
+ def use_relative_controller!
722
+ if !named_route && different_controller? && !controller.start_with?("/")
723
+ old_parts = current_controller.split("/")
724
+ size = controller.count("/") + 1
725
+ parts = old_parts[0...-size] << controller
726
+ @options[:controller] = parts.join("/")
727
+ end
728
+ end
729
+
730
+ # Remove leading slashes from controllers
731
+ def normalize_controller!
732
+ if controller
733
+ if controller.start_with?("/".freeze)
734
+ @options[:controller] = controller[1..-1]
735
+ else
736
+ @options[:controller] = controller
737
+ end
738
+ end
739
+ end
740
+
741
+ # Generates a path from routes, returns [path, params].
742
+ # If no route is generated the formatter will raise ActionController::UrlGenerationError
743
+ def generate
744
+ @set.formatter.generate(named_route, options, recall, PARAMETERIZE)
745
+ end
746
+
747
+ def different_controller?
748
+ return false unless current_controller
749
+ controller.to_param != current_controller.to_param
750
+ end
751
+
752
+ private
753
+ def named_route_exists?
754
+ named_route && set.named_routes[named_route]
755
+ end
756
+
757
+ def segment_keys
758
+ set.named_routes[named_route].segment_keys
759
+ end
760
+ end
761
+
762
+ # Generate the path indicated by the arguments, and return an array of
763
+ # the keys that were not used to generate it.
764
+ def extra_keys(options, recall = {})
765
+ generate_extras(options, recall).last
766
+ end
767
+
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
772
+ end
773
+
774
+ def generate(route_key, options, recall = {})
775
+ Generator.new(route_key, options, recall, self).generate
776
+ end
777
+ private :generate
778
+
779
+ RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
780
+ :trailing_slash, :anchor, :params, :only_path, :script_name,
781
+ :original_script_name, :relative_url_root]
782
+
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) || ""
789
+ end
790
+
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
804
+
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
814
+
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 }
821
+
822
+ path, params = generate(route_name, path_options, recall)
823
+
824
+ if options.key? :params
825
+ params.merge! options[:params]
826
+ end
827
+
828
+ options[:path] = path
829
+ options[:script_name] = script_name
830
+ options[:params] = params
831
+ options[:user] = user
832
+ options[:password] = password
833
+
834
+ url_strategy.call options
835
+ end
836
+
837
+ def call(env)
838
+ req = make_request(env)
839
+ req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
840
+ @router.serve(req)
841
+ end
842
+
843
+ def recognize_path(path, environment = {})
844
+ method = (environment[:method] || "GET").to_s.upcase
845
+ path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://}
846
+ extras = environment[:extras] || {}
847
+
848
+ begin
849
+ env = Rack::MockRequest.env_for(path, method: method)
850
+ rescue URI::InvalidURIError => e
851
+ raise ActionController::RoutingError, e.message
852
+ end
853
+
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)
861
+ params.each do |key, value|
862
+ if value.is_a?(String)
863
+ value = value.dup.force_encoding(Encoding::BINARY)
864
+ params[key] = URI.parser.unescape(value)
865
+ end
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
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
880
+ end
881
+ end
882
+
883
+ if raise_on_missing
884
+ raise ActionController::RoutingError, "No route matches #{path.inspect}"
885
+ end
886
+ end
887
+ end
888
+ # :startdoc:
889
+ end
890
+ end