actionpack 7.0.7 → 7.1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +367 -331
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/abstract_controller/base.rb +20 -11
  6. data/lib/abstract_controller/caching/fragments.rb +2 -0
  7. data/lib/abstract_controller/callbacks.rb +31 -6
  8. data/lib/abstract_controller/deprecator.rb +7 -0
  9. data/lib/abstract_controller/helpers.rb +61 -18
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
  11. data/lib/abstract_controller/rendering.rb +3 -3
  12. data/lib/abstract_controller/translation.rb +7 -4
  13. data/lib/abstract_controller/url_for.rb +2 -0
  14. data/lib/abstract_controller.rb +6 -0
  15. data/lib/action_controller/api.rb +5 -3
  16. data/lib/action_controller/base.rb +3 -17
  17. data/lib/action_controller/caching.rb +2 -0
  18. data/lib/action_controller/deprecator.rb +7 -0
  19. data/lib/action_controller/form_builder.rb +2 -0
  20. data/lib/action_controller/log_subscriber.rb +16 -4
  21. data/lib/action_controller/metal/content_security_policy.rb +1 -1
  22. data/lib/action_controller/metal/data_streaming.rb +2 -0
  23. data/lib/action_controller/metal/default_headers.rb +2 -0
  24. data/lib/action_controller/metal/etag_with_flash.rb +2 -0
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  26. data/lib/action_controller/metal/exceptions.rb +8 -0
  27. data/lib/action_controller/metal/head.rb +8 -6
  28. data/lib/action_controller/metal/helpers.rb +3 -14
  29. data/lib/action_controller/metal/http_authentication.rb +17 -8
  30. data/lib/action_controller/metal/implicit_render.rb +5 -3
  31. data/lib/action_controller/metal/instrumentation.rb +8 -1
  32. data/lib/action_controller/metal/live.rb +24 -0
  33. data/lib/action_controller/metal/mime_responds.rb +2 -2
  34. data/lib/action_controller/metal/params_wrapper.rb +4 -2
  35. data/lib/action_controller/metal/permissions_policy.rb +1 -1
  36. data/lib/action_controller/metal/redirecting.rb +7 -7
  37. data/lib/action_controller/metal/renderers.rb +2 -2
  38. data/lib/action_controller/metal/rendering.rb +0 -7
  39. data/lib/action_controller/metal/request_forgery_protection.rb +139 -50
  40. data/lib/action_controller/metal/rescue.rb +2 -0
  41. data/lib/action_controller/metal/streaming.rb +70 -30
  42. data/lib/action_controller/metal/strong_parameters.rb +132 -52
  43. data/lib/action_controller/metal/url_for.rb +7 -0
  44. data/lib/action_controller/metal.rb +79 -21
  45. data/lib/action_controller/railtie.rb +22 -9
  46. data/lib/action_controller/renderer.rb +98 -65
  47. data/lib/action_controller/test_case.rb +15 -5
  48. data/lib/action_controller.rb +8 -1
  49. data/lib/action_dispatch/constants.rb +32 -0
  50. data/lib/action_dispatch/deprecator.rb +7 -0
  51. data/lib/action_dispatch/http/cache.rb +1 -3
  52. data/lib/action_dispatch/http/content_security_policy.rb +9 -8
  53. data/lib/action_dispatch/http/filter_parameters.rb +11 -5
  54. data/lib/action_dispatch/http/headers.rb +2 -0
  55. data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
  56. data/lib/action_dispatch/http/mime_type.rb +35 -12
  57. data/lib/action_dispatch/http/mime_types.rb +3 -1
  58. data/lib/action_dispatch/http/parameters.rb +1 -1
  59. data/lib/action_dispatch/http/permissions_policy.rb +38 -23
  60. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  61. data/lib/action_dispatch/http/request.rb +48 -14
  62. data/lib/action_dispatch/http/response.rb +80 -59
  63. data/lib/action_dispatch/http/upload.rb +2 -0
  64. data/lib/action_dispatch/journey/formatter.rb +8 -2
  65. data/lib/action_dispatch/journey/path/pattern.rb +14 -14
  66. data/lib/action_dispatch/journey/route.rb +3 -2
  67. data/lib/action_dispatch/journey/router.rb +9 -8
  68. data/lib/action_dispatch/journey/routes.rb +2 -2
  69. data/lib/action_dispatch/log_subscriber.rb +23 -0
  70. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
  71. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  72. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  73. data/lib/action_dispatch/middleware/cookies.rb +81 -98
  74. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
  75. data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
  76. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  77. data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
  78. data/lib/action_dispatch/middleware/executor.rb +1 -1
  79. data/lib/action_dispatch/middleware/flash.rb +7 -0
  80. data/lib/action_dispatch/middleware/host_authorization.rb +18 -8
  81. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  82. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  83. data/lib/action_dispatch/middleware/remote_ip.rb +17 -16
  84. data/lib/action_dispatch/middleware/request_id.rb +2 -0
  85. data/lib/action_dispatch/middleware/server_timing.rb +4 -4
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
  89. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  90. data/lib/action_dispatch/middleware/show_exceptions.rb +19 -15
  91. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  92. data/lib/action_dispatch/middleware/stack.rb +7 -2
  93. data/lib/action_dispatch/middleware/static.rb +12 -8
  94. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  95. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
  96. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  97. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -3
  98. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -3
  99. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
  100. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  101. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
  102. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
  103. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
  104. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  105. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
  106. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  107. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  108. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  109. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +46 -37
  110. data/lib/action_dispatch/railtie.rb +14 -4
  111. data/lib/action_dispatch/request/session.rb +16 -6
  112. data/lib/action_dispatch/request/utils.rb +8 -3
  113. data/lib/action_dispatch/routing/inspector.rb +54 -6
  114. data/lib/action_dispatch/routing/mapper.rb +35 -24
  115. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  116. data/lib/action_dispatch/routing/redirection.rb +15 -6
  117. data/lib/action_dispatch/routing/route_set.rb +52 -22
  118. data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
  119. data/lib/action_dispatch/routing/url_for.rb +5 -1
  120. data/lib/action_dispatch/routing.rb +7 -7
  121. data/lib/action_dispatch/system_test_case.rb +3 -3
  122. data/lib/action_dispatch/system_testing/browser.rb +20 -19
  123. data/lib/action_dispatch/system_testing/driver.rb +13 -21
  124. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
  125. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  126. data/lib/action_dispatch/testing/assertions/response.rb +13 -6
  127. data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
  128. data/lib/action_dispatch/testing/assertions.rb +3 -1
  129. data/lib/action_dispatch/testing/integration.rb +27 -17
  130. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  131. data/lib/action_dispatch/testing/test_process.rb +4 -3
  132. data/lib/action_dispatch/testing/test_request.rb +1 -1
  133. data/lib/action_dispatch/testing/test_response.rb +23 -9
  134. data/lib/action_dispatch.rb +37 -4
  135. data/lib/action_pack/gem_version.rb +4 -4
  136. data/lib/action_pack/version.rb +1 -1
  137. data/lib/action_pack.rb +1 -1
  138. metadata +64 -28
