actionpack 6.1.7.5 → 7.1.3.1

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.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +355 -435
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +33 -37
  7. data/lib/abstract_controller/caching/fragments.rb +4 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +50 -11
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/deprecator.rb +7 -0
  12. data/lib/abstract_controller/error.rb +1 -1
  13. data/lib/abstract_controller/helpers.rb +78 -30
  14. data/lib/abstract_controller/logger.rb +1 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
  16. data/lib/abstract_controller/rendering.rb +12 -14
  17. data/lib/abstract_controller/translation.rb +26 -7
  18. data/lib/abstract_controller/url_for.rb +6 -6
  19. data/lib/abstract_controller.rb +6 -0
  20. data/lib/action_controller/api.rb +12 -10
  21. data/lib/action_controller/base.rb +8 -21
  22. data/lib/action_controller/caching.rb +2 -0
  23. data/lib/action_controller/deprecator.rb +7 -0
  24. data/lib/action_controller/form_builder.rb +4 -2
  25. data/lib/action_controller/log_subscriber.rb +20 -7
  26. data/lib/action_controller/metal/basic_implicit_render.rb +3 -1
  27. data/lib/action_controller/metal/conditional_get.rb +137 -102
  28. data/lib/action_controller/metal/content_security_policy.rb +37 -3
  29. data/lib/action_controller/metal/cookies.rb +1 -1
  30. data/lib/action_controller/metal/data_streaming.rb +25 -31
  31. data/lib/action_controller/metal/default_headers.rb +2 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  34. data/lib/action_controller/metal/exceptions.rb +27 -30
  35. data/lib/action_controller/metal/flash.rb +6 -2
  36. data/lib/action_controller/metal/head.rb +9 -7
  37. data/lib/action_controller/metal/helpers.rb +5 -16
  38. data/lib/action_controller/metal/http_authentication.rb +78 -42
  39. data/lib/action_controller/metal/implicit_render.rb +5 -3
  40. data/lib/action_controller/metal/instrumentation.rb +62 -50
  41. data/lib/action_controller/metal/live.rb +67 -2
  42. data/lib/action_controller/metal/mime_responds.rb +5 -5
  43. data/lib/action_controller/metal/params_wrapper.rb +24 -13
  44. data/lib/action_controller/metal/permissions_policy.rb +20 -29
  45. data/lib/action_controller/metal/redirecting.rb +96 -23
  46. data/lib/action_controller/metal/renderers.rb +14 -15
  47. data/lib/action_controller/metal/rendering.rb +121 -16
  48. data/lib/action_controller/metal/request_forgery_protection.rb +208 -68
  49. data/lib/action_controller/metal/rescue.rb +7 -4
  50. data/lib/action_controller/metal/streaming.rb +74 -36
  51. data/lib/action_controller/metal/strong_parameters.rb +254 -151
  52. data/lib/action_controller/metal/testing.rb +9 -2
  53. data/lib/action_controller/metal/url_for.rb +10 -5
  54. data/lib/action_controller/metal.rb +89 -34
  55. data/lib/action_controller/railtie.rb +66 -9
  56. data/lib/action_controller/renderer.rb +99 -85
  57. data/lib/action_controller/test_case.rb +42 -11
  58. data/lib/action_controller.rb +10 -6
  59. data/lib/action_dispatch/constants.rb +32 -0
  60. data/lib/action_dispatch/deprecator.rb +7 -0
  61. data/lib/action_dispatch/http/cache.rb +21 -16
  62. data/lib/action_dispatch/http/content_security_policy.rb +122 -44
  63. data/lib/action_dispatch/http/filter_parameters.rb +14 -23
  64. data/lib/action_dispatch/http/headers.rb +3 -1
  65. data/lib/action_dispatch/http/mime_negotiation.rb +25 -15
  66. data/lib/action_dispatch/http/mime_type.rb +43 -22
  67. data/lib/action_dispatch/http/mime_types.rb +3 -1
  68. data/lib/action_dispatch/http/parameters.rb +6 -6
  69. data/lib/action_dispatch/http/permissions_policy.rb +57 -19
  70. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  71. data/lib/action_dispatch/http/request.rb +75 -51
  72. data/lib/action_dispatch/http/response.rb +81 -77
  73. data/lib/action_dispatch/http/upload.rb +15 -2
  74. data/lib/action_dispatch/http/url.rb +11 -19
  75. data/lib/action_dispatch/journey/formatter.rb +8 -2
  76. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  79. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  80. data/lib/action_dispatch/journey/path/pattern.rb +36 -27
  81. data/lib/action_dispatch/journey/route.rb +8 -14
  82. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  83. data/lib/action_dispatch/journey/router.rb +10 -9
  84. data/lib/action_dispatch/journey/routes.rb +5 -5
  85. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  86. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  87. data/lib/action_dispatch/log_subscriber.rb +23 -0
  88. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -7
  89. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  90. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  91. data/lib/action_dispatch/middleware/cookies.rb +97 -107
  92. data/lib/action_dispatch/middleware/debug_exceptions.rb +31 -28
  93. data/lib/action_dispatch/middleware/debug_locks.rb +7 -4
  94. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  95. data/lib/action_dispatch/middleware/exception_wrapper.rb +190 -27
  96. data/lib/action_dispatch/middleware/executor.rb +3 -0
  97. data/lib/action_dispatch/middleware/flash.rb +24 -18
  98. data/lib/action_dispatch/middleware/host_authorization.rb +19 -20
  99. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  100. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  101. data/lib/action_dispatch/middleware/remote_ip.rb +32 -19
  102. data/lib/action_dispatch/middleware/request_id.rb +5 -3
  103. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +6 -1
  105. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  106. data/lib/action_dispatch/middleware/session/cookie_store.rb +19 -13
  107. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  108. data/lib/action_dispatch/middleware/show_exceptions.rb +30 -25
  109. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  110. data/lib/action_dispatch/middleware/stack.rb +34 -11
  111. data/lib/action_dispatch/middleware/static.rb +16 -16
  112. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  113. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +5 -5
  114. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  115. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  116. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +10 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +7 -3
  119. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +9 -9
  120. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  121. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  122. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +45 -18
  123. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -15
  124. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -4
  125. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +6 -6
  126. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +7 -7
  127. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -55
  131. data/lib/action_dispatch/railtie.rb +20 -4
  132. data/lib/action_dispatch/request/session.rb +59 -19
  133. data/lib/action_dispatch/request/utils.rb +8 -3
  134. data/lib/action_dispatch/routing/inspector.rb +55 -7
  135. data/lib/action_dispatch/routing/mapper.rb +117 -107
  136. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  137. data/lib/action_dispatch/routing/redirection.rb +20 -8
  138. data/lib/action_dispatch/routing/route_set.rb +67 -27
  139. data/lib/action_dispatch/routing/routes_proxy.rb +11 -16
  140. data/lib/action_dispatch/routing/url_for.rb +29 -26
  141. data/lib/action_dispatch/routing.rb +12 -13
  142. data/lib/action_dispatch/system_test_case.rb +8 -8
  143. data/lib/action_dispatch/system_testing/browser.rb +20 -29
  144. data/lib/action_dispatch/system_testing/driver.rb +34 -18
  145. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +35 -20
  146. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  147. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  148. data/lib/action_dispatch/testing/assertions/response.rb +14 -7
  149. data/lib/action_dispatch/testing/assertions/routing.rb +70 -30
  150. data/lib/action_dispatch/testing/assertions.rb +3 -4
  151. data/lib/action_dispatch/testing/integration.rb +33 -25
  152. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  153. data/lib/action_dispatch/testing/test_process.rb +5 -30
  154. data/lib/action_dispatch/testing/test_request.rb +1 -1
  155. data/lib/action_dispatch/testing/test_response.rb +34 -2
  156. data/lib/action_dispatch.rb +38 -4
  157. data/lib/action_pack/gem_version.rb +4 -4
  158. data/lib/action_pack/version.rb +1 -1
  159. data/lib/action_pack.rb +1 -1
  160. metadata +67 -30
