actionpack 7.2.1.1 → 8.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +108 -100
  3. data/lib/abstract_controller/helpers.rb +2 -0
  4. data/lib/abstract_controller/rendering.rb +0 -1
  5. data/lib/action_controller/api.rb +1 -0
  6. data/lib/action_controller/form_builder.rb +3 -3
  7. data/lib/action_controller/metal/allow_browser.rb +12 -2
  8. data/lib/action_controller/metal/conditional_get.rb +6 -3
  9. data/lib/action_controller/metal/http_authentication.rb +6 -3
  10. data/lib/action_controller/metal/instrumentation.rb +1 -2
  11. data/lib/action_controller/metal/live.rb +19 -8
  12. data/lib/action_controller/metal/rate_limiting.rb +13 -4
  13. data/lib/action_controller/metal/renderers.rb +2 -3
  14. data/lib/action_controller/metal/streaming.rb +5 -84
  15. data/lib/action_controller/metal/strong_parameters.rb +274 -88
  16. data/lib/action_controller/railtie.rb +1 -7
  17. data/lib/action_controller/test_case.rb +6 -5
  18. data/lib/action_dispatch/http/cache.rb +27 -10
  19. data/lib/action_dispatch/http/content_security_policy.rb +5 -8
  20. data/lib/action_dispatch/http/filter_parameters.rb +4 -9
  21. data/lib/action_dispatch/http/filter_redirect.rb +2 -9
  22. data/lib/action_dispatch/http/param_builder.rb +163 -0
  23. data/lib/action_dispatch/http/param_error.rb +26 -0
  24. data/lib/action_dispatch/http/permissions_policy.rb +2 -0
  25. data/lib/action_dispatch/http/query_parser.rb +31 -0
  26. data/lib/action_dispatch/http/request.rb +60 -16
  27. data/lib/action_dispatch/journey/parser.rb +99 -196
  28. data/lib/action_dispatch/journey/scanner.rb +40 -42
  29. data/lib/action_dispatch/middleware/cookies.rb +4 -2
  30. data/lib/action_dispatch/middleware/debug_exceptions.rb +16 -3
  31. data/lib/action_dispatch/middleware/debug_view.rb +0 -5
  32. data/lib/action_dispatch/middleware/exception_wrapper.rb +0 -6
  33. data/lib/action_dispatch/middleware/remote_ip.rb +5 -6
  34. data/lib/action_dispatch/middleware/request_id.rb +2 -1
  35. data/lib/action_dispatch/middleware/ssl.rb +14 -4
  36. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +0 -3
  37. data/lib/action_dispatch/railtie.rb +2 -0
  38. data/lib/action_dispatch/request/utils.rb +9 -3
  39. data/lib/action_dispatch/routing/inspector.rb +1 -1
  40. data/lib/action_dispatch/routing/mapper.rb +91 -62
  41. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -2
  42. data/lib/action_dispatch/routing/route_set.rb +20 -8
  43. data/lib/action_dispatch/system_testing/browser.rb +12 -21
  44. data/lib/action_dispatch/testing/assertions/response.rb +12 -2
  45. data/lib/action_dispatch/testing/assertions/routing.rb +4 -4
  46. data/lib/action_dispatch/testing/integration.rb +11 -1
  47. data/lib/action_dispatch.rb +6 -0
  48. data/lib/action_pack/gem_version.rb +4 -4
  49. metadata +15 -34
  50. data/lib/action_dispatch/journey/parser.y +0 -50
  51. data/lib/action_dispatch/journey/parser_extras.rb +0 -33
@@ -375,35 +375,18 @@ module ActionDispatch
375
375
  Routing::RouteSet::Dispatcher.new raise_on_name_error
376
376
  end
377
377
 