@@ -12,6 +12,9 @@ module ActionDispatch
12
12
  class Mapper
13
13
  URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
14
14
 
15
+ cattr_accessor :route_source_locations, instance_accessor: false, default: false
16
+ cattr_accessor :backtrace_cleaner, instance_accessor: false, default: ActiveSupport::BacktraceCleaner.new
17
+
15
18
  class Constraints < Routing::Endpoint # :nodoc:
16
19
  attr_reader :app, :constraints
17
20
 
@@ -43,7 +46,7 @@ module ActionDispatch
43
46
  end
44
47
 
45
48
  def serve(req)
46
- return [ 404, { "X-Cascade" => "pass" }, [] ] unless matches?(req)
49
+ return [ 404, { Constants::X_CASCADE => "pass" }, [] ] unless matches?(req)
47
50
 
48
51
  @strategy.call @app, req
49
52
  end
@@ -170,7 +173,7 @@ module ActionDispatch
170
173
  Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
171
174
  required_defaults: required_defaults, defaults: defaults,
172
175
  request_method_match: request_method, precedence: precedence,
173
- scope_options: scope_options, internal: @internal)
176
+ scope_options: scope_options, internal: @internal, source_location: route_source_location)
174
177
  end
175
178
 
176
179
  def application
@@ -214,9 +217,16 @@ module ActionDispatch
214
217
  if to.respond_to?(:action) || to.respond_to?(:call)
