actionpack 7.0.8.7 → 7.1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +423 -342
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/abstract_controller/base.rb +20 -11
  6. data/lib/abstract_controller/caching/fragments.rb +2 -0
  7. data/lib/abstract_controller/callbacks.rb +31 -6
  8. data/lib/abstract_controller/deprecator.rb +7 -0
  9. data/lib/abstract_controller/helpers.rb +61 -18
  10. data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
  11. data/lib/abstract_controller/rendering.rb +3 -3
  12. data/lib/abstract_controller/translation.rb +7 -24
  13. data/lib/abstract_controller/url_for.rb +2 -0
  14. data/lib/abstract_controller.rb +6 -0
  15. data/lib/action_controller/api.rb +5 -3
  16. data/lib/action_controller/base.rb +3 -17
  17. data/lib/action_controller/caching.rb +2 -0
  18. data/lib/action_controller/deprecator.rb +7 -0
  19. data/lib/action_controller/form_builder.rb +2 -0
  20. data/lib/action_controller/log_subscriber.rb +16 -4
  21. data/lib/action_controller/metal/content_security_policy.rb +1 -1
  22. data/lib/action_controller/metal/data_streaming.rb +2 -0
  23. data/lib/action_controller/metal/default_headers.rb +2 -0
  24. data/lib/action_controller/metal/etag_with_flash.rb +2 -0
  25. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
  26. data/lib/action_controller/metal/exceptions.rb +8 -0
  27. data/lib/action_controller/metal/head.rb +8 -6
  28. data/lib/action_controller/metal/helpers.rb +3 -14
  29. data/lib/action_controller/metal/http_authentication.rb +13 -8
  30. data/lib/action_controller/metal/implicit_render.rb +5 -3
  31. data/lib/action_controller/metal/instrumentation.rb +8 -1
  32. data/lib/action_controller/metal/live.rb +24 -0
  33. data/lib/action_controller/metal/mime_responds.rb +2 -2
  34. data/lib/action_controller/metal/params_wrapper.rb +4 -2
  35. data/lib/action_controller/metal/permissions_policy.rb +1 -1
  36. data/lib/action_controller/metal/redirecting.rb +7 -7
  37. data/lib/action_controller/metal/renderers.rb +2 -2
  38. data/lib/action_controller/metal/rendering.rb +0 -7
  39. data/lib/action_controller/metal/request_forgery_protection.rb +139 -50
  40. data/lib/action_controller/metal/rescue.rb +2 -0
  41. data/lib/action_controller/metal/streaming.rb +70 -30
  42. data/lib/action_controller/metal/strong_parameters.rb +174 -54
  43. data/lib/action_controller/metal/url_for.rb +7 -0
  44. data/lib/action_controller/metal.rb +79 -21
  45. data/lib/action_controller/railtie.rb +22 -9
  46. data/lib/action_controller/renderer.rb +98 -65
  47. data/lib/action_controller/test_case.rb +18 -8
  48. data/lib/action_controller.rb +13 -3
  49. data/lib/action_dispatch/constants.rb +32 -0
  50. data/lib/action_dispatch/deprecator.rb +7 -0
  51. data/lib/action_dispatch/http/cache.rb +1 -3
  52. data/lib/action_dispatch/http/content_security_policy.rb +9 -8
  53. data/lib/action_dispatch/http/filter_parameters.rb +11 -5
  54. data/lib/action_dispatch/http/headers.rb +2 -0
  55. data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
  56. data/lib/action_dispatch/http/mime_type.rb +37 -11
  57. data/lib/action_dispatch/http/mime_types.rb +3 -1
  58. data/lib/action_dispatch/http/parameters.rb +1 -1
  59. data/lib/action_dispatch/http/permissions_policy.rb +38 -16
  60. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  61. data/lib/action_dispatch/http/request.rb +70 -16
  62. data/lib/action_dispatch/http/response.rb +80 -59
  63. data/lib/action_dispatch/http/upload.rb +2 -0
  64. data/lib/action_dispatch/journey/formatter.rb +8 -2
  65. data/lib/action_dispatch/journey/path/pattern.rb +14 -14
  66. data/lib/action_dispatch/journey/route.rb +3 -2
  67. data/lib/action_dispatch/journey/router.rb +9 -8
  68. data/lib/action_dispatch/journey/routes.rb +2 -2
  69. data/lib/action_dispatch/log_subscriber.rb +23 -0
  70. data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
  71. data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
  72. data/lib/action_dispatch/middleware/callbacks.rb +2 -0
  73. data/lib/action_dispatch/middleware/cookies.rb +81 -98
  74. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
  75. data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
  76. data/lib/action_dispatch/middleware/debug_view.rb +7 -2
  77. data/lib/action_dispatch/middleware/exception_wrapper.rb +186 -27
  78. data/lib/action_dispatch/middleware/executor.rb +7 -1
  79. data/lib/action_dispatch/middleware/flash.rb +7 -0
  80. data/lib/action_dispatch/middleware/host_authorization.rb +6 -3
  81. data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
  82. data/lib/action_dispatch/middleware/reloader.rb +7 -5
  83. data/lib/action_dispatch/middleware/remote_ip.rb +17 -16
  84. data/lib/action_dispatch/middleware/request_id.rb +2 -0
  85. data/lib/action_dispatch/middleware/server_timing.rb +4 -4
  86. data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
  89. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
  90. data/lib/action_dispatch/middleware/show_exceptions.rb +33 -19
  91. data/lib/action_dispatch/middleware/ssl.rb +18 -6
  92. data/lib/action_dispatch/middleware/stack.rb +7 -2
  93. data/lib/action_dispatch/middleware/static.rb +12 -8
  94. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
  95. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
  96. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
  97. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
  98. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
  99. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
  102. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
  103. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
  104. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  105. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  106. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
  107. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +46 -37
  108. data/lib/action_dispatch/railtie.rb +13 -4
  109. data/lib/action_dispatch/request/session.rb +16 -6
  110. data/lib/action_dispatch/request/utils.rb +8 -3
  111. data/lib/action_dispatch/routing/inspector.rb +54 -6
  112. data/lib/action_dispatch/routing/mapper.rb +74 -26
  113. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
  114. data/lib/action_dispatch/routing/redirection.rb +15 -6
  115. data/lib/action_dispatch/routing/route_set.rb +53 -23
  116. data/lib/action_dispatch/routing/routes_proxy.rb +10 -15
  117. data/lib/action_dispatch/routing/url_for.rb +5 -1
  118. data/lib/action_dispatch/routing.rb +7 -7
  119. data/lib/action_dispatch/system_test_case.rb +3 -3
  120. data/lib/action_dispatch/system_testing/browser.rb +25 -19
  121. data/lib/action_dispatch/system_testing/driver.rb +14 -22
  122. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
  123. data/lib/action_dispatch/testing/assertion_response.rb +1 -1
  124. data/lib/action_dispatch/testing/assertions/response.rb +13 -6
  125. data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
  126. data/lib/action_dispatch/testing/assertions.rb +3 -1
  127. data/lib/action_dispatch/testing/integration.rb +27 -17
  128. data/lib/action_dispatch/testing/request_encoder.rb +4 -1
  129. data/lib/action_dispatch/testing/test_process.rb +4 -3
  130. data/lib/action_dispatch/testing/test_request.rb +1 -1
  131. data/lib/action_dispatch/testing/test_response.rb +23 -9
  132. data/lib/action_dispatch.rb +41 -4
  133. data/lib/action_pack/gem_version.rb +4 -4
  134. data/lib/action_pack/version.rb +1 -1
  135. data/lib/action_pack.rb +1 -1
  136. metadata +62 -26