378
- if Thread.respond_to?(:each_caller_location)
379
- def route_source_location
380
- if Mapper.route_source_locations
381
- action_dispatch_dir = File.expand_path("..", __dir__)
382
- Thread.each_caller_location do |location|
383
- next if location.path.start_with?(action_dispatch_dir)
378
+ def route_source_location
379
+ if Mapper.route_source_locations
380
+ action_dispatch_dir = File.expand_path("..", __dir__)
381
+ Thread.each_caller_location do |location|
382
+ next if location.path.start_with?(action_dispatch_dir)
384
383
 
385
- cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
386
- next if cleaned_path.nil?
384
+ cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
385
+ next if cleaned_path.nil?
387
386
 
388
- return "#{cleaned_path}:#{location.lineno}"
389
- end
390
- nil
391
- end
392
- end
393
- else
394
- def route_source_location
395
- if Mapper.route_source_locations
396
- action_dispatch_dir = File.expand_path("..", __dir__)
397
- caller_locations.each do |location|
398
- next if location.path.start_with?(action_dispatch_dir)
399
-
400
- cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
401
- next if cleaned_path.nil?
402
-
403
- return "#{cleaned_path}:#{location.lineno}"
404
- end
405
- nil
387
+ return "#{cleaned_path}:#{location.lineno}"
406
388
  end
389
+ nil
407
390
  end
408
391
  end
409
392
  end
@@ -470,7 +453,6 @@ module ActionDispatch
470
453
  # When a pattern points to an internal route, the route's `:action` and
471
454
  # `:controller` should be set in options or hash shorthand. Examples:
472
455
  #
473
- # match 'photos/:id' => 'photos#show', via: :get
474
456
  # match 'photos/:id', to: 'photos#show', via: :get
475
457
  # match 'photos/:id', controller: 'photos', action: 'show', via: :get
476
458
  #
@@ -614,10 +596,6 @@ module ActionDispatch
614
596
  #
615
597
  # mount SomeRackApp, at: "some_route"
616
598
  #
617
- # Alternatively:
618
- #
619
- # mount(SomeRackApp => "some_route")
620
- #
621
599
  # For options, see `match`, as `mount` uses it internally.
622
600
  #
623
601
  # All mounted applications come with routing helpers to access them. These are
@@ -625,7 +603,7 @@ module ActionDispatch
625
603
  # `some_rack_app_path` or `some_rack_app_url`. To customize this helper's name,
626
604
  # use the `:as` option:
627
605
  #
628
- # mount(SomeRackApp => "some_route", as: "exciting")
606
+ # mount(SomeRackApp, at: "some_route", as: "exciting")
629
607
  #
630
608
  # This will generate the `exciting_path` and `exciting_url` helpers which can be
631
609
  # used to navigate to this mounted app.
@@ -773,6 +751,16 @@ module ActionDispatch
773
751
  map_method(:options, args, &block)
774
752
  end
775
753
 
754
+ # Define a route that recognizes HTTP CONNECT (and GET) requests. More
755
+ # specifically this recognizes HTTP/1 protocol upgrade requests and HTTP/2
756
+ # CONNECT requests with the protocol pseudo header. For supported arguments,
757
+ # see [match](rdoc-ref:Base#match)
758
+ #
759
+ # connect 'live', to: 'live#index'
760
+ def connect(*args, &block)
761
+ map_method([:get, :connect], args, &block)
762
+ end
763
+
776
764
  private
777
765
  def map_method(method, args, &block)
778
766
  options = args.extract_options!
@@ -1048,6 +1036,7 @@ module ActionDispatch
1048
1036
  end
1049
1037
 
1050
1038
  # Allows you to set default parameters for a route, such as this:
1039
+ #
1051
1040
  # defaults id: 'home' do
1052
1041
  # match 'scoped_pages/(:id)', to: 'pages#show'
1053
1042
  # end
@@ -1173,6 +1162,16 @@ module ActionDispatch
1173
1162
  CANONICAL_ACTIONS = %w(index create new show update destroy)
1174
1163
 
1175
1164
  class Resource # :nodoc:
1165
+ class << self
1166
+ def default_actions(api_only)
1167
+ if api_only
1168
+ [:index, :create, :show, :update, :destroy]
1169
+ else
1170
+ [:index, :create, :new, :show, :update, :destroy, :edit]
1171
+ end
1172
+ end
1173
+ end
1174
+
1176
1175
  attr_reader :controller, :path, :param
1177
1176
 
1178
1177
  def initialize(entities, api_only, shallow, options = {})
@@ -1180,6 +1179,11 @@ module ActionDispatch
1180
1179
  raise ArgumentError, ":param option can't contain colons"
1181
1180
  end
1182
1181
 
1182
+ valid_actions = self.class.default_actions(false) # ignore api_only for this validation
1183
+ if invalid_actions = invalid_only_except_options(options, valid_actions).presence
1184
+ raise ArgumentError, ":only and :except must include only #{valid_actions}, but also included #{invalid_actions}"
1185
+ end
1186
+
1183
1187
  @name = entities.to_s
1184
1188
  @path = (options[:path] || @name).to_s
1185
1189
  @controller = (options[:controller] || @name).to_s
@@ -1193,11 +1197,7 @@ module ActionDispatch
1193
1197
  end
1194
1198
 
1195
1199
  def default_actions
1196
- if @api_only
1197
- [:index, :create, :show, :update, :destroy]
1198
- else
1199
- [:index, :create, :new, :show, :update, :destroy, :edit]
1200
- end
1200
+ self.class.default_actions(@api_only)
1201
1201
  end
1202
1202
 
1203
1203
  def actions
@@ -1265,9 +1265,24 @@ module ActionDispatch
1265
1265
  end
1266
1266
 
1267
1267
  def singleton?; false; end
1268
+
1269
+ private
1270
+ def invalid_only_except_options(options, valid_actions)
1271
+ options.values_at(:only, :except).flatten.compact.uniq.map(&:to_sym) - valid_actions
1272
+ end
1268
1273
  end
1269
1274
 
1270
1275
  class SingletonResource < Resource # :nodoc:
1276
+ class << self
1277
+ def default_actions(api_only)
1278
+ if api_only
1279
+ [:show, :create, :update, :destroy]
1280
+ else
1281
+ [:show, :create, :update, :destroy, :new, :edit]
1282
+ end
1283
+ end
1284
+ end
1285
+
1271
1286
  def initialize(entities, api_only, shallow, options)
1272
1287
  super
1273
1288
  @as = nil
@@ -1276,11 +1291,7 @@ module ActionDispatch
1276
1291
  end
1277
1292
 
1278
1293
  def default_actions
1279
- if @api_only
1280
- [:show, :create, :update, :destroy]
1281
- else
1282
- [:show, :create, :update, :destroy, :new, :edit]
1283
- end
1294
+ self.class.default_actions(@api_only)
1284
1295
  end
1285
1296
 
1286
1297
  def plural
@@ -1341,7 +1352,7 @@ module ActionDispatch
1341
1352
  end
1342
1353
 
1343
1354
  with_scope_level(:resource) do
1344
- options = apply_action_options options
1355
+ options = apply_action_options :resource, options
1345
1356
  resource_scope(SingletonResource.new(resources.pop, api_only?, @scope[:shallow], options)) do
1346
1357
  yield if block_given?
1347
1358
 
@@ -1511,7 +1522,7 @@ module ActionDispatch
1511
1522
  end
1512
1523
 
1513
1524
  with_scope_level(:resources) do
1514
- options = apply_action_options options
1525
+ options = apply_action_options :resources, options
1515
1526
  resource_scope(Resource.new(resources.pop, api_only?, @scope[:shallow], options)) do
1516
1527
  yield if block_given?
1517
1528
 
@@ -1672,7 +1683,6 @@ module ActionDispatch
1672
1683
  # Matches a URL pattern to one or more routes. For more information, see
1673
1684
  # [match](rdoc-ref:Base#match).
1674
1685
  #
1675
- # match 'path' => 'controller#action', via: :patch
1676
1686
  # match 'path', to: 'controller#action', via: :post