215
218
  options
216
219
  else
217
- to_endpoint = split_to to
218
- controller = to_endpoint[0] || default_controller
219
- action = to_endpoint[1] || default_action
220
+ if to.nil?
221
+ controller = default_controller
222
+ action = default_action
223
+ elsif to.is_a?(String) && to.include?("#")
224
+ to_endpoint = to.split("#").map!(&:-@)
225
+ controller = to_endpoint[0]
226
+ action = to_endpoint[1]
227
+ else
228
+ raise ArgumentError, ":to must respond to `action` or `call`, or it must be a String that includes '#'"
229
+ end
220
230
 
221
231
  controller = add_controller_module(controller, modyoule)
222
232
 
@@ -305,14 +315,6 @@ module ActionDispatch
305
315
  hash
306
316
  end
307
317
 
308
- def split_to(to)
309
- if /#/.match?(to)
310
- to.split("#").map!(&:-@)
311
- else
312
- []
313
- end
314
- end
315
-
316
318
  def add_controller_module(controller, modyoule)
317
319
  if modyoule && !controller.is_a?(Regexp)
318
320
  if controller&.start_with?("/")
@@ -356,6 +358,15 @@ module ActionDispatch
356
358
  def dispatcher(raise_on_name_error)
357
359
  Routing::RouteSet::Dispatcher.new raise_on_name_error
358
360
  end
361
+
362
+ def route_source_location
363
+ if Mapper.route_source_locations
364
+ action_dispatch_dir = File.expand_path("..", __dir__)
365
+ caller_location = caller_locations.find { |location| !location.path.include?(action_dispatch_dir) }
366
+ cleaned_path = Mapper.backtrace_cleaner.clean([caller_location.path]).first
367
+ "#{cleaned_path}:#{caller_location.lineno}" if cleaned_path
368
+ end
369
+ end
359
370
  end
360
371
 
361
372
  # Invokes Journey::Router::Utils.normalize_path, then ensures that
@@ -652,7 +663,7 @@ module ActionDispatch
652
663
 
653
664
  script_namer = ->(options) do
654
665
  prefix_options = options.slice(*_route.segment_keys)
655
- prefix_options[:relative_url_root] = ""
666
+ prefix_options[:script_name] = "" if options[:original_script_name]
656
667
 
657
668
  if options[:_recall]
658
669
  prefix_options.reverse_merge!(options[:_recall].slice(*_route.segment_keys))
@@ -748,7 +759,7 @@ module ActionDispatch
748
759
  # end
749
760
  #
750
761
  # This will create a number of routes for each of the posts and comments
751
- # controller. For <tt>Admin::PostsController</tt>, Rails will create:
762
+ # controller. For +Admin::PostsController+, \Rails will create:
752
763
  #
753
764
  # GET /admin/posts
754
765
  # GET /admin/posts/new
@@ -759,7 +770,7 @@ module ActionDispatch
759
770
  # DELETE /admin/posts/1
760
771
  #
761
772
  # If you want to route /posts (without the prefix /admin) to
762
- # <tt>Admin::PostsController</tt>, you could use
773
+ # +Admin::PostsController+, you could use
763
774
  #
764
775
  # scope module: "admin" do
765
776
  # resources :posts
@@ -808,7 +819,7 @@ module ActionDispatch
808
819
  #
809
820
  # Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
810
821
  #
811
- # # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt>
822
+ # # route /posts (without the prefix /admin) to +Admin::PostsController+
812
823
  # scope module: "admin" do
813
824
  # resources :posts
814
825
  # end
@@ -917,7 +928,7 @@ module ActionDispatch
917
928
  # resources :posts
918
929
  # end
919
930
  #
920
- # # maps to <tt>Sekret::PostsController</tt> rather than <tt>Admin::PostsController</tt>
931
+ # # maps to +Sekret::PostsController+ rather than +Admin::PostsController+
921
932
  # namespace :admin, module: "sekret" do
922
933
  # resources :posts