@@ -4,6 +4,7 @@ require "active_support/core_ext/hash/indifferent_access"
4
4
  require "active_support/core_ext/array/wrap"
5
5
  require "active_support/core_ext/string/filters"
6
6
  require "active_support/core_ext/object/to_query"
7
+ require "active_support/deep_mergeable"
7
8
  require "action_dispatch/http/upload"
8
9
  require "rack/test"
9
10
  require "stringio"
@@ -64,7 +65,14 @@ module ActionController
64
65
  end
65
66
  end
66
67
 
67
- # == Action Controller \Parameters
68
+ # Raised when initializing Parameters with keys that aren't strings or symbols.
69
+ #
70
+ # ActionController::Parameters.new(123 => 456)
71
+ # # => ActionController::InvalidParameterKey: all keys must be Strings or Symbols, got: Integer
72
+ class InvalidParameterKey < ArgumentError
73
+ end
74
+
75
+ # = Action Controller \Parameters
68
76
  #
69
77
  # Allows you to choose which attributes should be permitted for mass updating
70
78
  # and thus prevent accidentally exposing that which shouldn't be exposed.
@@ -92,8 +100,8 @@ module ActionController
92
100
  # * +permit_all_parameters+ - If it's +true+, all the parameters will be
93
101
  # permitted by default. The default is +false+.
