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
@@ -27,28 +27,13 @@ module ActionController
27
27
  super("param is missing or the value is empty: #{param}")
28
28
  end
29
29
 
30
- class Correction
31
- def initialize(error)
32
- @error = error
33
- end
34
-
35
- def corrections
36
- if @error.param && @error.keys
37
- maybe_these = @error.keys
30
+ if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
31
+ include DidYouMean::Correctable # :nodoc:
38
32
 
39
- maybe_these.sort_by { |n|
40
- DidYouMean::Jaro.distance(@error.param.to_s, n)
41
- }.reverse.first(4)
42
- else
43
- []
44
- end
33
+ def corrections # :nodoc:
34
+ @corrections ||= DidYouMean::SpellChecker.new(dictionary: keys).correct(param.to_s)
45
35
  end
46
36
  end
47
-
48
- # We may not have DYM, and DYM might not let us register error handlers
49
- if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
50
- DidYouMean.correct_error(self, Correction)
51
- end
52
37
  end
53
38
 
54
39
  # Raised when a supplied parameter is not expected and
@@ -96,7 +81,7 @@ module ActionController
96
81
  # })
97
82
  #
98
83
  # permitted = params.require(:person).permit(:name, :age)
99
- # permitted # => <ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true>
84
+ # permitted # => #<ActionController::Parameters {"name"=>"Francesco", "age"=>22} permitted: true>
100
85
  # permitted.permitted? # => true
101
86
  #
102
87
  # Person.first.update!(permitted)
@@ -106,11 +91,13 @@ module ActionController
106
91
  #
107
92
  # * +permit_all_parameters+ - If it's +true+, all the parameters will be
108
93
  # permitted by default. The default is +false+.
109
- # * +action_on_unpermitted_parameters+ - Allow to control the behavior when parameters
110
- # that are not explicitly permitted are found. The values can be +false+ to just filter them
111
- # out, <tt>:log</tt> to additionally write a message on the logger, or <tt>:raise</tt> to raise
112
- # ActionController::UnpermittedParameters exception. The default value is <tt>:log</tt>
113
- # in test and development environments, +false+ otherwise.
94
+ # * +action_on_unpermitted_parameters+ - Controls behavior when parameters that are not explicitly
95
+ # permitted are found. The default value is <tt>:log</tt> in test and development environments,
96
+ # +false+ otherwise. The values can be:
97
+ # * +false+ to take no action.
98
+ # * <tt>:log</tt> to emit an <tt>ActiveSupport::Notifications.instrument</tt> event on the
99
+ # <tt>unpermitted_parameters.action_controller</tt> topic and log at the DEBUG level.
100
+ # * <tt>:raise</tt> to raise a <tt>ActionController::UnpermittedParameters</tt> exception.
114
101
  #
115
102
  # Examples:
116
103
  #
@@ -124,7 +111,7 @@ module ActionController
124
111
  #
125
112
  # params = ActionController::Parameters.new(a: "123", b: "456")
126
113
  # params.permit(:c)
127
- # # => <ActionController::Parameters {} permitted: true>
114
+ # # => #<ActionController::Parameters {} permitted: true>
128
115
  #
129
116
  # ActionController::Parameters.action_on_unpermitted_parameters = :raise
130
117
  #
@@ -249,7 +236,7 @@ module ActionController
249
236
  # By default, never raise an UnpermittedParameters exception if these
250
237
  # params are present. The default includes both 'controller' and 'action'
251
238
  # because they are added by Rails and should be of no concern. One way
252
- # to change these is to specify `always_permitted_parameters` in your
239
+ # to change these is to specify +always_permitted_parameters+ in your
253
240
  # config. For instance:
254
241
  #
255
242
  # config.action_controller.always_permitted_parameters = %w( controller action format )
@@ -277,8 +264,9 @@ module ActionController
277
264
  # params = ActionController::Parameters.new(name: "Francesco")
278
265
  # params.permitted? # => true
279
266
  # Person.new(params) # => #<Person id: nil, name: "Francesco">
