actionpack 7.0.8 → 7.1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +360 -353
  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 -4
  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 +17 -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 +132 -52
  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 +15 -5
  48. data/lib/action_controller.rb +8 -1
  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 +35 -12
  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 +40 -18
  60. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  61. data/lib/action_dispatch/http/request.rb +48 -14
  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 +1 -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 +19 -15
  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 +14 -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 +35 -24
  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 +52 -22
  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 +20 -19
  121. data/lib/action_dispatch/system_testing/driver.rb +13 -21
  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 +37 -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 +64 -28
@@ -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,14 +780,14 @@ 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)
@@ -749,7 +796,7 @@ module ActionController
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)
@@ -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) # => ["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
@@ -1112,6 +1190,8 @@ module ActionController
1112
1190
  case element
1113
1191
  when ->(e) { permitted_scalar?(e) }
1114
1192
  sanitized << element
1193
+ when Array
1194
+ sanitized << permit_any_in_array(element)
1115
1195
  when Parameters
1116
1196
  sanitized << permit_any_in_parameters(element)
1117
1197
  else
@@ -1127,7 +1207,7 @@ module ActionController
1127
1207
  end
1128
1208
  end
1129
1209
 
1130
- # == Strong \Parameters
1210
+ # = Strong \Parameters
1131
1211
  #
1132
1212
  # It provides an interface for protecting attributes from end-user
1133
1213
  # 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,
@@ -4,6 +4,8 @@ require "active_support/core_ext/array/extract_options"
4
4
  require "action_dispatch/middleware/stack"
5
5
 
6
6
  module ActionController
7
+ # = Action Controller \MiddlewareStack
8
+ #
7
9
  # Extend ActionDispatch middleware stack to make it aware of options
8
10
  # allowing the following syntax in controllers:
9
11
  #
@@ -58,7 +60,9 @@ module ActionController
58
60
  end
59
61
  end
60
62
 
61
- # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
63
+ # = Action Controller \Metal
64
+ #
65
+ # +ActionController::Metal+ is the simplest possible controller, providing a
62
66
  # valid Rack interface without the additional niceties provided by
63
67
  # ActionController::Base.
64
68
  #
@@ -78,9 +82,9 @@ module ActionController
78
82
  # The +action+ method returns a valid Rack application for the \Rails
79
83
  # router to dispatch to.
80
84
  #
81
- # == Rendering Helpers
85
+ # == \Rendering \Helpers
82
86
  #
83
- # <tt>ActionController::Metal</tt> by default provides no utilities for rendering
87
+ # +ActionController::Metal+ by default provides no utilities for rendering
84
88
  # views, partials, or other responses aside from explicitly calling of
85
89
  # <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
86
90
  # add the render helpers you're used to having in a normal controller, you
@@ -96,7 +100,7 @@ module ActionController
96
100
  # end
97
101
  # end
98
102
  #
99
- # == Redirection Helpers
103
+ # == Redirection \Helpers
100
104
  #
101
105
  # To add redirection helpers to your metal controller, do the following:
102
106
  #
@@ -109,7 +113,7 @@ module ActionController
109
113
  # end
110
114
  # end
111
115
  #
112
- # == Other Helpers
116
+ # == Other \Helpers
113
117
  #
114
118
  # You can refer to the modules included in ActionController::Base to see
115
119
  # other features you can bring into your metal controller.
@@ -118,8 +122,8 @@ module ActionController
118
122
  abstract!
119
123
 
120
124
  # Returns the last part of the controller's name, underscored, without the ending
121
- # <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
122
- # Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
125
+ # <tt>Controller</tt>. For instance, +PostsController+ returns <tt>posts</tt>.
126
+ # Namespaces are left out, so +Admin::PostsController+ returns <tt>posts</tt> as well.
123
127
  #
124
128
  # ==== Returns
125
129
  # * <tt>string</tt>
@@ -137,20 +141,53 @@ module ActionController
137
141
  false
138
142
  end
139
143
 