923
934
  # end
@@ -1318,7 +1329,7 @@ module ActionDispatch
1318
1329
  self
1319
1330
  end
1320
1331
 
1321
- # In Rails, a resourceful route provides a mapping between HTTP verbs
1332
+ # In \Rails, a resourceful route provides a mapping between HTTP verbs
1322
1333
  # and URLs and controller actions. By convention, each action also maps
1323
1334
  # to particular CRUD operations in a database. A single entry in the
1324
1335
  # routing file, such as
@@ -1450,7 +1461,7 @@ module ActionDispatch
1450
1461
  #
1451
1462
  # === Examples
1452
1463
  #
1453
- # # routes call <tt>Admin::PostsController</tt>
1464
+ # # routes call +Admin::PostsController+
1454
1465
  # resources :posts, module: "admin"
1455
1466
  #
1456
1467
  # # resource actions are at /admin/posts.
@@ -1493,7 +1504,7 @@ module ActionDispatch
1493
1504
  # end
1494
1505
  # end
1495
1506
  #
1496
- # This will enable Rails to recognize paths such as <tt>/photos/search</tt>
1507
+ # This will enable \Rails to recognize paths such as <tt>/photos/search</tt>
1497
1508
  # with GET, and route to the search action of +PhotosController+. It will also
1498
1509
  # create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
1499
1510
  # route helpers.
@@ -1640,7 +1651,7 @@ module ActionDispatch
1640
1651
  when Symbol
1641
1652
  options[:action] = to
1642
1653
  when String
1643
- if /#/.match?(to)
1654
+ if to.include?("#")
1644
1655
  options[:to] = to
1645
1656
  else
1646
1657
  options[:controller] = to
@@ -1663,7 +1674,7 @@ module ActionDispatch
1663
1674
  end
1664
1675
  end
1665
1676
 
1666
- # You can specify what Rails should route "/" to with the root method:
1677
+ # You can specify what \Rails should route "/" to with the root method:
1667
1678
  #
1668
1679
  # root to: 'pages#main'
1669
1680
  #
@@ -1675,7 +1686,7 @@ module ActionDispatch
1675
1686
  #
1676
1687
  # You should put the root route at the top of <tt>config/routes.rb</tt>,
1677
1688
  # because this means it will be matched first. As this is the most popular route
1678
- # of most Rails applications, this is beneficial.
1689
+ # of most \Rails applications, this is beneficial.
1679
1690
  def root(path, options = {})
1680
1691
  if path.is_a?(String)
1681
1692
  options[:to] = path
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ActionDispatch
4
4
  module Routing
5
+ # = Action Dispatch Routing \PolymorphicRoutes
6
+ #
5
7
  # Polymorphic URL helpers are methods for smart resolution to a named route call when
6
8
  # given an Active Record model instance. They are to be used in combination with
7
9
  # ActionController::Resources.
@@ -18,10 +18,19 @@ module ActionDispatch
18
18
  def redirect?; true; end
19
19
 
20
20
  def call(env)
21
- serve Request.new env
21
+ ActiveSupport::Notifications.instrument("redirect.action_dispatch") do |payload|
22
+ request = Request.new(env)
23
+ response = build_response(request)
24
+
25
+ payload[:status] = @status
26
+ payload[:location] = response.headers["Location"]
27
+ payload[:request] = request
28
+
29
+ response.to_a
30
+ end
22
31
  end
23
32
 
24
- def serve(req)
33
+ def build_response(req)
25
34
  uri = URI.parse(path(req.path_parameters, req))
26
35
 
27
36
  unless uri.host
@@ -38,15 +47,15 @@ module ActionDispatch
38
47
 
39
48
  req.commit_flash
40
49
 
41
- body = %(<html><body>You are being <a href="#{ERB::Util.unwrapped_html_escape(uri.to_s)}">redirected</a>.</body></html>)
50
+ body = ""
42
51
 
43
52
  headers = {
44
53
  "Location" => uri.to_s,
45
- "Content-Type" => "text/html",
54
+ "Content-Type" => "text/html; charset=#{ActionDispatch::Response.default_charset}",
46
55
  "Content-Length" => body.length.to_s
47
56
  }
48
57
 