280
- def initialize(parameters = {})
267
+ def initialize(parameters = {}, logging_context = {})
281
268
  @parameters = parameters.with_indifferent_access
269
+ @logging_context = logging_context
282
270
  @permitted = self.class.permit_all_parameters
283
271
  end
284
272
 
@@ -291,10 +279,15 @@ module ActionController
291
279
  @parameters == other
292
280
  end
293
281
  end
294
- alias eql? ==
282
+
283
+ def eql?(other)
284
+ self.class == other.class &&
285
+ permitted? == other.permitted? &&
286
+ parameters.eql?(other.parameters)
287
+ end
295
288
 
296
289
  def hash
297
- [@parameters.hash, @permitted].hash
290
+ [self.class, @parameters, @permitted].hash
298
291
  end
299
292
 
300
293
  # Returns a safe <tt>ActiveSupport::HashWithIndifferentAccess</tt>
@@ -454,7 +447,7 @@ module ActionController
454
447
  # either present or the singleton +false+, returns said value:
455
448
  #
456
449
  # ActionController::Parameters.new(person: { name: "Francesco" }).require(:person)
457
- # # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
450
+ # # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
458
451
  #
459
452
  # Otherwise raises <tt>ActionController::ParameterMissing</tt>:
460
453
  #
@@ -582,13 +575,48 @@ module ActionController
582
575
  # })
583
576
  #
584
577
  # params.require(:person).permit(:contact)
585
- # # => <ActionController::Parameters {} permitted: true>
578
+ # # => #<ActionController::Parameters {} permitted: true>
586
579
  #
587
580
  # params.require(:person).permit(contact: :phone)
588
- # # => <ActionController::Parameters {"contact"=><ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true>
581
+ # # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"phone"=>"555-1234"} permitted: true>} permitted: true>
589
582
  #
590
583
  # params.require(:person).permit(contact: [ :email, :phone ])
591
- # # => <ActionController::Parameters {"contact"=><ActionController::Parameters {"email"=>"none@test.com", "phone"=>"555-1234"} permitted: true>} permitted: true>
584
+ # # => #<ActionController::Parameters {"contact"=>#<ActionController::Parameters {"email"=>"none@test.com", "phone"=>"555-1234"} permitted: true>} permitted: true>
585
+ #
586
+ # If your parameters specify multiple parameters indexed by a number,
587
+ # you can permit each set of parameters under the numeric key to be the same using the same syntax as permitting a single item.
588
+ #
589
+ # params = ActionController::Parameters.new({
590
+ # person: {
591
+ # '0': {
592
+ # email: "none@test.com",
593
+ # phone: "555-1234"
594
+ # },
595
+ # '1': {
596
+ # email: "nothing@test.com",
597
+ # phone: "555-6789"
598
+ # },
599
+ # }
600
+ # })
601
+ # params.permit(person: [:email]).to_h
602
+ # # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"email"=>"nothing@test.com"}}}
603
+ #
604
+ # If you want to specify what keys you want from each numeric key, you can instead specify each one individually
605
+ #
606
+ # params = ActionController::Parameters.new({
607
+ # person: {
608
+ # '0': {
609
+ # email: "none@test.com",
610
+ # phone: "555-1234"
611
+ # },
612
+ # '1': {
613
+ # email: "nothing@test.com",
614
+ # phone: "555-6789"
615
+ # },
616
+ # }
617
+ # })
618
+ # params.permit(person: { '0': [:email], '1': [:phone]}).to_h
619
+ # # => {"person"=>{"0"=>{"email"=>"none@test.com"}, "1"=>{"phone"=>"555-6789"}}}
592
620
  def permit(*filters)
593
621
  params = self.class.new
594
622
 
@@ -610,7 +638,7 @@ module ActionController
610
638
  # returns +nil+.
611
639
  #
612
640
  # params = ActionController::Parameters.new(person: { name: "Francesco" })
613
- # params[:person] # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
641
+ # params[:person] # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
614
642
  # params[:none] # => nil
615
643
  def [](key)
616
644
  convert_hashes_to_parameters(key, @parameters[key])