94
102
  # * +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:
103
+ # permitted are found. The default value is <tt>:log</tt> in test and development environments,
104
+ # +false+ otherwise. The values can be:
97
105
  # * +false+ to take no action.
98
106
  # * <tt>:log</tt> to emit an <tt>ActiveSupport::Notifications.instrument</tt> event on the
99
107
  # <tt>unpermitted_parameters.action_controller</tt> topic and log at the DEBUG level.
@@ -123,17 +131,44 @@ module ActionController
123
131
  # environment they should only be set once at boot-time and never mutated at
124
132
  # runtime.
125
133
  #
126
- # You can fetch values of <tt>ActionController::Parameters</tt> using either
134
+ # You can fetch values of +ActionController::Parameters+ using either
127
135
  # <tt>:key</tt> or <tt>"key"</tt>.
128
136
  #
129
137
  # params = ActionController::Parameters.new(key: "value")
130
138
  # params[:key] # => "value"
131
139
  # params["key"] # => "value"
132
140
  class Parameters
141
+ include ActiveSupport::DeepMergeable
142
+
133
143
  cattr_accessor :permit_all_parameters, instance_accessor: false, default: false
134
144
 
135
145
  cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
136
146
 
147
+ ##
148
+ # :method: deep_merge
149
+ #
150
+ # :call-seq:
151
+ # deep_merge(other_hash, &block)
152
+ #
153
+ # Returns a new +ActionController::Parameters+ instance with +self+ and +other_hash+ merged recursively.
154
+ #
155
+ # Like with <tt>Hash#merge</tt> in the standard library, a block can be provided
156
+ # to merge values.
157
+ #
158
+ #--
159
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge.
160
+
161
+ ##
162
+ # :method: deep_merge!
163
+ #
164
+ # :call-seq:
165
+ # deep_merge!(other_hash, &block)
166
+ #
167
+ # Same as +#deep_merge+, but modifies +self+.
168
+ #
169
+ #--
170
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge!.
171
+
137
172
  ##
138
173
  # :method: as_json
139
174
  #
@@ -160,12 +195,12 @@ module ActionController
160
195
  # Returns true if the parameters have no key/value pairs.
161
196
 
162
197
  ##
163
- # :method: has_value?
198
+ # :method: exclude?
164
199
  #
165
200
  # :call-seq:
166
- # has_value?(value)
201
+ # exclude?(key)
167
202
  #
168
- # Returns true if the given value is present for some key in the parameters.
203
+ # Returns true if the given key is not present in the parameters.
169
204
 
170
205
  ##
171
206
  # :method: include?
@@ -191,22 +226,7 @@ module ActionController
191
226
  #
192
227
  # Returns the content of the parameters as a string.
193
228
 
194
- ##
195
- # :method: value?
196
- #
197
- # :call-seq:
198
- # value?(value)
199
- #
200
- # Returns true if the given value is present for some key in the parameters.
201
-
202
- ##
203
- # :method: values
204
- #
205
- # :call-seq:
206
- # values()
207
- #
208
- # Returns a new array of the values of the parameters.
209
- delegate :keys, :values, :has_value?, :value?, :empty?, :include?,
229
+ delegate :keys, :empty?, :exclude?, :include?,
210
230
  :as_json, :to_s, :each_key, to: :@parameters
211
231
 
212
232
  alias_method :has_key?, :include?
@@ -222,13 +242,15 @@ module ActionController
222
242
  # config.action_controller.always_permitted_parameters = %w( controller action format )
223
243
  cattr_accessor :always_permitted_parameters, default: %w( controller action )
224
244
 
245
+ cattr_accessor :allow_deprecated_parameters_hash_equality, default: true, instance_accessor: false
246
+
225
247
  class << self
