actionpack 6.1.7 → 7.0.4.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +269 -406
  3. data/MIT-LICENSE +1 -0
  4. data/README.rdoc +2 -3
  5. data/lib/abstract_controller/asset_paths.rb +1 -1
  6. data/lib/abstract_controller/base.rb +13 -26
  7. data/lib/abstract_controller/caching/fragments.rb +2 -2
  8. data/lib/abstract_controller/caching.rb +1 -1
  9. data/lib/abstract_controller/callbacks.rb +21 -7
  10. data/lib/abstract_controller/collector.rb +2 -2
  11. data/lib/abstract_controller/error.rb +1 -1
  12. data/lib/abstract_controller/helpers.rb +4 -3
  13. data/lib/abstract_controller/logger.rb +1 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/translation.rb +3 -2
  16. data/lib/abstract_controller/url_for.rb +4 -6
  17. data/lib/action_controller/api.rb +6 -6
  18. data/lib/action_controller/base.rb +5 -4
  19. data/lib/action_controller/form_builder.rb +2 -2
  20. data/lib/action_controller/log_subscriber.rb +4 -3
  21. data/lib/action_controller/metal/conditional_get.rb +39 -2
  22. data/lib/action_controller/metal/content_security_policy.rb +36 -2
  23. data/lib/action_controller/metal/cookies.rb +1 -1
  24. data/lib/action_controller/metal/data_streaming.rb +5 -13
  25. data/lib/action_controller/metal/exceptions.rb +19 -30
  26. data/lib/action_controller/metal/flash.rb +6 -2
  27. data/lib/action_controller/metal/helpers.rb +2 -2
  28. data/lib/action_controller/metal/http_authentication.rb +66 -39
  29. data/lib/action_controller/metal/instrumentation.rb +57 -52
  30. data/lib/action_controller/metal/live.rb +43 -2
  31. data/lib/action_controller/metal/mime_responds.rb +3 -3
  32. data/lib/action_controller/metal/params_wrapper.rb +20 -11
  33. data/lib/action_controller/metal/permissions_policy.rb +19 -28
  34. data/lib/action_controller/metal/redirecting.rb +93 -18
  35. data/lib/action_controller/metal/renderers.rb +10 -11
  36. data/lib/action_controller/metal/rendering.rb +8 -8
  37. data/lib/action_controller/metal/request_forgery_protection.rb +78 -29
  38. data/lib/action_controller/metal/rescue.rb +1 -1
  39. data/lib/action_controller/metal/streaming.rb +6 -8
  40. data/lib/action_controller/metal/strong_parameters.rb +100 -54
  41. data/lib/action_controller/metal/testing.rb +9 -2
  42. data/lib/action_controller/metal/url_for.rb +3 -3
  43. data/lib/action_controller/metal.rb +10 -13
  44. data/lib/action_controller/railtie.rb +49 -6
  45. data/lib/action_controller/renderer.rb +1 -1
  46. data/lib/action_controller/test_case.rb +28 -7
  47. data/lib/action_controller.rb +2 -5
  48. data/lib/action_dispatch/http/cache.rb +14 -7
  49. data/lib/action_dispatch/http/content_security_policy.rb +108 -35
  50. data/lib/action_dispatch/http/filter_parameters.rb +5 -0
  51. data/lib/action_dispatch/http/mime_negotiation.rb +15 -5
  52. data/lib/action_dispatch/http/mime_type.rb +9 -11
  53. data/lib/action_dispatch/http/parameters.rb +5 -5
  54. data/lib/action_dispatch/http/permissions_policy.rb +17 -1
  55. data/lib/action_dispatch/http/request.rb +12 -21
  56. data/lib/action_dispatch/http/response.rb +3 -16
  57. data/lib/action_dispatch/http/url.rb +11 -19
  58. data/lib/action_dispatch/journey/gtg/builder.rb +11 -12
  59. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -4
  60. data/lib/action_dispatch/journey/gtg/transition_table.rb +77 -21
  61. data/lib/action_dispatch/journey/nodes/node.rb +70 -5
  62. data/lib/action_dispatch/journey/path/pattern.rb +22 -13
  63. data/lib/action_dispatch/journey/route.rb +6 -13
  64. data/lib/action_dispatch/journey/router/utils.rb +2 -2
  65. data/lib/action_dispatch/journey/router.rb +1 -1
  66. data/lib/action_dispatch/journey/routes.rb +3 -3
  67. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  68. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  69. data/lib/action_dispatch/middleware/actionable_exceptions.rb +0 -1
  70. data/lib/action_dispatch/middleware/cookies.rb +42 -27
  71. data/lib/action_dispatch/middleware/debug_exceptions.rb +6 -4
  72. data/lib/action_dispatch/middleware/debug_locks.rb +3 -3
  73. data/lib/action_dispatch/middleware/exception_wrapper.rb +4 -0
  74. data/lib/action_dispatch/middleware/executor.rb +3 -0
  75. data/lib/action_dispatch/middleware/flash.rb +17 -18
  76. data/lib/action_dispatch/middleware/host_authorization.rb +1 -12
  77. data/lib/action_dispatch/middleware/remote_ip.rb +16 -4
  78. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  79. data/lib/action_dispatch/middleware/server_timing.rb +76 -0
  80. data/lib/action_dispatch/middleware/session/abstract_store.rb +1 -1
  81. data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
  82. data/lib/action_dispatch/middleware/show_exceptions.rb +7 -9
  83. data/lib/action_dispatch/middleware/stack.rb +27 -9
  84. data/lib/action_dispatch/middleware/static.rb +2 -6
  85. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +1 -1
  86. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -11
  87. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +2 -2
  88. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +3 -2
  89. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +4 -4
  91. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +3 -3
  92. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +28 -18
  93. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +3 -3
  94. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +3 -3
  95. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  96. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  97. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +3 -3
  98. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +5 -14
  99. data/lib/action_dispatch/railtie.rb +8 -2
  100. data/lib/action_dispatch/request/session.rb +43 -13
  101. data/lib/action_dispatch/routing/inspector.rb +1 -1
  102. data/lib/action_dispatch/routing/mapper.rb +59 -83
  103. data/lib/action_dispatch/routing/redirection.rb +5 -2
  104. data/lib/action_dispatch/routing/route_set.rb +17 -7
  105. data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
  106. data/lib/action_dispatch/routing/url_for.rb +4 -5
  107. data/lib/action_dispatch/routing.rb +5 -6
  108. data/lib/action_dispatch/system_test_case.rb +5 -5
  109. data/lib/action_dispatch/system_testing/browser.rb +2 -12
  110. data/lib/action_dispatch/system_testing/driver.rb +35 -11
  111. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +11 -7
  112. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +0 -8
  113. data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
  114. data/lib/action_dispatch/testing/assertions.rb +2 -5
  115. data/lib/action_dispatch/testing/integration.rb +6 -8
  116. data/lib/action_dispatch/testing/test_process.rb +3 -29
  117. data/lib/action_dispatch/testing/test_response.rb +20 -2
  118. data/lib/action_dispatch.rb +1 -0
  119. data/lib/action_pack/gem_version.rb +5 -5
  120. data/lib/action_pack/version.rb +1 -1
  121. metadata +16 -15