1677
1687
  # match 'path', 'otherpath', on: :member, via: :get
1678
1688
  def match(path, *rest, &block)
@@ -1781,17 +1791,32 @@ module ActionDispatch
1781
1791
  false
1782
1792
  end
1783
1793
 
1784
- def apply_action_options(options)
1794
+ def apply_action_options(method, options)
1785
1795
  return options if action_options? options
1786
- options.merge scope_action_options
1796
+ options.merge scope_action_options(method)
1787
1797
  end
1788
1798
 
1789
1799
  def action_options?(options)
1790
1800
  options[:only] || options[:except]
1791
1801
  end
1792
1802
 
1793
- def scope_action_options
1794
- @scope[:action_options] || {}
1803
+ def scope_action_options(method)
1804
+ return {} unless @scope[:action_options]
1805
+
1806
+ actions = applicable_actions_for(method)
1807
+ @scope[:action_options].dup.tap do |options|
1808
+ (options[:only] = Array(options[:only]) & actions) if options[:only]
1809
+ (options[:except] = Array(options[:except]) & actions) if options[:except]
1810
+ end
1811
+ end
1812
+
1813
+ def applicable_actions_for(method)
1814
+ case method
1815
+ when :resource
1816
+ SingletonResource.default_actions(api_only?)
1817
+ when :resources
1818
+ Resource.default_actions(api_only?)
1819
+ end
1795
1820
  end
1796
1821
 
1797
1822
  def resource_scope?
@@ -1934,6 +1959,11 @@ module ActionDispatch
1934
1959
  end
1935
1960
 
1936
1961
  def map_match(paths, options)
1962
+ ActionDispatch.deprecator.warn(<<-MSG.squish) if paths.count > 1
1963
+ Mapping a route with multiple paths is deprecated and
1964
+ will be removed in Rails 8.1. Please use multiple method calls instead.
1965
+ MSG
1966
+
1937
1967
  if (on = options[:on]) && !VALID_ON_OPTIONS.include?(on)
1938
1968
  raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
1939
1969
  end
@@ -2024,7 +2054,7 @@ module ActionDispatch
2024
2054
  name_for_action(options.delete(:as), action)
2025
2055
  end
2026
2056
 
2027
- path = Mapping.normalize_path URI::DEFAULT_PARSER.escape(path), formatted
2057
+ path = Mapping.normalize_path URI::RFC2396_PARSER.escape(path), formatted
2028
2058
  ast = Journey::Parser.parse path
2029
2059
 
2030
2060
  mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
@@ -2199,8 +2229,8 @@ module ActionDispatch
2199
2229
  end
2200
2230
 
2201
2231
  # Define custom polymorphic mappings of models to URLs. This alters the behavior
2202
- # of `polymorphic_url` and consequently the behavior of `link_to` and `form_for`
2203
- # when passed a model instance, e.g:
2232
+ # of `polymorphic_url` and consequently the behavior of `link_to`, `form_with`
2233
+ # and `form_for` when passed a model instance, e.g:
2204
2234
  #
2205
2235
  # resource :basket
2206
2236
  #
@@ -2209,7 +2239,7 @@ module ActionDispatch
2209
2239
  # end
2210
2240
  #
2211
2241
  # This will now generate "/basket" when a `Basket` instance is passed to
2212
- # `link_to` or `form_for` instead of the standard "/baskets/:id".
2242
+ # `link_to`, `form_with` or `form_for` instead of the standard "/baskets/:id".
2213
2243
  #
2214
2244
  # NOTE: This custom behavior only applies to simple polymorphic URLs where a
2215
2245
  # single model instance is passed and not more complicated forms, e.g:
@@ -2266,9 +2296,9 @@ module ActionDispatch
2266
2296
 
2267
2297
  attr_reader :parent, :scope_level
2268
2298
 
2269
- def initialize(hash, parent = NULL, scope_level = nil)
2270
- @hash = hash
2299
+ def initialize(hash, parent = ROOT, scope_level = nil)
2271
2300
  @parent = parent