226
248
  def nested_attribute?(key, value) # :nodoc:
227
249
  /\A-?\d+\z/.match?(key) && (value.is_a?(Hash) || value.is_a?(Parameters))
228
250
  end
229
251
  end
230
252
 
231
- # Returns a new <tt>ActionController::Parameters</tt> instance.
253
+ # Returns a new +ActionController::Parameters+ instance.
232
254
  # Also, sets the +permitted+ attribute to the default value of
233
255
  # <tt>ActionController::Parameters.permit_all_parameters</tt>.
234
256
  #
@@ -245,6 +267,12 @@ module ActionController
245
267
  # params.permitted? # => true
246
268
  # Person.new(params) # => #<Person id: nil, name: "Francesco">
247
269
  def initialize(parameters = {}, logging_context = {})
270
+ parameters.each_key do |key|
271
+ unless key.is_a?(String) || key.is_a?(Symbol)
272
+ raise InvalidParameterKey, "all keys must be Strings or Symbols, got: #{key.class}"
273
+ end
274
+ end
275
+
248
276
  @parameters = parameters.with_indifferent_access
249
277
  @logging_context = logging_context
250
278
  @permitted = self.class.permit_all_parameters
@@ -256,7 +284,20 @@ module ActionController
256
284
  if other.respond_to?(:permitted?)
257
285
  permitted? == other.permitted? && parameters == other.parameters
258
286
  else
259
- @parameters == other
287
+ if self.class.allow_deprecated_parameters_hash_equality && Hash === other
288
+ ActionController.deprecator.warn <<-WARNING.squish
289
+ Comparing equality between `ActionController::Parameters` and a
290
+ `Hash` is deprecated and will be removed in Rails 7.2. Please only do
291
+ comparisons between instances of `ActionController::Parameters`. If
292
+ you need to compare to a hash, first convert it using
293
+ `ActionController::Parameters#new`.
294
+ To disable the deprecated behavior set
295
+ `Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality = false`.
296
+ WARNING
297
+ @parameters == other
298
+ else
299
+ super
300
+ end
260
301
  end
261
302
  end
262
303
 
@@ -282,9 +323,9 @@ module ActionController
282
323
  #
283
324
  # safe_params = params.permit(:name)
284
325
  # safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
285
- def to_h
326
+ def to_h(&block)
286
327
  if permitted?
287
- convert_parameters_to_hashes(@parameters, :to_h)
328
+ convert_parameters_to_hashes(@parameters, :to_h, &block)
288
329
  else
289
330
  raise UnfilteredParameters
290
331
  end
@@ -374,6 +415,11 @@ module ActionController
374
415
  self
375
416
  end
376
417
 
418
+ # Returns a new array of the values of the parameters.
419
+ def values
420
+ to_enum(:each_value).to_a
421
+ end
422
+
377
423
  # Attribute that keeps track of converted arrays, if any, to avoid double
378
424
  # looping in the common use case permit + mass-assignment. Defined in a
379
425
  # method to instantiate it only if needed.
@@ -480,7 +526,7 @@ module ActionController
480
526
 
481
527
  alias :required :require
482
528
 
483
- # Returns a new <tt>ActionController::Parameters</tt> instance that
529
+ # Returns a new +ActionController::Parameters+ instance that
484
530
  # includes only the given +filters+ and sets the +permitted+ attribute
485
531
  # for the object to +true+. This is useful for limiting which attributes
486
532
  # should be allowed for mass updating.
@@ -665,7 +711,7 @@ module ActionController
665
711
  @parameters.dig(*keys)
666
712
  end
667
713
 
668
- # Returns a new <tt>ActionController::Parameters</tt> instance that
714
+ # Returns a new +ActionController::Parameters+ instance that
669
715
  # includes only the given +keys+. If the given +keys+
670
716
  # don't exist, returns an empty hash.
671
717
  #
@@ -676,14 +722,14 @@ module ActionController
676
722
  new_instance_with_inherited_permitted_status(@parameters.slice(*keys))
677
723
  end
678
724
 
679
- # Returns the current <tt>ActionController::Parameters</tt> instance which
725
+ # Returns the current +ActionController::Parameters+ instance which
680
726
  # contains only the given +keys+.