@@ -4,7 +4,6 @@ require "active_support/core_ext/hash/slice"
4
4
  require "active_support/core_ext/enumerable"
5
5
  require "active_support/core_ext/array/extract_options"
6
6
  require "active_support/core_ext/regexp"
7
- require "active_support/core_ext/symbol/starts_ends_with"
8
7
  require "action_dispatch/routing/redirection"
9
8
  require "action_dispatch/routing/endpoint"
10
9
 
@@ -13,7 +12,7 @@ module ActionDispatch
13
12
  class Mapper
14
13
  URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
15
14
 
16
- class Constraints < Routing::Endpoint #:nodoc:
15
+ class Constraints < Routing::Endpoint # :nodoc:
17
16
  attr_reader :app, :constraints
18
17
 
19
18
  SERVE = ->(app, req) { app.serve req }
@@ -67,11 +66,11 @@ module ActionDispatch
67
66
  end
68
67
  end
69
68
 
70
- class Mapping #:nodoc:
69
+ class Mapping # :nodoc:
71
70
  ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
72
71
  OPTIONAL_FORMAT_REGEX = %r{(?:\(\.:format\)+|\.:format|/)\Z}
73
72
 
74
- attr_reader :requirements, :defaults, :to, :default_controller,
73
+ attr_reader :path, :requirements, :defaults, :to, :default_controller,
75
74
  :default_action, :required_defaults, :ast, :scope_options