@@ -6,7 +6,6 @@ require "active_support/core_ext/module/redefine_method"
6
6
  require "active_support/core_ext/module/remove_method"
7
7
  require "active_support/core_ext/array/extract_options"
8
8
  require "action_controller/metal/exceptions"
9
- require "action_dispatch/http/request"
10
9
  require "action_dispatch/routing/endpoint"
11
10
 
12
11
  module ActionDispatch
@@ -35,20 +34,20 @@ module ActionDispatch
35
34
  if @raise_on_name_error
36
35
  raise
37
36
  else
38
- [404, { "X-Cascade" => "pass" }, []]
37
+ [404, { Constants::X_CASCADE => "pass" }, []]
39
38
  end
40
39
  end
41
40
 
42
- private
43
- def controller(req)
44
- req.controller_class
45
- rescue NameError => e
46
- raise ActionController::RoutingError, e.message, e.backtrace
47
- 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
48
47
 
49
- def dispatch(controller, action, req, res)
50
- controller.dispatch(action, req, res)
51
- end
48
+ def dispatch(controller, action, req, res)
49
+ controller.dispatch(action, req, res)
50
+ end
52
51
  end
53
52
 
54
53
  class StaticDispatcher < Dispatcher
@@ -132,8 +131,8 @@ module ActionDispatch
132
131
  alias [] get