@@ -630,9 +658,9 @@ module ActionController
630
658
  # is given, then that will be run and its result returned.
631
659
  #
632
660
  # params = ActionController::Parameters.new(person: { name: "Francesco" })
633
- # params.fetch(:person) # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
661
+ # params.fetch(:person) # => #<ActionController::Parameters {"name"=>"Francesco"} permitted: false>
634
662
  # params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
635
- # params.fetch(:none, {}) # => <ActionController::Parameters {} permitted: false>
663
+ # params.fetch(:none, {}) # => #<ActionController::Parameters {} permitted: false>
636
664
  # params.fetch(:none, "Francesco") # => "Francesco"
637
665
  # params.fetch(:none) { "Francesco" } # => "Francesco"
638
666
  def fetch(key, *args)
@@ -666,8 +694,8 @@ module ActionController
666
694
  # don't exist, returns an empty hash.
667
695
  #
668
696
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
669
- # params.slice(:a, :b) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
670
- # params.slice(:d) # => <ActionController::Parameters {} permitted: false>
697
+ # params.slice(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
698
+ # params.slice(:d) # => #<ActionController::Parameters {} permitted: false>
671
699
  def slice(*keys)
672
700
  new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
673
701
  end
@@ -683,8 +711,8 @@ module ActionController
683
711
  # filters out the given +keys+.
684
712
  #
685
713
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
686
- # params.except(:a, :b) # => <ActionController::Parameters {"c"=>3} permitted: false>
687
- # params.except(:d) # => <ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false>
714
+ # params.except(:a, :b) # => #<ActionController::Parameters {"c"=>3} permitted: false>
715
+ # params.except(:d) # => #<ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false>
688
716
  def except(*keys)
689
717
  new_instance_with_inherited_permitted_status(@parameters.except(*keys))
690
718
  end
@@ -692,8 +720,8 @@ module ActionController
692
720
  # Removes and returns the key/value pairs matching the given keys.
693
721
  #
694
722
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
695
- # params.extract!(:a, :b) # => <ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
696
- # params # => <ActionController::Parameters {"c"=>3} permitted: false>
723
+ # params.extract!(:a, :b) # => #<ActionController::Parameters {"a"=>1, "b"=>2} permitted: false>
724
+ # params # => #<ActionController::Parameters {"c"=>3} permitted: false>
697
725
  def extract!(*keys)
698
726
  new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
699
727
  end
@@ -703,7 +731,7 @@ module ActionController
703
731
  #
704
732
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
705
733
  # params.transform_values { |x| x * 2 }
706
- # # => <ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
734
+ # # => #<ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
707
735
  def transform_values
708
736
  return to_enum(:transform_values) unless block_given?