76
75
 
77
76
  def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
@@ -122,31 +121,17 @@ module ActionDispatch
122
121
  @to = intern(to)
123
122
  @default_controller = intern(controller)
124
123
  @default_action = intern(default_action)
125
- @ast = ast
126
124
  @anchor = anchor
127
125
  @via = via
128
126
  @internal = options.delete(:internal)
129
127
  @scope_options = scope_params[:options]
128
+ ast = Journey::Ast.new(ast, formatted)
130
129
 
131
- path_params = []
132
- wildcard_options = {}
133
- ast.each do |node|
134
- if node.symbol?
135
- path_params << node.to_sym
136
- elsif formatted != false && node.star?
137
- # Add a constraint for wildcard route to make it non-greedy and match the
138
- # optional format part of the route by default.
139
- wildcard_options[node.name.to_sym] ||= /.+?/
140
- elsif node.cat?
141
- alter_regex_for_custom_routes(node)
142
- end
143
- end
144
-
145
- options = wildcard_options.merge!(options)
130
+ options = ast.wildcard_options.merge!(options)
146
131
 
147
- options = normalize_options!(options, path_params, scope_params[:module])
132
+ options = normalize_options!(options, ast.path_params, scope_params[:module])
148
133
 
149
- split_options = constraints(options, path_params)
134
+ split_options = constraints(options, ast.path_params)
150
135
 
151
136
  constraints = scope_params[:constraints].merge Hash[split_options[:constraints] || []]
152
137
 
@@ -160,8 +145,8 @@ module ActionDispatch
160
145
  @blocks = blocks(options_constraints)
161
146
  end
162
147
 
163
- requirements, conditions = split_constraints path_params, constraints
164
- verify_regexp_requirements requirements.map(&:last).grep(Regexp)
148
+ requirements, conditions = split_constraints ast.path_params, constraints
149
+ verify_regexp_requirements requirements, ast.wildcard_options
165
150
 
166
151
  formats = normalize_format(formatted)
167
152
 
@@ -169,13 +154,18 @@ module ActionDispatch
169
154
  @conditions = Hash[conditions]
170
155
  @defaults = formats[:defaults].merge(@defaults).merge(normalize_defaults(options))
171
156
 
172
- if path_params.include?(:action) && !@requirements.key?(:action)
157
+ if ast.path_params.include?(:action) && !@requirements.key?(:action)
173
158
  @defaults[:action] ||= "index"
174
159
  end
175
160
 
176
161
  @required_defaults = (split_options[:required_defaults] || []).map(&:first)
162
+
163
+ ast.requirements = @requirements
164
+ @path = Journey::Path::Pattern.new(ast, @requirements, JOINED_SEPARATORS, @anchor)
177
165
  end
178
166
 
167
+ JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
168
+
179
169
  def make_route(name, precedence)
180
170
  Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
181
171
  required_defaults: required_defaults, defaults: defaults,
@@ -187,12 +177,6 @@ module ActionDispatch
187
177
  app(@blocks)
188
178
  end
189
179
 
190
- JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
191
-
192
- def path
193
- Journey::Path::Pattern.new(@ast, requirements, JOINED_SEPARATORS, @anchor)
194
- end
195
-
196
180
  def conditions
197
181
  build_conditions @conditions, @set.request_class
198
182
  end
@@ -212,24 +196,6 @@ module ActionDispatch
212
196
  private :request_method
213
197
 
214
198
  private