133
132
  alias clear clear!
134
133
 
135
- def each
136
- routes.each { |name, route| yield name, route }
134
+ def each(&block)
135
+ routes.each(&block)
137
136
  self
138
137
  end
139
138
 
@@ -197,7 +196,9 @@ module ActionDispatch
197
196
  def call(t, method_name, args, inner_options, url_strategy)
198
197
  if args.size == arg_size && !inner_options && optimize_routes_generation?(t)
199
198
  options = t.url_options.merge @options
200
- options[:path] = optimized_helper(args)
199
+ path = optimized_helper(args)
200
+ path << "/" if options[:trailing_slash] && !path.end_with?("/")
201
+ options[:path] = path
201
202
 
202
203
  original_script_name = options.delete(:original_script_name)
203
204
  script_name = t._routes.find_script_name(options)
@@ -281,7 +282,7 @@ module ActionDispatch
281
282
 
282
283
  if args.size < path_params_size
283
284
  path_params -= controller_options.keys
284
- path_params -= result.keys
285
+ path_params -= (result[:path_params] || {}).merge(result).keys
285
286
  else
286
287
  path_params = path_params.dup
287
288
  end
@@ -374,6 +375,7 @@ module ActionDispatch
374
375
  @disable_clear_and_finalize = false
375
376
  @finalized = false
376
377
  @env_key = "ROUTES_#{object_id}_SCRIPT_NAME"
378
+ @default_env = nil
377
379
 
378
380
  @set = Journey::Routes.new
379
381
  @router = Journey::Router.new @set
@@ -404,6 +406,25 @@ module ActionDispatch
404
406
  end
405
407
  private :make_request
406
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
+
407
428
  def draw(&block)
408
429
  clear! unless @disable_clear_and_finalize
409
430
  eval_block(block)
@@ -573,6 +594,20 @@ module ActionDispatch
573
594
  end
574
595
 
575
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
576
611
  end
577
612
  end
578
613
 
@@ -595,16 +630,16 @@ module ActionDispatch
595
630
  named_routes[name] = route if name
596
631
 
597
632
  if route.segment_keys.include?(:controller)
598
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
633
+ ActionDispatch.deprecator.warn(<<-MSG.squish)
599
634
  Using a dynamic :controller segment in a route is deprecated and
600
- will be removed in Rails 7.0.
635
+ will be removed in Rails 7.2.
601
636
  MSG
602
637
  end
603
638
 
604
639
  if route.segment_keys.include?(:action)
605
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
640
+ ActionDispatch.deprecator.warn(<<-MSG.squish)
606
641
  Using a dynamic :action segment in a route is deprecated and
607
- will be removed in Rails 7.0.
642
+ will be removed in Rails 7.2.
608
643
  MSG
609
644
  end
610
645
 
@@ -778,18 +813,14 @@ module ActionDispatch
778
813
 
779
814
  RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
780
815
  :trailing_slash, :anchor, :params, :only_path, :script_name,
781
- :original_script_name, :relative_url_root]
816
+ :original_script_name]
782
817
 
783
818
  def optimize_routes_generation?
784
819
  default_url_options.empty?
785
820
  end
786
821
 
787
822
  def find_script_name(options)
788
- options.delete(:script_name) || find_relative_url_root(options) || ""
789
- end
790
-
791
- def find_relative_url_root(options)
792
- options.delete(:relative_url_root) || relative_url_root
823
+ options.delete(:script_name) || relative_url_root || ""
793
824
  end
794
825
 