2301
+ @hash = parent ? parent.frame.merge(hash) : hash
2272
2302
  @scope_level = scope_level
2273
2303
  end
2274
2304
 
@@ -2281,7 +2311,7 @@ module ActionDispatch
2281
2311
  end
2282
2312
 
2283
2313
  def root?
2284
- @parent.null?
2314
+ @parent == ROOT
2285
2315
  end
2286
2316
 
2287
2317
  def resources?
@@ -2326,23 +2356,22 @@ module ActionDispatch
2326
2356
  end
2327
2357
 
2328
2358
  def [](key)
2329
- scope = find { |node| node.frame.key? key }
2330
- scope && scope.frame[key]
2359
+ frame[key]
2331
2360
  end
2332
2361
 
2362
+ def frame; @hash; end
2363
+
2333
2364
  include Enumerable
2334
2365
 
2335
2366
  def each
2336
2367
  node = self
2337
- until node.equal? NULL
2368
+ until node.equal? ROOT
2338
2369
  yield node
2339
2370
  node = node.parent
2340
2371
  end
2341
2372
  end
2342
2373
 
2343
- def frame; @hash; end
2344
-
2345
- NULL = Scope.new(nil, nil)
2374
+ ROOT = Scope.new({}, nil)
2346
2375
  end
2347
2376
 
2348
2377
  def initialize(set) # :nodoc:
@@ -31,7 +31,7 @@ module ActionDispatch
31
31
  # * `url_for`, so you can use it with a record as the argument, e.g.
32
32
  # `url_for(@article)`;
33
33
  # * ActionView::Helpers::FormHelper uses `polymorphic_path`, so you can write
34
- # `form_for(@article)` without having to specify `:url` parameter for the
34
+ # `form_with(model: @article)` without having to specify `:url` parameter for the
35
35
  # form action;
36
36
  # * `redirect_to` (which, in fact, uses `url_for`) so you can write
37
37
  # `redirect_to(post)` in your controllers;
@@ -61,7 +61,7 @@ module ActionDispatch
61
61
  # argument to the method. For example:
62
62
  #
63
63
  # polymorphic_url([blog, @post]) # calls blog.post_path(@post)
64
- # form_for([blog, @post]) # => "/blog/posts/1"
64
+ # form_with(model: [blog, @post]) # => "/blog/posts/1"
65
65
  #
66
66
  module PolymorphicRoutes
67
67
  # Constructs a call to a named RESTful route for the given record and returns
@@ -351,7 +351,7 @@ module ActionDispatch
351
351
  PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
352
352
  UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
353
353
 
354
- attr_accessor :formatter, :set, :named_routes, :default_scope, :router
354
+ attr_accessor :formatter, :set, :named_routes, :router
355
355
  attr_accessor :disable_clear_and_finalize, :resources_path_names
356
356
  attr_accessor :default_url_options, :draw_paths
357
357
  attr_reader :env_key, :polymorphic_mappings
@@ -363,7 +363,7 @@ module ActionDispatch
363
363
  end
364
364
 
365
365
  def self.new_with_config(config)
366
- route_set_config = DEFAULT_CONFIG
366
+ route_set_config = DEFAULT_CONFIG.dup
367
367
 
368
368
  # engines apparently don't have this set
369
369
  if config.respond_to? :relative_url_root
@@ -374,14 +374,18 @@ module ActionDispatch
374
374
  route_set_config.api_only = config.api_only
375
375
  end
376
376
 
377
+ if config.respond_to? :default_scope
378
+ route_set_config.default_scope = config.default_scope
379
+ end
380
+
377
381
  new route_set_config
378
382
  end
379
383
 
380
- Config = Struct.new :relative_url_root, :api_only
384
+ Config = Struct.new :relative_url_root, :api_only, :default_scope
381
385
 
382
- DEFAULT_CONFIG = Config.new(nil, false)
386
+ DEFAULT_CONFIG = Config.new(nil, false, nil)
383
387
 