709
737
  new_instance_with_inherited_permitted_status(
@@ -755,7 +783,7 @@ module ActionController
755
783
 
756
784
  # Deletes a key-value pair from +Parameters+ and returns the value. If
757
785
  # +key+ is not found, returns +nil+ (or, with optional code block, yields
758
- # +key+ and returns the result). Cf. +#extract!+, which returns the
786
+ # +key+ and returns the result). Cf. #extract!, which returns the
759
787
  # corresponding +ActionController::Parameters+ object.
760
788
  def delete(key, &block)
761
789
  convert_value_to_parameters(@parameters.delete(key, &block))
@@ -885,9 +913,13 @@ module ActionController
885
913
  end
886
914
  end
887
915
 
916
+ def encode_with(coder) # :nodoc:
917
+ coder.map = { "parameters" => @parameters, "permitted" => @permitted }
918
+ end
919
+
888
920
  # Returns duplicate of object including all parameters.
889
921
  def deep_dup
890
- self.class.new(@parameters.deep_dup).tap do |duplicate|
922
+ self.class.new(@parameters.deep_dup, @logging_context).tap do |duplicate|
891
923
  duplicate.permitted = @permitted
892
924
  end
893
925
  end
@@ -909,7 +941,7 @@ module ActionController
909
941
 
910
942
  private
911
943
  def new_instance_with_inherited_permitted_status(hash)
912
- self.class.new(hash).tap do |new_instance|
944
+ self.class.new(hash, @logging_context).tap do |new_instance|
913
945
  new_instance.permitted = @permitted
914
946
  end
915
947
  end
@@ -943,18 +975,24 @@ module ActionController
943
975
  converted_arrays << converted.dup
944
976
  converted
945
977
  when Hash
946
- self.class.new(value)
978
+ self.class.new(value, @logging_context)
947
979
  else
948
980
  value
949
981
  end
950
982
  end
951
983
 
952
- def each_element(object, &block)
984
+ def specify_numeric_keys?(filter)
985
+ if filter.respond_to?(:keys)
986
+ filter.keys.any? { |key| /\A-?\d+\z/.match?(key) }
987
+ end
988
+ end
989
+
990
+ def each_element(object, filter, &block)
953
991
  case object
954
992
  when Array
955
- object.grep(Parameters).map { |el| yield el }.compact
993
+ object.grep(Parameters).filter_map(&block)
956
994
  when Parameters
957
- if object.nested_attributes?
995
+ if object.nested_attributes? && !specify_numeric_keys?(filter)
958
996
  object.each_nested_attribute(&block)
959
997
  else
960
998
  yield object
@@ -968,7 +1006,7 @@ module ActionController
968
1006
  case self.class.action_on_unpermitted_parameters
969
1007
  when :log
970
1008
  name = "unpermitted_parameters.action_controller"
971
- ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys)
1009
+ ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys, context: @logging_context)
972
1010
  when :raise
973
1011
  raise ActionController::UnpermittedParameters.new(unpermitted_keys)
974
1012
  end
@@ -1067,7 +1105,7 @@ module ActionController
1067
1105
  end
1068
1106
  elsif non_scalar?(value)
1069
1107
  # Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
1070
- params[key] = each_element(value) do |element|
1108
+ params[key] = each_element(value, filter[key]) do |element|
1071
1109
  element.permit(*Array.wrap(filter[key]))
1072
1110
  end
1073
1111
  end
@@ -1184,7 +1222,15 @@ module ActionController
1184
1222
  # Returns a new ActionController::Parameters object that
1185
1223
  # has been instantiated with the <tt>request.parameters</tt>.
1186
1224
  def params
1187
- @_params ||= Parameters.new(request.parameters)
1225
+ @_params ||= begin
1226
+ context = {
1227
+ controller: self.class.name,
1228
+ action: action_name,
1229
+ request: request,
1230
+ params: request.filtered_parameters
1231
+ }
1232
+ Parameters.new(request.parameters, context)
1233
+ end
1188
1234
  end
1189
1235
 
1190
1236
  # Assigns the given +value+ to the +params+ hash. If +value+
@@ -2,10 +2,17 @@
2
2
 
3
3
  module ActionController
4
4
  module Testing
5
- extend ActiveSupport::Concern
6
-
7
5
  # Behavior specific to functional tests
8
6
  module Functional # :nodoc:
7
+ def clear_instance_variables_between_requests
8
+ if defined?(@_ivars)
9
+ new_ivars = instance_variables - @_ivars
10
+ new_ivars.each { |ivar| remove_instance_variable(ivar) }
11
+ end
12
+
13
+ @_ivars = instance_variables
14
+ end
15
+
9
16
  def recycle!
10
17
  @_url_options = nil
11
18
  self.formats = nil
@@ -4,11 +4,11 @@ module ActionController
4
4
  # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
5
5
  # the <tt>_routes</tt> method. Otherwise, an exception will be raised.
6
6
  #
7
- # In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
7
+ # In addition to AbstractController::UrlFor, this module accesses the HTTP layer to define
8
8
  # URL options like the +host+. In order to do so, this module requires the host class
9
9
  # to implement +env+ which needs to be Rack-compatible and +request+
10
- # which is either an instance of +ActionDispatch::Request+ or an object
11
- # that responds to the +host+, +optional_port+, +protocol+ and
10
+ # which is either an instance of ActionDispatch::Request or an object
11
+ # that responds to the +host+, +optional_port+, +protocol+, and
12
12
  # +symbolized_path_parameter+ methods.