795
826
  def path_for(options, route_name = nil, reserved = RESERVED_OPTIONS)
@@ -821,10 +852,19 @@ module ActionDispatch
821
852
 
822
853
  route_with_params = generate(route_name, path_options, recall)
823
854
  path = route_with_params.path(method_name)
855
+
856
+ if options[:trailing_slash] && !options[:format] && !path.end_with?("/")
857
+ path += "/"
858
+ end
859
+
824
860
  params = route_with_params.params
825
861
 
826
862
  if options.key? :params
827
- params.merge! options[:params]
863
+ if options[:params]&.respond_to?(:to_hash)
864
+ params.merge! options[:params]
865
+ else
866
+ params[:params] = options[:params]
867
+ end
828
868
  end
829
869
 
830
870
  options[:path] = path
@@ -844,7 +884,7 @@ module ActionDispatch
844
884
 
845
885
  def recognize_path(path, environment = {})
846
886
  method = (environment[:method] || "GET").to_s.upcase
847
- path = Journey::Router::Utils.normalize_path(path) unless %r{://}.match?(path)
887
+ path = Journey::Router::Utils.normalize_path(path) unless path&.include?("://")
848
888
  extras = environment[:extras] || {}
849
889
 
850
890
  begin
@@ -4,7 +4,7 @@ require "active_support/core_ext/array/extract_options"
4
4
 
5
5
  module ActionDispatch
6
6
  module Routing
7
- class RoutesProxy #:nodoc:
7
+ class RoutesProxy # :nodoc:
8
8
  include ActionDispatch::Routing::UrlFor
9
9
 
10
10
  attr_accessor :scope, :routes
@@ -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,20 +2,22 @@
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.
8
10
  #
9
- # See ActionDispatch::Routing for general information about routing and routes.rb.
11
+ # See ActionDispatch::Routing for general information about routing and <tt>config/routes.rb</tt>.
10
12
  #
11
13
  # <b>Tip:</b> If you need to generate URLs from your models or some other place,
12
- # then ActionController::UrlFor is what you're looking for. Read on for
14
+ # then ActionDispatch::Routing::UrlFor is what you're looking for. Read on for
13
15
  # an introduction. In general, this module should not be included on its own,
14
- # as it is usually included by url_helpers (as in Rails.application.routes.url_helpers).
16
+ # as it is usually included by +url_helpers+ (as in <tt>Rails.application.routes.url_helpers</tt>).
15
17
  #
16
18
  # == URL generation from parameters
17
19
  #
18
- # As you may know, some functions, such as ActionController::Base#url_for
20
+ # As you may know, some functions, such as <tt>ActionController::Base#url_for</tt>
19
21
  # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
20
22
  # of parameters. For example, you've probably had the chance to write code
21
23
  # like this in one of your views:
@@ -24,12 +26,12 @@ module ActionDispatch
24
26
  # action: 'new', message: 'Welcome!') %>
25
27
  # # => <a href="/users/new?message=Welcome%21">Click here</a>
26
28
  #
27
- # link_to, and all other functions that require URL generation functionality,
28
- # actually use ActionController::UrlFor under the hood. And in particular,
29
- # they use the ActionController::UrlFor#url_for method. One can generate
29
+ # +link_to+, and all other functions that require URL generation functionality,
30
+ # actually use ActionDispatch::Routing::UrlFor under the hood. And in particular,
31
+ # they use the ActionDispatch::Routing::UrlFor#url_for method. One can generate
30
32
  # the same path as the above example by using the following code:
31
33
  #
32
- # include UrlFor
34
+ # include ActionDispatch::Routing::UrlFor
33
35
  # url_for(controller: 'users',
34
36
  # action: 'new',
35
37
  # message: 'Welcome!',
@@ -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
  #
@@ -48,17 +50,17 @@ module ActionDispatch
48
50
  # host: 'www.example.com')
49
51
  # # => "http://www.example.com/users/new?message=Welcome%21"
50
52
  #
51
- # By default, all controllers and views have access to a special version of url_for,
52
- # that already knows what the current hostname is. So if you use url_for in your
53
+ # By default, all controllers and views have access to a special version of +url_for+,
54
+ # that already knows what the current hostname is. So if you use +url_for+ in your
53
55
  # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
54
56
  # argument.
55
57
  #
56
- # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
57
- # So within mailers, you only have to type +url_for+ instead of 'ActionController::UrlFor#url_for'
58
- # in full. However, mailers don't have hostname information, and you still have to provide
59
- # the +:host+ argument or set the default host that will be used in all mailers using the
60
- # configuration option +config.action_mailer.default_url_options+. For more information on
61
- # url_for in mailers read the ActionMailer#Base documentation.
58
+ # For convenience, mailers also include ActionDispatch::Routing::UrlFor. So
59
+ # within mailers, you can use url_for. However, mailers cannot access
60
+ # incoming web requests in order to derive hostname information, so you have
61
+ # to provide the +:host+ option or set the default host using
62
+ # +default_url_options+. For more information on url_for in mailers see the
63
+ # ActionMailer::Base documentation.
62
64
  #
63
65
  #
64
66
  # == URL generation for named routes
@@ -70,9 +72,9 @@ module ActionDispatch
70
72
  # resources :users
71
73
  #
72
74
  # This generates, among other things, the method <tt>users_path</tt>. By default,
73
- # this method is accessible from your controllers, views and mailers. If you need
75
+ # this method is accessible from your controllers, views, and mailers. If you need
74
76
  # to access this auto-generated method from other places (such as a model), then
75
- # you can do that by including Rails.application.routes.url_helpers in your class:
77
+ # you can do that by including <tt>Rails.application.routes.url_helpers</tt> in your class:
76
78
  #
77
79
  # class User < ActiveRecord::Base
78
80
  # include Rails.application.routes.url_helpers
@@ -103,11 +105,10 @@ module ActionDispatch
103
105
  include(*_url_for_modules) if respond_to?(:_url_for_modules)
104
106
  end
105
107
 
106
- def initialize(*)
108
+ def initialize(...)
107
109
  @_routes = nil
108
110
  super
109
111
  end
110
- ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
111
112
 
112
113
  # Hook overridden in controller to add request information
113
114
  # with +default_url_options+. Application logic should not
@@ -116,11 +117,11 @@ module ActionDispatch
116
117
  default_url_options
117
118
  end
118
119
 
119
- # Generate a URL based on the options provided, default_url_options and the
120
- # routes defined in routes.rb. The following options are supported:
120
+ # Generate a URL based on the options provided, +default_url_options+, and the
121
+ # routes defined in <tt>config/routes.rb</tt>. The following options are supported:
121
122
  #
122
123
  # * <tt>:only_path</tt> - If true, the relative URL is returned. Defaults to +false+.
123
- # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
124
+ # * <tt>:protocol</tt> - The protocol to connect to. Defaults to <tt>"http"</tt>.
124
125
  # * <tt>:host</tt> - Specifies the host the link should be targeted at.
125
126
  # If <tt>:only_path</tt> is false, this option must be
126
127
  # provided either explicitly, or via +default_url_options+.
@@ -135,7 +136,9 @@ module ActionDispatch
135
136
  # * <tt>:port</tt> - Optionally specify the port to connect to.
136
137
  # * <tt>:anchor</tt> - An anchor name to be appended to the path.
137
138
  # * <tt>:params</tt> - The query parameters to be appended to the path.
138
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
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.
141
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in <tt>"/archive/2009/"</tt>.
139
142
  # * <tt>:script_name</tt> - Specifies application path relative to domain root. If provided, prepends application path.
140
143
  #
141
144
  # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
@@ -155,7 +158,7 @@ module ActionDispatch
155
158
  # # => '/myapp/tasks/testing'
156
159
  #
157
160
  # Missing routes keys may be filled in from the current request's parameters
158
- # (e.g. +:controller+, +:action+, +:id+ and any other parameters that are
161
+ # (e.g. +:controller+, +:action+, +:id+, and any other parameters that are
159
162
  # placed in the path). Given that the current action has been reached
160
163
  # through <tt>GET /users/1</tt>:
161
164
  #
@@ -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
@@ -28,7 +26,7 @@ module ActionDispatch
28
26
  #
29
27
  # Resource routing allows you to quickly declare all of the common routes
30
28
  # for a given resourceful controller. Instead of declaring separate routes
31
- # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
29
+ # for your +index+, +show+, +new+, +edit+, +create+, +update+, and +destroy+
32
30
  # actions, a resourceful route declares them in a single line of code:
33
31
  #
34
32
  # resources :photos
@@ -65,9 +63,8 @@ module ActionDispatch
65
63
  # resources :posts, :comments
66
64
  # end
67
65
  #
68
- # For more, see <tt>Routing::Mapper::Resources#resources</tt>,
69
- # <tt>Routing::Mapper::Scoping#namespace</tt>, and
70
- # <tt>Routing::Mapper::Scoping#scope</tt>.
66
+ # For more, see Routing::Mapper::Resources#resources,
67
+ # Routing::Mapper::Scoping#namespace, and Routing::Mapper::Scoping#scope.
71
68
  #
72
69
  # == Non-resourceful routes
73
70
  #
@@ -120,9 +117,9 @@ module ActionDispatch
120
117
  #
121
118
  # # In config/routes.rb
122
119
  # controller :blog do
123
- # get 'blog/show', to: :list
124
- # get 'blog/delete', to: :delete
125
- # get 'blog/edit', to: :edit
120
+ # get 'blog/show' => :list
121
+ # get 'blog/delete' => :delete
122
+ # get 'blog/edit' => :edit
126
123
  # end
127
124
  #
128
125
  # # provides named routes for show, delete, and edit
@@ -241,7 +238,7 @@ module ActionDispatch
241
238
  #
242
239
  # == View a list of all your routes
243
240
  #
244
- # rails routes
241
+ # $ bin/rails routes
245
242
  #
246
243
  # Target a specific controller with <tt>-c</tt>, or grep routes
247
244
  # using <tt>-g</tt>. Useful in conjunction with <tt>--expanded</tt>
@@ -251,11 +248,13 @@ module ActionDispatch
251
248
 
252
249
  autoload :Mapper
253
250
  autoload :RouteSet
254
- autoload :RoutesProxy
251
+ eager_autoload do
252
+ autoload :RoutesProxy
253
+ end
255
254
  autoload :UrlFor
256
255
  autoload :PolymorphicRoutes
257
256
 
258
- SEPARATORS = %w( / . ? ) #:nodoc:
259
- HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc:
257
+ SEPARATORS = %w( / . ? ) # :nodoc:
258
+ HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] # :nodoc:
260
259
  end