215
- # Find all the symbol nodes that are adjacent to literal nodes and alter
216
- # the regexp so that Journey will partition them into custom routes.
217
- def alter_regex_for_custom_routes(node)
218
- if node.left.literal? && node.right.symbol?
219
- symbol = node.right
220
- elsif node.left.literal? && node.right.cat? && node.right.left.symbol?
221
- symbol = node.right.left
222
- elsif node.left.symbol? && node.right.literal?
223
- symbol = node.left
224
- elsif node.left.symbol? && node.right.cat? && node.right.left.literal?
225
- symbol = node.left
226
- end
227
-
228
- if symbol
229
- symbol.regexp = /(?:#{Regexp.union(symbol.regexp, '-')})+/
230
- end
231
- end
232
-
233
199
  def intern(object)
234
200
  object.is_a?(String) ? -object : object
235
201
  end
@@ -280,14 +246,18 @@ module ActionDispatch
280
246
  end
281
247
  end
282
248
 
283
- def verify_regexp_requirements(requirements)
284
- requirements.each do |requirement|
285
- if ANCHOR_CHARACTERS_REGEX.match?(requirement.source)
249
+ def verify_regexp_requirements(requirements, wildcard_options)
250
+ requirements.each do |requirement, regex|
251
+ next unless regex.is_a? Regexp
252
+
253
+ if ANCHOR_CHARACTERS_REGEX.match?(regex.source)
286
254
  raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
287
255
  end
288
256
 
289
- if requirement.multiline?
290
- raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
257
+ if regex.multiline?
258
+ next if wildcard_options.key?(requirement)
259
+
260
+ raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{regex.inspect}"
291
261
  end
292
262
  end
293
263
  end
@@ -420,10 +390,10 @@ module ActionDispatch
420
390
  #
421
391
  # If you want to expose your action to both GET and POST, use:
422
392
  #
423
- # # sets :controller, :action and :id in params
393
+ # # sets :controller, :action, and :id in params
424
394
  # match ':controller/:action/:id', via: [:get, :post]
425
395
  #
426
- # Note that +:controller+, +:action+ and +:id+ are interpreted as URL
396
+ # Note that +:controller+, +:action+, and +:id+ are interpreted as URL
427
397
  # query parameters and thus available through +params+ in an action.
428
398
  #
429
399
  # If you want to expose your action to GET, use +get+ in the router:
@@ -639,7 +609,7 @@ module ActionDispatch
639
609
  target_as = name_for_action(options[:as], path)
640
610
  options[:via] ||= :all
641
611
 
642
- match(path, options.merge(to: app, anchor: false, format: false))
612
+ match(path, { to: app, anchor: false, format: false }.merge(options))
643
613
 
644
614
  define_generate_prefix(app, target_as) if rails_app
645
615
  self
@@ -936,7 +906,7 @@ module ActionDispatch
936
906
  #
937
907
  # === Options
938
908
  #
939
- # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
909
+ # The +:path+, +:as+, +:module+, +:shallow_path+, and +:shallow_prefix+
940
910
  # options all default to the name of the namespace.
941
911
  #
942
912
  # For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
@@ -956,7 +926,7 @@ module ActionDispatch
956
926
  # namespace :admin, as: "sekret" do
957
927
  # resources :posts
958
928
  # end
959
- def namespace(path, options = {})
929
+ def namespace(path, options = {}, &block)
960
930
  path = path.to_s
961
931
 
962
932
  defaults = {
@@ -967,7 +937,7 @@ module ActionDispatch
967
937
  }
968
938
 
969
939
  path_scope(options.delete(:path) { path }) do
970
- scope(defaults.merge!(options)) { yield }
940
+ scope(defaults.merge!(options), &block)
971
941
  end
972
942
  end
973
943
 
@@ -1026,8 +996,8 @@ module ActionDispatch
1026
996
  # constraints(Iphone) do
1027
997
  # resources :iphones
1028
998
  # end
1029
- def constraints(constraints = {})
1030
- scope(constraints: constraints) { yield }
999
+ def constraints(constraints = {}, &block)
1000
+ scope(constraints: constraints, &block)
1031
1001
  end
1032
1002
 
1033
1003
  # Allows you to set default parameters for a route, such as this:
@@ -1112,7 +1082,7 @@ module ActionDispatch
1112
1082
 
1113
1083
  # Resource routing allows you to quickly declare all of the common routes
1114
1084
  # for a given resourceful controller. Instead of declaring separate routes
1115
- # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
1085
+ # for your +index+, +show+, +new+, +edit+, +create+, +update+, and +destroy+
1116
1086
  # actions, a resourceful route declares them in a single line of code:
1117
1087
  #
1118
1088
  # resources :photos
@@ -1156,7 +1126,7 @@ module ActionDispatch
1156
1126
  RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
1157
1127
  CANONICAL_ACTIONS = %w(index create new show update destroy)
1158
1128
 
1159
- class Resource #:nodoc:
1129
+ class Resource # :nodoc:
1160
1130
  attr_reader :controller, :path, :param
1161
1131
 
1162
1132
  def initialize(entities, api_only, shallow, options = {})
@@ -1251,7 +1221,7 @@ module ActionDispatch
1251
1221
  def singleton?; false; end
1252
1222
  end
1253
1223
 
1254
- class SingletonResource < Resource #:nodoc:
1224
+ class SingletonResource < Resource # :nodoc:
1255
1225
  def initialize(entities, api_only, shallow, options)
1256
1226
  super
1257
1227
  @as = nil
@@ -1307,6 +1277,16 @@ module ActionDispatch
1307
1277
  # DELETE /profile
1308
1278
  # POST /profile
1309
1279
  #
1280
+ # If you want instances of a model to work with this resource via
1281
+ # record identification (e.g. in +form_with+ or +redirect_to+), you
1282
+ # will need to call resolve[rdoc-ref:CustomUrls#resolve]:
1283
+ #
1284
+ # resource :profile
1285
+ # resolve('Profile') { [:profile] }
1286
+ #
1287
+ # # Enables this to work with singular routes:
1288
+ # form_with(model: @profile) {}
1289
+ #
1310
1290
  # === Options
1311
1291
  # Takes same options as resources[rdoc-ref:#resources]
1312
1292
  def resource(*resources, &block)
@@ -1517,15 +1497,13 @@ module ActionDispatch
1517
1497
  # with GET, and route to the search action of +PhotosController+. It will also
1518
1498
  # create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
1519
1499
  # route helpers.
1520
- def collection
1500
+ def collection(&block)
1521
1501
  unless resource_scope?
1522
1502
  raise ArgumentError, "can't use collection outside resource(s) scope"
1523
1503
  end
1524
1504
 
1525
1505
  with_scope_level(:collection) do
1526
- path_scope(parent_resource.collection_scope) do
1527
- yield
1528
- end
1506
+ path_scope(parent_resource.collection_scope, &block)
1529
1507
  end
1530
1508
  end
1531
1509
 
@@ -1540,7 +1518,7 @@ module ActionDispatch
1540
1518
  # This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
1541
1519
  # preview action of +PhotosController+. It will also create the
1542
1520
  # <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
1543
- def member
1521
+ def member(&block)
1544
1522
  unless resource_scope?
1545
1523
  raise ArgumentError, "can't use member outside resource(s) scope"
1546
1524
  end
@@ -1548,27 +1526,25 @@ module ActionDispatch
1548
1526
  with_scope_level(:member) do
1549
1527
  if shallow?
1550
1528
  shallow_scope {
1551
- path_scope(parent_resource.member_scope) { yield }
1529
+ path_scope(parent_resource.member_scope, &block)
1552
1530
  }
1553
1531
  else
1554
- path_scope(parent_resource.member_scope) { yield }
1532
+ path_scope(parent_resource.member_scope, &block)
1555
1533
  end
1556
1534
  end
1557
1535
  end
1558
1536
 
1559
- def new
1537
+ def new(&block)
1560
1538
  unless resource_scope?
1561
1539
  raise ArgumentError, "can't use new outside resource(s) scope"
1562
1540
  end
1563
1541
 
1564
1542
  with_scope_level(:new) do
1565
- path_scope(parent_resource.new_scope(action_path(:new))) do
1566
- yield
1567
- end
1543
+ path_scope(parent_resource.new_scope(action_path(:new)), &block)
1568
1544
  end
1569
1545
  end
1570
1546
 
1571
- def nested
1547
+ def nested(&block)
1572
1548
  unless resource_scope?
1573
1549
  raise ArgumentError, "can't use nested outside resource(s) scope"
1574
1550
  end
@@ -1577,12 +1553,12 @@ module ActionDispatch
1577
1553
  if shallow? && shallow_nesting_depth >= 1
1578
1554
  shallow_scope do
1579
1555
  path_scope(parent_resource.nested_scope) do
1580
- scope(nested_options) { yield }
1556
+ scope(nested_options, &block)
1581
1557
  end
1582
1558
  end
1583
1559
  else
1584
1560
  path_scope(parent_resource.nested_scope) do
1585
- scope(nested_options) { yield }
1561
+ scope(nested_options, &block)
1586
1562
  end
1587
1563
  end
1588
1564
  end
@@ -1768,10 +1744,10 @@ module ActionDispatch
1768
1744
  @scope = @scope.parent
1769
1745
  end
1770
1746
 
1771
- def resource_scope(resource)
1747
+ def resource_scope(resource, &block)
1772
1748
  @scope = @scope.new(scope_level_resource: resource)
1773
1749
 
1774
- controller(resource.resource_scope) { yield }
1750
+ controller(resource.resource_scope, &block)
1775
1751
  ensure
1776
1752
  @scope = @scope.parent
1777
1753
  end
@@ -1889,7 +1865,7 @@ module ActionDispatch
1889
1865
  end
1890
1866
 
1891
1867
  def map_match(paths, options)
1892
- if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
1868
+ if (on = options[:on]) && !VALID_ON_OPTIONS.include?(on)
1893
1869
  raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
1894
1870
  end
1895
1871
 
@@ -2300,7 +2276,7 @@ module ActionDispatch
2300
2276
  NULL = Scope.new(nil, nil)
2301
2277
  end
2302
2278
 
2303
- def initialize(set) #:nodoc:
2279
+ def initialize(set) # :nodoc:
2304
2280
  @set = set
2305
2281
  @draw_paths = set.draw_paths
2306
2282
  @scope = Scope.new(path_names: @set.resources_path_names)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "action_dispatch/http/request"
4
- require "active_support/core_ext/uri"
5
3
  require "active_support/core_ext/array/extract_options"
6
4
  require "rack/utils"
7
5
  require "action_controller/metal/exceptions"
@@ -144,6 +142,11 @@ module ActionDispatch
144
142
  # This will redirect the user, while ignoring certain parts of the request, including query string, etc.
145
143
  # <tt>/stories</tt>, <tt>/stories?foo=bar</tt>, etc all redirect to <tt>/posts</tt>.
146
144
  #
145
+ # The redirect will use a <tt>301 Moved Permanently</tt> status code by
146
+ # default. This can be overridden with the +:status+ option:
147
+ #
148
+ # get "/stories" => redirect("/posts", status: 307)
149
+ #
147
150
  # You can also use interpolation in the supplied redirect argument:
148
151
  #
149
152
  # get 'docs/:article', to: redirect('/wiki/%{article}')
@@ -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
@@ -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)
@@ -597,14 +598,14 @@ module ActionDispatch
597
598
  if route.segment_keys.include?(:controller)