49
- [ status, headers, [body] ]
58
+ ActionDispatch::Response.new(status, headers, body)
50
59
  end
51
60
 
52
61
  def path(params, request)
@@ -59,7 +68,7 @@ module ActionDispatch
59
68
 
60
69
  private
61
70
  def relative_path?(path)
62
- path && !path.empty? && path[0] != "/"
71
+ path && !path.empty? && !path.start_with?("/")
63
72
  end
64
73
 
65
74
  def escape(params)
@@ -34,20 +34,20 @@ module ActionDispatch
34
34
  if @raise_on_name_error
35
35
  raise
36
36
  else
37
- [404, { "X-Cascade" => "pass" }, []]
37
+ [404, { Constants::X_CASCADE => "pass" }, []]
38
38
  end
39
39
  end
40
40
 
41
- private
42
- def controller(req)
43
- req.controller_class
44
- rescue NameError => e
45
- raise ActionController::RoutingError, e.message, e.backtrace
46
- end
41
+ private
42
+ def controller(req)
43
+ req.controller_class
44
+ rescue NameError => e
45
+ raise ActionController::RoutingError, e.message, e.backtrace
46
+ end
47
47
 
48
- def dispatch(controller, action, req, res)
49
- controller.dispatch(action, req, res)
50
- end
48
+ def dispatch(controller, action, req, res)
49
+ controller.dispatch(action, req, res)
50
+ end
51
51
  end
52
52
 
53
53
  class StaticDispatcher < Dispatcher
@@ -282,7 +282,7 @@ module ActionDispatch
282
282
 
283
283
  if args.size < path_params_size
284
284
  path_params -= controller_options.keys
285
- path_params -= result.keys
285
+ path_params -= (result[:path_params] || {}).merge(result).keys
286
286
  else
287
287
  path_params = path_params.dup
288
288
  end
@@ -375,6 +375,7 @@ module ActionDispatch
375
375
  @disable_clear_and_finalize = false
376
376
  @finalized = false
377
377
  @env_key = "ROUTES_#{object_id}_SCRIPT_NAME"
378
+ @default_env = nil
378
379
 
379
380
  @set = Journey::Routes.new
380
381
  @router = Journey::Router.new @set
@@ -405,6 +406,25 @@ module ActionDispatch
405
406
  end
406
407
  private :make_request
407
408
 
409
+ def default_env
410
+ if default_url_options != @default_env&.[]("action_dispatch.routes.default_url_options")
411
+ url_options = default_url_options.dup.freeze
412
+ uri = URI(ActionDispatch::Http::URL.full_url_for(host: "example.org", **url_options))
413
+
414
+ @default_env = {
415
+ "action_dispatch.routes" => self,
416
+ "action_dispatch.routes.default_url_options" => url_options,
417
+ "HTTPS" => uri.scheme == "https" ? "on" : "off",
418
+ "rack.url_scheme" => uri.scheme,
419
+ "HTTP_HOST" => uri.port == uri.default_port ? uri.host : "#{uri.host}:#{uri.port}",
420
+ "SCRIPT_NAME" => uri.path.chomp("/"),
421
+ "rack.input" => "",
422
+ }.freeze
423
+ end
424
+
425
+ @default_env
426
+ end
427
+
408
428
  def draw(&block)
409
429
  clear! unless @disable_clear_and_finalize
410
430
  eval_block(block)
@@ -574,6 +594,20 @@ module ActionDispatch
574
594
  end
575
595
 
576
596
  private :_generate_paths_by_default
597
+
598
+ # If the module is included more than once (for example, in a subclass
599
+ # of an ancestor that includes the module), ensure that the `_routes`
600
+ # singleton and instance methods return the desired route set by
601
+ # including a new copy of the module (recursively if necessary). Note
602
+ # that this method is called for each inclusion, whereas the above
603
+ # `included` block is run only for the initial inclusion of each copy.
604
+ def self.included(base)
605
+ super
606
+ if base.respond_to?(:_routes) && !base._routes.equal?(@_proxy._routes)
607
+ @dup_for_reinclude ||= self.dup
608
+ base.include @dup_for_reinclude
609
+ end
610
+ end
577
611
  end
578
612
  end