13
13
  #
14
14
  # class RootUrl
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "active_support/core_ext/array/extract_options"
4
4
  require "action_dispatch/middleware/stack"
5
- require "action_dispatch/http/request"
6
- require "action_dispatch/http/response"
7
5
 
8
6
  module ActionController
9
7
  # Extend ActionDispatch middleware stack to make it aware of options
@@ -13,8 +11,8 @@ module ActionController
13
11
  # use AuthenticationMiddleware, except: [:index, :show]
14
12
  # end
15
13
  #
16
- class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
17
- class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
14
+ class MiddlewareStack < ActionDispatch::MiddlewareStack # :nodoc:
15
+ class Middleware < ActionDispatch::MiddlewareStack::Middleware # :nodoc:
18
16
  def initialize(klass, args, actions, strategy, block)
19
17
  @actions = actions
20
18
  @strategy = strategy
@@ -62,7 +60,7 @@ module ActionController
62
60
 
63
61
  # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
64
62
  # valid Rack interface without the additional niceties provided by
65
- # <tt>ActionController::Base</tt>.
63
+ # ActionController::Base.
66
64
  #
67
65
  # A sample metal controller might look like this:
68
66
  #
@@ -113,7 +111,7 @@ module ActionController
113
111
  #
114
112
  # == Other Helpers
115
113
  #
116
- # You can refer to the modules included in <tt>ActionController::Base</tt> to see
114
+ # You can refer to the modules included in ActionController::Base to see
117
115
  # other features you can bring into your metal controller.
118
116
  #
119
117
  class Metal < AbstractController::Base
@@ -139,7 +137,7 @@ module ActionController
139
137
  false
140
138
  end
141
139
 
142
- # Delegates to the class' <tt>controller_name</tt>.
140
+ # Delegates to the class's ::controller_name.
143
141
  def controller_name
144
142
  self.class.controller_name
145
143
  end
@@ -184,7 +182,7 @@ module ActionController
184
182
  response_body || response.committed?
185
183
  end
186
184
 
187
- def dispatch(name, request, response) #:nodoc:
185
+ def dispatch(name, request, response) # :nodoc:
188
186
  set_request!(request)
189
187
  set_response!(response)
190
188
  process(name)
@@ -196,12 +194,12 @@ module ActionController
196
194
  @_response = response
197
195
  end
198
196
 
199
- def set_request!(request) #:nodoc:
197
+ def set_request!(request) # :nodoc:
200
198
  @_request = request
201
199
  @_request.controller_instance = self
202
200
  end
203
201
 
204
- def to_a #:nodoc:
202
+ def to_a # :nodoc:
205
203
  response.to_a
206
204
  end
207
205
 
@@ -219,10 +217,9 @@ module ActionController
219
217
  class << self
220
218
  # Pushes the given Rack middleware and its arguments to the bottom of the
221
219
  # middleware stack.
222
- def use(*args, &block)
223
- middleware_stack.use(*args, &block)
220
+ def use(...)
221
+ middleware_stack.use(...)
224
222
  end
225
- ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
226
223
  end
227
224
 
228
225
  # Alias for +middleware_stack+.
@@ -8,8 +8,11 @@ require "action_controller/railties/helpers"
8
8
  require "action_view/railtie"
9
9
 
10
10
  module ActionController
11
- class Railtie < Rails::Railtie #:nodoc:
11
+ class Railtie < Rails::Railtie # :nodoc:
12
12
  config.action_controller = ActiveSupport::OrderedOptions.new
13
+ config.action_controller.raise_on_open_redirects = false
14
+ config.action_controller.log_query_tags_around_actions = true
15
+ config.action_controller.wrap_parameters_by_default = false
13
16
 
14
17
  config.eager_load_namespaces << ActionController
15
18
 
@@ -25,14 +28,19 @@ module ActionController
25
28
  options = app.config.action_controller
26
29
 
27
30
  ActiveSupport.on_load(:action_controller, run_once: true) do