261
260
  end
@@ -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
@@ -72,8 +72,8 @@ module ActionDispatch
72
72
  # Headless browsers such as headless Chrome and headless Firefox are also supported.
73
73
  # You can use these browsers by setting the +:using+ argument to +:headless_chrome+ or +:headless_firefox+.
74
74
  #
75
- # To use a headless driver, like Poltergeist, update your Gemfile to use
76
- # Poltergeist instead of Selenium and then declare the driver name in the
75
+ # To use a headless driver, like Cuprite, update your Gemfile to use
76
+ # Cuprite instead of Selenium and then declare the driver name in the
77
77
  # +application_system_test_case.rb+ file. In this case, you would leave out
78
78
  # the +:using+ option because the driver is headless, but you can still use
79
79
  # +:screen_size+ to change the size of the browser screen, also you can use
@@ -81,10 +81,10 @@ module ActionDispatch
81
81
  # driver documentation to learn about supported options.
82
82
  #
83
83
  # require "test_helper"
84
- # require "capybara/poltergeist"
84
+ # require "capybara/cuprite"
85
85
  #
86
86
  # class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
87
- # driven_by :poltergeist, screen_size: [1400, 1400], options:
87
+ # driven_by :cuprite, screen_size: [1400, 1400], options:
88
88
  # { js_errors: true }