681
727
  def slice!(*keys)
682
728
  @parameters.slice!(*keys)
683
729
  self
684
730
  end
685
731
 
686
- # Returns a new <tt>ActionController::Parameters</tt> instance that
732
+ # Returns a new +ActionController::Parameters+ instance that
687
733
  # filters out the given +keys+.
688
734
  #
689
735
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
@@ -692,6 +738,7 @@ module ActionController
692
738
  def except(*keys)
693
739
  new_instance_with_inherited_permitted_status(@parameters.except(*keys))
694
740
  end
741
+ alias_method :without, :except
695
742
 
696
743
  # Removes and returns the key/value pairs matching the given keys.
697
744
  #
@@ -702,7 +749,7 @@ module ActionController
702
749
  new_instance_with_inherited_permitted_status(@parameters.extract!(*keys))
703
750
  end
704
751
 
705
- # Returns a new <tt>ActionController::Parameters</tt> instance with the results of
752
+ # Returns a new +ActionController::Parameters+ instance with the results of
706
753
  # running +block+ once for every value. The keys are unchanged.
707
754
  #
708
755
  # params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
@@ -716,14 +763,14 @@ module ActionController
716
763
  end
717
764
 
718
765
  # Performs values transformation and returns the altered
719
- # <tt>ActionController::Parameters</tt> instance.
766
+ # +ActionController::Parameters+ instance.
720
767
  def transform_values!
721
768
  return to_enum(:transform_values!) unless block_given?
722
769
  @parameters.transform_values! { |v| yield convert_value_to_parameters(v) }
723
770
  self
724
771
  end
725
772
 
726
- # Returns a new <tt>ActionController::Parameters</tt> instance with the
773
+ # Returns a new +ActionController::Parameters+ instance with the
727
774
  # results of running +block+ once for every key. The values are unchanged.
728
775
  def transform_keys(&block)
729
776
  return to_enum(:transform_keys) unless block_given?
@@ -733,27 +780,27 @@ module ActionController
733
780
  end
734
781
 
735
782
  # Performs keys transformation and returns the altered
736
- # <tt>ActionController::Parameters</tt> instance.
783
+ # +ActionController::Parameters+ instance.
737
784
  def transform_keys!(&block)
738
785
  return to_enum(:transform_keys!) unless block_given?
739
786
  @parameters.transform_keys!(&block)
740
787
  self
741
788
  end
742
789
 
743
- # Returns a new <tt>ActionController::Parameters</tt> instance with the
790
+ # Returns a new +ActionController::Parameters+ instance with the
744
791
  # results of running +block+ once for every key. This includes the keys
745
792
  # from the root hash and from all nested hashes and arrays. The values are unchanged.
746
793
  def deep_transform_keys(&block)
747
794
  new_instance_with_inherited_permitted_status(
748
- @parameters.deep_transform_keys(&block)
795
+ _deep_transform_keys_in_object(@parameters, &block).to_unsafe_h
749
796
  )
750
797
  end
751
798
 
752
- # Returns the same <tt>ActionController::Parameters</tt> instance with
799
+ # Returns the same +ActionController::Parameters+ instance with
753
800
  # changed keys. This includes the keys from the root hash and from all
754
801
  # nested hashes and arrays. The values are unchanged.
755
802
  def deep_transform_keys!(&block)
756
- @parameters.deep_transform_keys!(&block)
803
+ @parameters = _deep_transform_keys_in_object(@parameters, &block).to_unsafe_h
757
804
  self
758
805
  end
759
806
 
@@ -765,7 +812,7 @@ module ActionController
765
812
  convert_value_to_parameters(@parameters.delete(key, &block))
766
813
  end
767
814
 
768
- # Returns a new <tt>ActionController::Parameters</tt> instance with only
815
+ # Returns a new +ActionController::Parameters+ instance with only
769
816
  # items that the block evaluates to true.
770
817
  def select(&block)
771
818
  new_instance_with_inherited_permitted_status(@parameters.select(&block))
@@ -778,7 +825,7 @@ module ActionController
778
825
  end
779
826
  alias_method :keep_if, :select!
780
827
 
781
- # Returns a new <tt>ActionController::Parameters</tt> instance with items
828
+ # Returns a new +ActionController::Parameters+ instance with items
782
829
  # that the block evaluates to true removed.