384
- def initialize(config = DEFAULT_CONFIG)
388
+ def initialize(config = DEFAULT_CONFIG.dup)
385
389
  self.named_routes = NamedRouteCollection.new
386
390
  self.resources_path_names = self.class.default_resources_path_names
387
391
  self.default_url_options = {}
@@ -416,6 +420,14 @@ module ActionDispatch
416
420
  @config.api_only
417
421
  end
418
422
 
423
+ def default_scope
424
+ @config.default_scope
425
+ end
426
+
427
+ def default_scope=(new_default_scope)
428
+ @config.default_scope = new_default_scope
429
+ end
430
+
419
431
  def request_class
420
432
  ActionDispatch::Request
421
433
  end
@@ -647,14 +659,14 @@ module ActionDispatch
647
659
  if route.segment_keys.include?(:controller)
648
660
  ActionDispatch.deprecator.warn(<<-MSG.squish)
649
661
  Using a dynamic :controller segment in a route is deprecated and
650
- will be removed in Rails 7.2.
662
+ will be removed in Rails 8.1.
651
663
  MSG
652
664
  end
653
665
 
654
666
  if route.segment_keys.include?(:action)
655
667
  ActionDispatch.deprecator.warn(<<-MSG.squish)
656
668
  Using a dynamic :action segment in a route is deprecated and
657
- will be removed in Rails 7.2.
669
+ will be removed in Rails 8.1.
658
670
  MSG
659
671
  end
660
672
 
@@ -917,7 +929,7 @@ module ActionDispatch
917
929
  params.each do |key, value|
918
930
  if value.is_a?(String)
919
931
  value = value.dup.force_encoding(Encoding::BINARY)
920
- params[key] = URI::DEFAULT_PARSER.unescape(value)
932
+ params[key] = URI::RFC2396_PARSER.unescape(value)
921
933
  end
922
934
  end
923
935
  req.path_parameters = params
@@ -9,7 +9,6 @@ module ActionDispatch
9
9
 
10
10
  def initialize(name)
11
11
  @name = name
12
- set_default_options
13
12
  end
14
13
 
15
14
  def type
@@ -27,9 +26,9 @@ module ActionDispatch
27
26
  @options ||=
28
27
  case type
29
28
  when :chrome
30
- ::Selenium::WebDriver::Chrome::Options.new
29
+ default_chrome_options
31
30
  when :firefox
32
- ::Selenium::WebDriver::Firefox::Options.new
31
+ default_firefox_options
33
32
  end
34
33
  end
35
34
 
@@ -49,26 +48,18 @@ module ActionDispatch
49
48
  end
50
49
 
51
50
  private
52
- def set_default_options
53
- case name
54
- when :headless_chrome
55
- set_headless_chrome_browser_options
56
- when :headless_firefox
57
- set_headless_firefox_browser_options
58
- end
51
+ def default_chrome_options
52
+ options = ::Selenium::WebDriver::Chrome::Options.new
53
+ options.add_argument("--disable-search-engine-choice-screen")
54
+ options.add_argument("--headless") if name == :headless_chrome
55
+ options.add_argument("--disable-gpu") if Gem.win_platform?
56
+ options
59
57
  end
60
58
 
61
- def set_headless_chrome_browser_options
62
- configure do |capabilities|
63
- capabilities.add_argument("--headless")
64
- capabilities.add_argument("--disable-gpu") if Gem.win_platform?
65
- end
66
- end
67
-
68
- def set_headless_firefox_browser_options
69
- configure do |capabilities|
70
- capabilities.add_argument("-headless")
71
- end
59
+ def default_firefox_options
60
+ options = ::Selenium::WebDriver::Firefox::Options.new
61
+ options.add_argument("-headless") if name == :headless_firefox
62
+ options
72
63
  end
73
64
 
74
65
  def resolve_driver_path(namespace)
@@ -87,8 +87,13 @@ module ActionDispatch
87
87
  end
88
88
 
89
89
  def generate_response_message(expected, actual = @response.response_code)