89
89
  # end
90
90
  #
@@ -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
@@ -142,7 +142,7 @@ module ActionDispatch
142
142
  #
143
143
  # Examples:
144
144
  #
145
- # driven_by :poltergeist
145
+ # driven_by :cuprite
146
146
  #
147
147
  # driven_by :selenium, screen_size: [800, 800]
148
148
  #
@@ -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,45 +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
- if ::Selenium::WebDriver::Service.respond_to? :driver_path=
37
- ::Selenium::WebDriver::Chrome::Service.driver_path&.call
38
- else
39
- # Selenium <= v3.141.0
40
- ::Selenium::WebDriver::Chrome.driver_path
41
- end
43
+ resolve_driver_path(::Selenium::WebDriver::Chrome)
42
44
  when :firefox
43
- if ::Selenium::WebDriver::Service.respond_to? :driver_path=
44
- ::Selenium::WebDriver::Firefox::Service.driver_path&.call
45
- else
46
- # Selenium <= v3.141.0
47
- ::Selenium::WebDriver::Firefox.driver_path
48
- end
45
+ resolve_driver_path(::Selenium::WebDriver::Firefox)
49
46
  end
50
47
  end
51
48
 
52
49
  private
53
- def initialize_options
54
- @options ||=
55
- case type
56
- when :chrome
57
- ::Selenium::WebDriver::Chrome::Options.new
58
- when :firefox
59
- ::Selenium::WebDriver::Firefox::Options.new
60
- end
61
- end
62
-
63
50
  def set_default_options