783
830
  def reject(&block)
784
831
  new_instance_with_inherited_permitted_status(@parameters.reject(&block))
@@ -791,7 +838,7 @@ module ActionController
791
838
  end
792
839
  alias_method :delete_if, :reject!
793
840
 
794
- # Returns a new <tt>ActionController::Parameters</tt> instance with +nil+ values removed.
841
+ # Returns a new +ActionController::Parameters+ instance with +nil+ values removed.
795
842
  def compact
796
843
  new_instance_with_inherited_permitted_status(@parameters.compact)
797
844
  end
@@ -801,7 +848,7 @@ module ActionController
801
848
  self if @parameters.compact!
802
849
  end
803
850
 
804
- # Returns a new <tt>ActionController::Parameters</tt> instance without the blank values.
851
+ # Returns a new +ActionController::Parameters+ instance without the blank values.
805
852
  # Uses Object#blank? for determining if a value is blank.
806
853
  def compact_blank
807
854
  reject { |_k, v| v.blank? }
@@ -813,13 +860,20 @@ module ActionController
813
860
  reject! { |_k, v| v.blank? }
814
861
  end
815
862
 
863
+ # Returns true if the given value is present for some key in the parameters.
864
+ def has_value?(value)
865
+ each_value.include?(convert_value_to_parameters(value))
866
+ end
867
+
868
+ alias value? has_value?
869
+
816
870
  # Returns values that were assigned to the given +keys+. Note that all the
817
- # +Hash+ objects will be converted to <tt>ActionController::Parameters</tt>.
871
+ # +Hash+ objects will be converted to +ActionController::Parameters+.
818
872
  def values_at(*keys)
819
873
  convert_value_to_parameters(@parameters.values_at(*keys))
820
874
  end
821
875
 
822
- # Returns a new <tt>ActionController::Parameters</tt> instance with all keys from
876
+ # Returns a new +ActionController::Parameters+ instance with all keys from
823
877
  # +other_hash+ merged into current hash.
824
878
  def merge(other_hash)
825
879
  new_instance_with_inherited_permitted_status(
@@ -827,14 +881,21 @@ module ActionController
827
881
  )
828
882
  end
829
883
 
830
- # Returns the current <tt>ActionController::Parameters</tt> instance with
884
+ ##
885
+ # :call-seq: merge!(other_hash)
886
+ #
887
+ # Returns the current +ActionController::Parameters+ instance with
831
888
  # +other_hash+ merged into current hash.
832
- def merge!(other_hash)
833
- @parameters.merge!(other_hash.to_h)
889
+ def merge!(other_hash, &block)
890
+ @parameters.merge!(other_hash.to_h, &block)
834
891
  self
835
892
  end
836
893
 
837
- # Returns a new <tt>ActionController::Parameters</tt> instance with all keys
894
+ def deep_merge?(other_hash) # :nodoc:
895
+ other_hash.is_a?(ActiveSupport::DeepMergeable)
896
+ end
897
+
898
+ # Returns a new +ActionController::Parameters+ instance with all keys
838
899
  # from current hash merged into +other_hash+.
839
900
  def reverse_merge(other_hash)