579
613
 
@@ -596,16 +630,16 @@ module ActionDispatch
596
630
  named_routes[name] = route if name
597
631
 
598
632
  if route.segment_keys.include?(:controller)
599
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
633
+ ActionDispatch.deprecator.warn(<<-MSG.squish)
600
634
  Using a dynamic :controller segment in a route is deprecated and
601
- will be removed in Rails 7.1.
635
+ will be removed in Rails 7.2.
602
636
  MSG
603
637
  end
604
638
 
605
639
  if route.segment_keys.include?(:action)
606
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
640
+ ActionDispatch.deprecator.warn(<<-MSG.squish)
607
641
  Using a dynamic :action segment in a route is deprecated and
608
- will be removed in Rails 7.1.
642
+ will be removed in Rails 7.2.
609
643
  MSG
610
644
  end
611
645
 
@@ -779,18 +813,14 @@ module ActionDispatch
779
813
 
780
814
  RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
781
815
  :trailing_slash, :anchor, :params, :only_path, :script_name,
782
- :original_script_name, :relative_url_root]
816
+ :original_script_name]
783
817
 
784
818
  def optimize_routes_generation?
785
819
  default_url_options.empty?
786
820
  end
787
821
 
788
822
  def find_script_name(options)
789
- options.delete(:script_name) || find_relative_url_root(options) || ""
790
- end
791
-
792
- def find_relative_url_root(options)
793
- options.delete(:relative_url_root) || relative_url_root
823
+ options.delete(:script_name) || relative_url_root || ""
794
824
  end
795
825
 
796
826
  def path_for(options, route_name = nil, reserved = RESERVED_OPTIONS)
@@ -854,7 +884,7 @@ module ActionDispatch
854
884
 
855
885
  def recognize_path(path, environment = {})
856
886
  method = (environment[:method] || "GET").to_s.upcase