598
599
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
599
600
  Using a dynamic :controller segment in a route is deprecated and
600
- will be removed in Rails 7.0.
601
+ will be removed in Rails 7.1.
601
602
  MSG
602
603
  end
603
604
 
604
605
  if route.segment_keys.include?(:action)
605
606
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
606
607
  Using a dynamic :action segment in a route is deprecated and
607
- will be removed in Rails 7.0.
608
+ will be removed in Rails 7.1.
608
609
  MSG
609
610
  end
610
611
 
@@ -821,10 +822,19 @@ module ActionDispatch
821
822
 
822
823
  route_with_params = generate(route_name, path_options, recall)
823
824
  path = route_with_params.path(method_name)
825
+
826
+ if options[:trailing_slash] && !options[:format] && !path.end_with?("/")
827
+ path += "/"
828
+ end
829
+
824
830
  params = route_with_params.params
825
831
 
826
832
  if options.key? :params
827
- params.merge! options[:params]
833
+ if options[:params]&.respond_to?(:to_hash)
834
+ params.merge! options[:params]
835
+ else
836
+ params[:params] = options[:params]
837
+ end
828
838
  end
829
839
 
830
840
  options[:path] = path
@@ -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
@@ -70,7 +70,7 @@ module ActionDispatch
70
70
  # resources :users