840
901
  new_instance_with_inherited_permitted_status(
@@ -843,7 +904,7 @@ module ActionController
843
904
  end
844
905
  alias_method :with_defaults, :reverse_merge
845
906
 
846
- # Returns the current <tt>ActionController::Parameters</tt> instance with
907
+ # Returns the current +ActionController::Parameters+ instance with
847
908
  # current hash merged into +other_hash+.
848
909
  def reverse_merge!(other_hash)
849
910
  @parameters.merge!(other_hash.to_h) { |key, left, right| left }
@@ -900,6 +961,22 @@ module ActionController
900
961
  end
901
962
  end
902
963
 
964
+ # Returns parameter value for the given +key+ separated by +delimiter+.
965
+ #
966
+ # params = ActionController::Parameters.new(id: "1_123", tags: "ruby,rails")
967
+ # params.extract_value(:id) # => ["1", "123"]
968
+ # params.extract_value(:tags, delimiter: ",") # => ["ruby", "rails"]
969
+ # params.extract_value(:non_existent_key) # => nil
970
+ #
971
+ # Note that if the given +key+'s value contains blank elements, then
972
+ # the returned array will include empty strings.
973
+ #
974
+ # params = ActionController::Parameters.new(tags: "ruby,rails,,web")
975
+ # params.extract_value(:tags, delimiter: ",") # => ["ruby", "rails", "", "web"]
976
+ def extract_value(key, delimiter: "_")
977
+ @parameters[key]&.split(delimiter, -1)
978
+ end
979
+
903
980
  protected
904
981
  attr_reader :parameters
905
982
 
@@ -922,14 +999,15 @@ module ActionController
922
999
  end
923
1000
  end
924
1001
 
925
- def convert_parameters_to_hashes(value, using)
1002
+ def convert_parameters_to_hashes(value, using, &block)
926
1003
  case value
927
1004
  when Array
928
1005
  value.map { |v| convert_parameters_to_hashes(v, using) }
929
1006
  when Hash
930
- value.transform_values do |v|
1007
+ transformed = value.transform_values do |v|
931
1008
  convert_parameters_to_hashes(v, using)
932
- end.with_indifferent_access
1009
+ end
1010
+ (block_given? ? transformed.to_h(&block) : transformed).with_indifferent_access
933
1011
  when Parameters
934
1012
  value.send(using)
935
1013
  else
@@ -957,6 +1035,46 @@ module ActionController
957
1035
  end
958
1036
  end
959
1037
 
1038
+ def _deep_transform_keys_in_object(object, &block)
1039
+ case object
1040
+ when Hash
1041
+ object.each_with_object(self.class.new) do |(key, value), result|
1042
+ result[yield(key)] = _deep_transform_keys_in_object(value, &block)
1043
+ end
1044
+ when Parameters
1045
+ if object.permitted?
1046
+ object.to_h.deep_transform_keys(&block)
1047
+ else
1048
+ object.to_unsafe_h.deep_transform_keys(&block)
1049
+ end
1050
+ when Array
1051
+ object.map { |e| _deep_transform_keys_in_object(e, &block) }
1052
+ else
1053
+ object
1054
+ end
1055
+ end
1056
+
1057
+ def _deep_transform_keys_in_object!(object, &block)
1058
+ case object
1059
+ when Hash
1060
+ object.keys.each do |key|
1061
+ value = object.delete(key)
1062
+ object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
1063
+ end
1064
+ object
1065
+ when Parameters
1066
+ if object.permitted?
1067
+ object.to_h.deep_transform_keys!(&block)
1068
+ else
1069
+ object.to_unsafe_h.deep_transform_keys!(&block)
1070
+ end
1071
+ when Array
1072
+ object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
1073
+ else
1074
+ object
1075
+ end
1076
+ end
1077
+
960
1078
  def specify_numeric_keys?(filter)
961
1079
  if filter.respond_to?(:keys)
962
1080
  filter.keys.any? { |key| /\A-?\d+\z/.match?(key) }
@@ -1112,6 +1230,8 @@ module ActionController
1112
1230
  case element
1113
1231
  when ->(e) { permitted_scalar?(e) }
1114
1232
  sanitized << element
1233
+ when Array
1234
+ sanitized << permit_any_in_array(element)
1115
1235
  when Parameters
1116
1236
  sanitized << permit_any_in_parameters(element)
1117
1237
  else
@@ -1127,7 +1247,7 @@ module ActionController
1127
1247
  end
1128
1248
  end
1129
1249
 
1130
- # == Strong \Parameters
1250
+ # = Strong \Parameters
1131
1251
  #
1132
1252
  # It provides an interface for protecting attributes from end-user
1133
1253
  # assignment. This makes Action Controller parameters forbidden
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionController
4
+ # = Action Controller \UrlFor
5
+ #
4
6
  # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
5
7
  # the <tt>_routes</tt> method. Otherwise, an exception will be raised.
6
8
  #
@@ -25,6 +27,11 @@ module ActionController
25
27
 
26
28
  include AbstractController::UrlFor
27
29
 
30
+ def initialize(...)
31
+ super
32
+ @_url_options = nil
33
+ end
34
+
28
35
  def url_options
29
36
  @_url_options ||= {
30
37
  host: request.host,