857
- path = Journey::Router::Utils.normalize_path(path) unless %r{://}.match?(path)
887
+ path = Journey::Router::Utils.normalize_path(path) unless path&.include?("://")
858
888
  extras = environment[:extras] || {}
859
889
 
860
890
  begin
@@ -29,23 +29,18 @@ module ActionDispatch
29
29
 
30
30
  def method_missing(method, *args)
31
31
  if @helpers.respond_to?(method)
32
- self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
33
- def #{method}(*args)
34
- options = args.extract_options!
35
- options = url_options.merge((options || {}).symbolize_keys)
32
+ options = args.extract_options!
33
+ options = url_options.merge((options || {}).symbolize_keys)
36
34
 
37
- if @script_namer
38
- options[:script_name] = merge_script_names(
39
- options[:script_name],
40
- @script_namer.call(options)
41
- )
42
- end
35
+ if @script_namer
36
+ options[:script_name] = merge_script_names(
37
+ options[:script_name],
38
+ @script_namer.call(options)
39
+ )
40
+ end
43
41
 
44
- args << options
45
- @helpers.#{method}(*args)
46
- end
47
- RUBY
48
- public_send(method, *args)
42
+ args << options
43
+ @helpers.public_send(method, *args)
49
44
  else
50
45
  super
51
46
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ActionDispatch
4
4
  module Routing
5
+ # = Action Dispatch Routing \UrlFor
6
+ #
5
7
  # In <tt>config/routes.rb</tt> you define URL-to-controller mappings, but the reverse
6
8
  # is also possible: a URL can be generated from one of your routing definitions.
7
9
  # URL generation functionality is centralized in this module.
@@ -37,7 +39,7 @@ module ActionDispatch
37
39
  # # => "/users/new?message=Welcome%21"
38
40
  #
39
41
  # Notice the <tt>only_path: true</tt> part. This is because UrlFor has no
40
- # information about the website hostname that your Rails app is serving. So if you
42
+ # information about the website hostname that your \Rails app is serving. So if you
41
43
  # want to include the hostname as well, then you must also pass the <tt>:host</tt>
42
44
  # argument:
43
45
  #
@@ -134,6 +136,8 @@ module ActionDispatch
134
136
  # * <tt>:port</tt> - Optionally specify the port to connect to.
135
137
  # * <tt>:anchor</tt> - An anchor name to be appended to the path.
136
138
  # * <tt>:params</tt> - The query parameters to be appended to the path.
139
+ # * <tt>:path_params</tt> - The query parameters that will only be used
140
+ # for the named dynamic segments of path. If unused, they will be discarded.
137
141
  # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in <tt>"/archive/2009/"</tt>.
138
142
  # * <tt>:script_name</tt> - Specifies application path relative to domain root. If provided, prepends application path.
139
143
  #
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/string/filters"
4
-
5
3
  module ActionDispatch
6
4
  # The routing module provides URL rewriting in native Ruby. It's a way to
7
5
  # redirect incoming requests to controllers and actions. This replaces
@@ -119,9 +117,9 @@ module ActionDispatch
119
117
  #
120
118
  # # In config/routes.rb
121
119
  # controller :blog do
122
- # get 'blog/show', to: :list
123
- # get 'blog/delete', to: :delete
124
- # get 'blog/edit', to: :edit
120
+ # get 'blog/show' => :list
121
+ # get 'blog/delete' => :delete
122
+ # get 'blog/edit' => :edit
125
123
  # end
126
124
  #
127
125
  # # provides named routes for show, delete, and edit
@@ -240,7 +238,7 @@ module ActionDispatch
240
238
  #
241
239
  # == View a list of all your routes
242
240
  #
243
- # rails routes
241
+ # $ bin/rails routes
244
242
  #
245
243
  # Target a specific controller with <tt>-c</tt>, or grep routes
246
244
  # using <tt>-g</tt>. Useful in conjunction with <tt>--expanded</tt>
@@ -250,7 +248,9 @@ module ActionDispatch
250
248
 
251
249
  autoload :Mapper
252
250
  autoload :RouteSet
253
- autoload :RoutesProxy
251
+ eager_autoload do
252
+ autoload :RoutesProxy
253
+ end
254
254
  autoload :UrlFor
255
255
  autoload :PolymorphicRoutes
256
256
 
@@ -51,7 +51,7 @@ module ActionDispatch
51
51
  # driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
52
52
  # end
53
53
  #
54
- # By default, <tt>ActionDispatch::SystemTestCase</tt> is driven by the
54
+ # By default, +ActionDispatch::SystemTestCase+ is driven by the
55
55
  # Selenium driver, with the Chrome browser, and a browser size of 1400x1400.
56
56
  #
57
57
  # Changing the driver configuration options is easy. Let's say you want to use
@@ -106,8 +106,8 @@ module ActionDispatch
106
106
  # end
107
107
  # end
108
108
  #
109
- # Because <tt>ActionDispatch::SystemTestCase</tt> is a shim between Capybara
110
- # and Rails, any driver that is supported by Capybara is supported by system
109
+ # Because +ActionDispatch::SystemTestCase+ is a shim between Capybara
110
+ # and \Rails, any driver that is supported by Capybara is supported by system
111
111
  # tests as long as you include the required gems and files.
112
112
  class SystemTestCase < ActiveSupport::TestCase
113
113
  include Capybara::DSL
@@ -3,7 +3,7 @@
3
3
  module ActionDispatch
4
4
  module SystemTesting
5
5
  class Browser # :nodoc:
6
- attr_reader :name, :options
6
+ attr_reader :name
7
7
 
8
8
  def initialize(name)
9
9
  @name = name
@@ -21,35 +21,32 @@ module ActionDispatch
21
21
  end
22
22
  end
23
23
 
24
+ def options
25
+ @options ||=
26
+ case type
27
+ when :chrome
28
+ ::Selenium::WebDriver::Chrome::Options.new
29
+ when :firefox
30
+ ::Selenium::WebDriver::Firefox::Options.new
31
+ end
32
+ end
33
+
24
34
  def configure
25
- initialize_options
26
- yield options if block_given? && options
35
+ yield options if block_given?
27
36
  end
28
37
 
29
- # driver_path can be configured as a proc. The webdrivers gem uses this
30
- # proc to update web drivers. Running this proc early allows us to only
31
- # update the webdriver once and avoid race conditions when using
32
- # parallel tests.
38
+ # driver_path is lazily initialized by default. Eagerly set it to
39
+ # avoid race conditions when using parallel tests.
33
40
  def preload
34
41
  case type
35
42
  when :chrome
36
- ::Selenium::WebDriver::Chrome::Service.driver_path&.call
43
+ resolve_driver_path(::Selenium::WebDriver::Chrome)
37
44
  when :firefox
38
- ::Selenium::WebDriver::Firefox::Service.driver_path&.call
45
+ resolve_driver_path(::Selenium::WebDriver::Firefox)
39
46
  end
40
47
  end
41
48
 
42
49
  private
43
- def initialize_options
44
- @options ||=
45
- case type
46
- when :chrome
47
- ::Selenium::WebDriver::Chrome::Options.new
48
- when :firefox
49
- ::Selenium::WebDriver::Firefox::Options.new
50
- end
51
- end
52
-
53
50
  def set_default_options
54
51
  case name
55
52
  when :headless_chrome
@@ -71,6 +68,10 @@ module ActionDispatch
71
68
  capabilities.add_argument("-headless")
72
69
  end
73
70
  end
71
+
72
+ def resolve_driver_path(namespace)
73
+ namespace::Service.driver_path = ::Selenium::WebDriver::DriverFinder.path(options, namespace::Service)
74
+ end
74
75
  end
75
76
  end
76
77
  end
@@ -12,14 +12,6 @@ module ActionDispatch
12
12
  @name = @options.delete(:name) || driver_type
13
13
  @capabilities = capabilities
14
14
 
15
- if [:poltergeist, :webkit].include?(driver_type)
16
- ActiveSupport::Deprecation.warn <<~MSG.squish
17
- Poltergeist and capybara-webkit are not maintained already.
18
- Driver registration of :poltergeist or :webkit is deprecated and will be removed in Rails 7.1.
19
- You can still use :selenium, and also :cuprite is available for alternative to Poltergeist.
20
- MSG
21
- end
22
-
23
15
  if driver_type == :selenium
24
16
  gem "selenium-webdriver", ">= 4.0.0"
25
17
  require "selenium/webdriver"
@@ -38,7 +30,7 @@ module ActionDispatch
38
30
 
39
31
  private
40
32
  def registerable?
41
- [:selenium, :poltergeist, :webkit, :cuprite, :rack_test].include?(@driver_type)
33
+ [:selenium, :cuprite, :rack_test, :playwright].include?(@driver_type)
42
34
  end
43
35
 
44
36
  def register
@@ -47,10 +39,9 @@ module ActionDispatch
47
39
  Capybara.register_driver name do |app|
48
40
  case @driver_type
49
41
  when :selenium then register_selenium(app)
50
- when :poltergeist then register_poltergeist(app)
51
- when :webkit then register_webkit(app)
52
42
  when :cuprite then register_cuprite(app)
53
43
  when :rack_test then register_rack_test(app)
44
+ when :playwright then register_playwright(app)
54
45
  end
55
46
  end
56
47
  end
@@ -65,16 +56,6 @@ module ActionDispatch
65
56
  end
66
57
  end
67
58
 
68
- def register_poltergeist(app)
69
- Capybara::Poltergeist::Driver.new(app, @options.merge(window_size: @screen_size))
70
- end
71
-
72
- def register_webkit(app)
73
- Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash.merge(@options)).tap do |driver|
74
- driver.resize_window_to(driver.current_window_handle, *@screen_size)
75
- end
76
- end
77
-
78
59
  def register_cuprite(app)
79
60
  Capybara::Cuprite::Driver.new(app, @options.merge(window_size: @screen_size))
80
61
  end
@@ -83,6 +64,17 @@ module ActionDispatch
83
64
  Capybara::RackTest::Driver.new(app, respect_data_method: true, **@options)
84
65
  end
85
66
 
67
+ def register_playwright(app)
68
+ screen = { width: @screen_size[0], height: @screen_size[1] } if @screen_size
69
+ options = {
70
+ screen: screen,
71
+ viewport: screen,
72
+ **@options
73
+ }.compact
74
+
75
+ Capybara::Playwright::Driver.new(app, **options)
76
+ end
77
+
86
78
  def setup
87
79
  Capybara.current_driver = name
88
80
  end