64
51
  case name
65
52
  when :headless_chrome
@@ -81,6 +68,10 @@ module ActionDispatch
81
68
  capabilities.add_argument("-headless")
82
69
  end
83
70
  end
71
+
72
+ def resolve_driver_path(namespace)
73
+ namespace::Service.driver_path = ::Selenium::WebDriver::DriverFinder.path(options, namespace::Service)
74
+ end
84
75
  end
85
76
  end
86
77
  end
@@ -3,16 +3,22 @@
3
3
  module ActionDispatch
4
4
  module SystemTesting
5
5
  class Driver # :nodoc:
6
- def initialize(name, **options, &capabilities)
7
- @name = name
8
- @browser = Browser.new(options[:using])
6
+ attr_reader :name
7
+
8
+ def initialize(driver_type, **options, &capabilities)
9
+ @driver_type = driver_type
9
10
  @screen_size = options[:screen_size]
10
11
  @options = options[:options] || {}
12
+ @name = @options.delete(:name) || driver_type
11
13
  @capabilities = capabilities
12
14
 
13
- if name == :selenium
15
+ if driver_type == :selenium
16
+ gem "selenium-webdriver", ">= 4.0.0"
14
17
  require "selenium/webdriver"
18
+ @browser = Browser.new(options[:using])
15
19
  @browser.preload
20
+ else
21
+ @browser = nil
16
22
  end
17
23
  end
18
24
 
@@ -24,17 +30,18 @@ module ActionDispatch
24
30
 
25
31
  private
26
32
  def registerable?
27
- [:selenium, :poltergeist, :webkit].include?(@name)
33
+ [:selenium, :cuprite, :rack_test, :playwright].include?(@driver_type)
28
34
  end
29
35
 
30
36
  def register
31
- @browser.configure(&@capabilities)
37
+ @browser&.configure(&@capabilities)
32
38
 
33
- Capybara.register_driver @name do |app|
34
- case @name
39
+ Capybara.register_driver name do |app|
40
+ case @driver_type
35
41
  when :selenium then register_selenium(app)
36
- when :poltergeist then register_poltergeist(app)
37
- when :webkit then register_webkit(app)
42
+ when :cuprite then register_cuprite(app)
43
+ when :rack_test then register_rack_test(app)
44
+ when :playwright then register_playwright(app)
38
45
  end
39
46
  end
40
47
  end
@@ -44,23 +51,32 @@ module ActionDispatch
44
51
  end
45
52
 
46
53
  def register_selenium(app)
47
- Capybara::Selenium::Driver.new(app, **{ browser: @browser.type }.merge(browser_options)).tap do |driver|
54
+ Capybara::Selenium::Driver.new(app, browser: @browser.type, **browser_options).tap do |driver|
48
55
  driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
49
56
  end
50
57
  end
51
58
 
52
- def register_poltergeist(app)
53
- Capybara::Poltergeist::Driver.new(app, @options.merge(window_size: @screen_size))
59
+ def register_cuprite(app)
60
+ Capybara::Cuprite::Driver.new(app, @options.merge(window_size: @screen_size))
54
61
  end
55
62
 
56
- def register_webkit(app)
57
- Capybara::Webkit::Driver.new(app, Capybara::Webkit::Configuration.to_hash.merge(@options)).tap do |driver|
58
- driver.resize_window_to(driver.current_window_handle, *@screen_size)
59
- end
63
+ def register_rack_test(app)
64
+ Capybara::RackTest::Driver.new(app, respect_data_method: true, **@options)
65
+ end
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)
60
76
  end
61
77
 
62
78
  def setup
63
- Capybara.current_driver = @name
79
+ Capybara.current_driver = name
64
80
  end
65
81
  end
66
82
  end