28
- ActionController::Parameters.permit_all_parameters = options.delete(:permit_all_parameters) { false }
31
+ ActionController::Parameters.permit_all_parameters = options.permit_all_parameters || false
29
32
  if app.config.action_controller[:always_permitted_parameters]
30
33
  ActionController::Parameters.always_permitted_parameters =
31
- app.config.action_controller.delete(:always_permitted_parameters)
34
+ app.config.action_controller.always_permitted_parameters
32
35
  end
33
- ActionController::Parameters.action_on_unpermitted_parameters = options.delete(:action_on_unpermitted_parameters) do
34
- (Rails.env.test? || Rails.env.development?) ? :log : false
36
+
37
+ action_on_unpermitted_parameters = options.action_on_unpermitted_parameters
38
+
39
+ if action_on_unpermitted_parameters.nil?
40
+ action_on_unpermitted_parameters = (Rails.env.test? || Rails.env.development?) ? :log : false
35
41
  end
42
+
43
+ ActionController::Parameters.action_on_unpermitted_parameters = action_on_unpermitted_parameters
36
44
  end
37
45
  end
38
46
 
@@ -55,7 +63,18 @@ module ActionController
55
63
  extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
56
64
  extend ::ActionController::Railties::Helpers
57
65
 
58
- options.each do |k, v|
66
+ wrap_parameters format: [:json] if options.wrap_parameters_by_default && respond_to?(:wrap_parameters)
67
+
68
+ # Configs used in other initializers
69
+ filtered_options = options.except(
70
+ :log_query_tags_around_actions,
71
+ :permit_all_parameters,
72
+ :action_on_unpermitted_parameters,
73
+ :always_permitted_parameters,
74
+ :wrap_parameters_by_default
75
+ )
76
+
77
+ filtered_options.each do |k, v|
59
78
  k = "#{k}="
60
79
  if respond_to?(k)
61
80
  send(k, v)
@@ -85,5 +104,29 @@ module ActionController
85
104
  ActionController::Metal.descendants.each(&:action_methods) if config.eager_load
86
105
  end
87
106
  end
107
+
108
+ initializer "action_controller.query_log_tags" do |app|
109
+ query_logs_tags_enabled = app.config.respond_to?(:active_record) &&
110
+ app.config.active_record.query_log_tags_enabled &&
111
+ app.config.action_controller.log_query_tags_around_actions
112
+
113
+ if query_logs_tags_enabled
114
+ app.config.active_record.query_log_tags += [:controller, :action]
115
+
116
+ ActiveSupport.on_load(:active_record) do
117
+ ActiveRecord::QueryLogs.taggings.merge!(
118
+ controller: ->(context) { context[:controller]&.controller_name },
119
+ action: ->(context) { context[:controller]&.action_name },
120
+ namespaced_controller: ->(context) { context[:controller].class.name if context[:controller] }
121
+ )
122
+ end
123
+ end
124
+ end
125
+
126
+ initializer "action_controller.test_case" do |app|
127
+ ActiveSupport.on_load(:action_controller_test_case) do
128
+ ActionController::TestCase.executor_around_each_request = app.config.active_support.executor_around_test_case
129
+ end
130
+ end
88
131
  end
89
132
  end
@@ -71,7 +71,7 @@ module ActionController
71
71
  # Render templates with any options from ActionController::Base#render_to_string.
72
72
  #
73
73
  # The primary options are:
74
- # * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt> for details.
74
+ # * <tt>:partial</tt> - See ActionView::PartialRenderer for details.
75
75
  # * <tt>:file</tt> - Renders an explicit template file. Add <tt>:locals</tt> to pass in, if so desired.
76
76
  # It shouldn’t be used directly with unsanitized user input due to lack of validation.
77
77
  # * <tt>:inline</tt> - Renders an ERB template string.
@@ -31,7 +31,7 @@ module ActionController
31
31
 
32
32
  # ActionController::TestCase will be deprecated and moved to a gem in the future.
33
33
  # Please use ActionDispatch::IntegrationTest going forward.
34
- class TestRequest < ActionDispatch::TestRequest #:nodoc:
34
+ class TestRequest < ActionDispatch::TestRequest # :nodoc:
35
35
  DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