71
71
  #
72
72
  # 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
73
+ # this method is accessible from your controllers, views, and mailers. If you need
74
74
  # to access this auto-generated method from other places (such as a model), then
75
75
  # you can do that by including Rails.application.routes.url_helpers in your class:
76
76
  #
@@ -103,11 +103,10 @@ module ActionDispatch
103
103
  include(*_url_for_modules) if respond_to?(:_url_for_modules)
104
104
  end
105
105
 
106
- def initialize(*)
106
+ def initialize(...)
107
107
  @_routes = nil
108
108
  super
109
109
  end
110
- ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
111
110
 
112
111
  # Hook overridden in controller to add request information
113
112
  # with +default_url_options+. Application logic should not
@@ -116,7 +115,7 @@ module ActionDispatch
116
115
  default_url_options
117
116
  end
118
117
 
119
- # Generate a URL based on the options provided, default_url_options and the
118
+ # Generate a URL based on the options provided, default_url_options, and the
120
119
  # routes defined in routes.rb. The following options are supported:
121
120
  #
122
121
  # * <tt>:only_path</tt> - If true, the relative URL is returned. Defaults to +false+.
@@ -155,7 +154,7 @@ module ActionDispatch
155
154
  # # => '/myapp/tasks/testing'
156
155
  #
