actionpack 6.0.0

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