90
- (+"Expected response to be a <#{code_with_name(expected)}>,"\
91
- " but was a <#{code_with_name(actual)}>").concat(location_if_redirected).concat(response_body_if_short)
90
+ lambda do
91
+ (+"Expected response to be a <#{code_with_name(expected)}>,"\
92
+ " but was a <#{code_with_name(actual)}>").
93
+ concat(location_if_redirected).
94
+ concat(exception_if_present).
95
+ concat(response_body_if_short)
96
+ end
92
97
  end
93
98
 
94
99
  def response_body_if_short
@@ -96,6 +101,11 @@ module ActionDispatch
96
101
  "\nResponse body: #{@response.body}"
97
102
  end
98
103
 
104
+ def exception_if_present
105
+ return "" unless ex = @request&.env&.[]("action_dispatch.exception")
106
+ "\n\nException while processing request: #{Minitest::UnexpectedError.new(ex).message}\n"
107
+ end
108
+
99
109
  def location_if_redirected
100
110
  return "" unless @response.redirection? && @response.location.present?
101
111
  location = normalize_argument_to_redirection(@response.location)
@@ -118,9 +118,9 @@ module ActionDispatch
118
118
  # assert_equal "/users", users_path
119
119
  # end
120
120
  #
121
- def with_routing(&block)
121
+ def with_routing(config = nil, &block)
122
122
  old_routes, old_controller = @routes, @controller
123
- create_routes(&block)
123
+ create_routes(config, &block)
124
124
  ensure
125
125
  reset_routes(old_routes, old_controller)
126
126
  end
@@ -267,8 +267,8 @@ module ActionDispatch
267
267
  end
268
268
 
269
269
  private
270
- def create_routes
271
- @routes = ActionDispatch::Routing::RouteSet.new
270
+ def create_routes(config = nil)
271
+ @routes = ActionDispatch::Routing::RouteSet.new(config || ActionDispatch::Routing::RouteSet::DEFAULT_CONFIG)
272
272
  if @controller
273
273
  @controller = @controller.clone
274
274
  _routes = @routes
@@ -284,7 +284,17 @@ module ActionDispatch
284
284
 
285
285
  # NOTE: rack-test v0.5 doesn't build a default uri correctly Make sure requested
286
286
  # path is always a full URI.
287
- session.request(build_full_uri(path, request_env), request_env)
287
+ uri = build_full_uri(path, request_env)
288
+
289
+ if method == :get && String === request_env[:params]
290
+ # rack-test will needlessly parse and rebuild a :params
291
+ # querystring, using Rack's query parser. At best that's a
292
+ # waste of time; at worst it can change the value.
293
+
294
+ uri << "?" << request_env.delete(:params)
295
+ end
296
+
297
+ session.request(uri, request_env)
288
298
 
289
299
  @request_count += 1
290
300
  @request = ActionDispatch::Request.new(session.last_request.env)
@@ -53,7 +53,13 @@ module ActionDispatch
53
53
  eager_autoload do
54
54
  autoload_under "http" do
55
55
  autoload :ContentSecurityPolicy
56
+ autoload :InvalidParameterError, "action_dispatch/http/param_error"
57
+ autoload :ParamBuilder
58
+ autoload :ParamError
59
+ autoload :ParameterTypeError, "action_dispatch/http/param_error"
60
+ autoload :ParamsTooDeepError, "action_dispatch/http/param_error"
56
61
  autoload :PermissionsPolicy
62
+ autoload :QueryParser
57
63
  autoload :Request
58
64
  autoload :Response
59
65
  end
@@ -9,10 +9,10 @@ module ActionPack
9
9
  end
10
10
 
11
11
  module VERSION
12
- MAJOR = 7
13
- MINOR = 2
14
- TINY = 1
15
- PRE = "1"
12
+ MAJOR = 8
13
+ MINOR = 0
14
+ TINY = 0
15
+ PRE = "rc1"
16
16
 
17
17
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
18
18
  end