omg-actionpack 8.0.0.alpha1

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