157
156
  # 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
157
+ # (e.g. +:controller+, +:action+, +:id+, and any other parameters that are
159
158
  # placed in the path). Given that the current action has been reached
160
159
  # through <tt>GET /users/1</tt>:
161
160
  #
@@ -28,7 +28,7 @@ module ActionDispatch
28
28
  #
29
29
  # Resource routing allows you to quickly declare all of the common routes
30
30
  # for a given resourceful controller. Instead of declaring separate routes
31
- # for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
31
+ # for your +index+, +show+, +new+, +edit+, +create+, +update+, and +destroy+
32
32
  # actions, a resourceful route declares them in a single line of code:
33
33
  #
34
34
  # resources :photos
@@ -65,9 +65,8 @@ module ActionDispatch
65
65
  # resources :posts, :comments
66
66
  # end
67
67
  #
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>.
68
+ # For more, see Routing::Mapper::Resources#resources,
69
+ # Routing::Mapper::Scoping#namespace, and Routing::Mapper::Scoping#scope.
71
70
  #
72
71
  # == Non-resourceful routes
73
72
  #
@@ -255,7 +254,7 @@ module ActionDispatch
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
@@ -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
  #
@@ -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
  #
@@ -33,19 +33,9 @@ module ActionDispatch
33
33
  def preload
34
34
  case type
35
35
  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
36
+ ::Selenium::WebDriver::Chrome::Service.driver_path&.call
42
37
  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
38
+ ::Selenium::WebDriver::Firefox::Service.driver_path&.call
49
39
  end
50
40
  end
51
41
 
@@ -3,16 +3,30 @@
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 [: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
+ if driver_type == :selenium
24
+ gem "selenium-webdriver", ">= 4.0.0"
14
25
  require "selenium/webdriver"
26
+ @browser = Browser.new(options[:using])
15
27
  @browser.preload
28
+ else
29
+ @browser = nil
16
30
  end
17
31
  end
18
32
 
@@ -24,27 +38,29 @@ module ActionDispatch
24
38
 
25
39
  private
26
40
  def registerable?
27
- [:selenium, :poltergeist, :webkit].include?(@name)
41
+ [:selenium, :poltergeist, :webkit, :cuprite, :rack_test].include?(@driver_type)
28
42
  end
29
43
 
30
44
  def register
31
- @browser.configure(&@capabilities)
45
+ @browser&.configure(&@capabilities)
32
46
 
33
- Capybara.register_driver @name do |app|
34
- case @name
47
+ Capybara.register_driver name do |app|
48
+ case @driver_type
35
49
  when :selenium then register_selenium(app)
36
50
  when :poltergeist then register_poltergeist(app)
37
51
  when :webkit then register_webkit(app)
52
+ when :cuprite then register_cuprite(app)
53
+ when :rack_test then register_rack_test(app)
38
54
  end
39
55
  end
40
56
  end
41
57
 
42
58
  def browser_options
43
- @options.merge(options: @browser.options).compact
59
+ @options.merge(capabilities: @browser.options).compact
44
60
  end
45
61
 
46
62
  def register_selenium(app)
47
- Capybara::Selenium::Driver.new(app, **{ browser: @browser.type }.merge(browser_options)).tap do |driver|
63
+ Capybara::Selenium::Driver.new(app, browser: @browser.type, **browser_options).tap do |driver|
48
64
  driver.browser.manage.window.size = Selenium::WebDriver::Dimension.new(*@screen_size)
49
65
  end
50
66
  end
@@ -59,8 +75,16 @@ module ActionDispatch
59
75
  end
60
76
  end
61
77
 
78
+ def register_cuprite(app)
79
+ Capybara::Cuprite::Driver.new(app, @options.merge(window_size: @screen_size))
80
+ end
81
+
82
+ def register_rack_test(app)
83
+ Capybara::RackTest::Driver.new(app, respect_data_method: true, **@options)
84
+ end
85
+
62
86
  def setup
63
- Capybara.current_driver = @name
87
+ Capybara.current_driver = name
64
88
  end
65
89
  end
66
90
  end