36
36
  DEFAULT_ENV.delete "PATH_INFO"
37
37
 
@@ -179,7 +179,7 @@ module ActionController
179
179
 
180
180
  # Methods #destroy and #load! are overridden to avoid calling methods on the
181
181
  # @store object, which does not exist for the TestSession class.
182
- class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash #:nodoc:
182
+ class TestSession < Rack::Session::Abstract::PersistedSecure::SecureSessionHash # :nodoc:
183
183
  DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
184
184
 
185
185
  def initialize(session = {})
@@ -214,6 +214,10 @@ module ActionController
214
214
  @data.fetch(key.to_s, *args, &block)
215
215
  end
216
216
 
217
+ def enabled?
218
+ true
219
+ end
220
+
217
221
  private
218
222
  def load!
219
223
  @id
@@ -237,7 +241,7 @@ module ActionController
237
241
  # == Basic example
238
242
  #
239
243
  # Functional tests are written as follows:
240
- # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
244
+ # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+, or +head+ method to simulate
241
245
  # an HTTP request.
242
246
  # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
243
247
  # the controller's HTTP response, the database contents, etc.
@@ -329,6 +333,8 @@ module ActionController
329
333
  #
330
334
  # assert_redirected_to page_url(title: 'foo')
331
335
  class TestCase < ActiveSupport::TestCase
336
+ singleton_class.attr_accessor :executor_around_each_request
337
+
332
338
  module Behavior
333
339
  extend ActiveSupport::Concern
334
340
  include ActionDispatch::TestProcess
@@ -385,7 +391,7 @@ module ActionController
385
391
  #
386
392
  # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
387
393
  # +post+, +patch+, +put+, +delete+, and +head+.
388
- # Example sending parameters, session and setting a flash message:
394
+ # Example sending parameters, session, and setting a flash message:
389
395
  #
390
396
  # get :show,
391
397
  # params: { id: 7 },
@@ -455,13 +461,19 @@ module ActionController
455
461
  # session: { user_id: 1 },
456
462
  # flash: { notice: 'This is flash message' }
457
463
  #
458
- # To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
464
+ # To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, and +HEAD+ requests
459
465
  # prefer using #get, #post, #patch, #put, #delete and #head methods
460
466
  # respectively which will make tests more expressive.
461
467
  #
468
+ # It's not recommended to make more than one request in the same test. Instance
469
+ # variables that are set in one request will not persist to the next request,
470
+ # but it's not guaranteed that all Rails internal state will be reset. Prefer
471
+ # ActionDispatch::IntegrationTest for making multiple requests in the same test.
472
+ #
462
473
  # Note that the request method is not verified.
463
474
  def process(action, method: "GET", params: nil, session: nil, body: nil, flash: {}, format: nil, xhr: false, as: nil)
464
475
  check_required_ivars
476
+ @controller.clear_instance_variables_between_requests
465
477
 
466
478
  action = +action.to_s
467
479
  http_method = method.to_s.upcase
@@ -574,10 +586,19 @@ module ActionController
574
586
  end
575
587
  end
576
588
 
589
+ def wrap_execution(&block)
590
+ if ActionController::TestCase.executor_around_each_request && defined?(Rails.application) && Rails.application
591
+ Rails.application.executor.wrap(&block)
592
+ else
593
+ yield
594
+ end
595
+ end
596
+
577
597
  def process_controller_response(action, cookies, xhr)
578
598
  begin
579
599
  @controller.recycle!
580
- @controller.dispatch(action, @request, @response)
600
+
601
+ wrap_execution { @controller.dispatch(action, @request, @response) }
581
602
  ensure
582
603
  @request = @controller.request
583
604
  @response = @controller.response
@@ -623,7 +644,7 @@ module ActionController
623
644
  end
624
645
 
625
646
  def check_required_ivars
626
- # Sanity check for required instance variables so we can give an
647
+ # Check for required instance variables so we can give an
627
648
  # understandable error message.
628
649
  [:@routes, :@controller, :@request, :@response].each do |iv_name|
629
650
  if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?