144
+ class << self
145
+ private
146
+ def inherited(subclass)
147
+ super
148
+ subclass.middleware_stack = middleware_stack.dup
149
+ subclass.class_eval do
150
+ @controller_name = nil
151
+ end
152
+ end
153
+ end
154
+
140
155
  # Delegates to the class's ::controller_name.
141
156
  def controller_name
142
157
  self.class.controller_name
143
158
  end
144
159
 
145
- attr_internal :response, :request
160
+ ##
161
+ # :attr_reader: request
162
+ #
163
+ # The ActionDispatch::Request instance for the current request.
164
+ attr_internal :request
165
+
166
+ ##
167
+ # :attr_reader: response
168
+ #
169
+ # The ActionDispatch::Response instance for the current response.
170
+ attr_internal_reader :response
171
+
172
+ ##
173
+ # The ActionDispatch::Request::Session instance for the current request.
174
+ # See further details in the
175
+ # {Active Controller Session guide}[https://guides.rubyonrails.org/action_controller_overview.html#session].
146
176
  delegate :session, to: "@_request"
147
- delegate :headers, :status=, :location=, :content_type=,
177
+
178
+ ##
179
+ # Delegates to ActionDispatch::Response#headers.
180
+ delegate :headers, to: "@_response"
181
+
182
+ delegate :status=, :location=, :content_type=,
148
183
  :status, :location, :content_type, :media_type, to: "@_response"
149
184
 
150
185
  def initialize
151
186
  @_request = nil
152
187
  @_response = nil
188
+ @_response_body = nil
153
189
  @_routes = nil
190
+ @_params = nil
154
191
  super
155
192
  end
156
193
 
@@ -164,17 +201,19 @@ module ActionController
164
201
 
165
202
  alias :response_code :status # :nodoc:
166
203
 
167
- # Basic url_for that can be overridden for more robust functionality.
204
+ # Basic \url_for that can be overridden for more robust functionality.
168
205
  def url_for(string)
169
206
  string
170
207
  end
171
208
 
172
209
  def response_body=(body)
173
- body = [body] unless body.nil? || body.respond_to?(:each)
174
- response.reset_body!
175
- return unless body
176
- response.body = body
177
- super
210
+ if body
211
+ body = [body] if body.is_a?(String)
212
+ response.body = body
213
+ super
214
+ else
215
+ response.reset_body!
216
+ end
178
217
  end
179
218
 
180
219
  # Tests if render or redirect has already happened.
@@ -191,9 +230,22 @@ module ActionController
191
230
  end
192
231
 
193
232
  def set_response!(response) # :nodoc:
233
+ if @_response
234
+ _, _, body = @_response
235
+ body.close if body.respond_to?(:close)
236
+ end
237
+
194
238
  @_response = response
195
239
  end
196
240
 
241
+ # Assign the response and mark it as committed. No further processing will occur.
242
+ def response=(response)
243
+ set_response!(response)
244
+
245
+ # Force `performed?` to return true:
246
+ @_response_body = true
247
+ end
248
+
197
249
  def set_request!(request) # :nodoc:
198
250
  @_request = request
199
251
  @_request.controller_instance = self
@@ -209,11 +261,6 @@ module ActionController
209
261
 
210
262
  class_attribute :middleware_stack, default: ActionController::MiddlewareStack.new
211
263
 
212
- def self.inherited(base) # :nodoc:
213
- base.middleware_stack = middleware_stack.dup
214
- super
215
- end
216
-
217
264
  class << self
218
265
  # Pushes the given Rack middleware and its arguments to the bottom of the
219
266
  # middleware stack.
@@ -222,7 +269,18 @@ module ActionController
222
269
  end
223
270
  end
224
271
 
225
- # Alias for +middleware_stack+.
272
+ # The middleware stack used by this controller.
273
+ #
274
+ # By default uses a variation of ActionDispatch::MiddlewareStack which
275
+ # allows for the following syntax:
276
+ #
277
+ # class PostsController < ApplicationController
278
+ # use AuthenticationMiddleware, except: [:index, :show]
279
+ # end
280
+ #
281
+ # Read more about {Rails middleware
282
+ # stack}[https://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack]
283
+ # in the guides.
226
284
  def self.middleware
227
285
  middleware_stack
228
286
  end