activesupport 7.0.8.7 → 7.2.2.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 (198) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +143 -459
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +3 -1
  7. data/lib/active_support/backtrace_cleaner.rb +39 -7
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/broadcast_logger.rb +251 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +49 -17
  14. data/lib/active_support/cache/mem_cache_store.rb +94 -128
  15. data/lib/active_support/cache/memory_store.rb +80 -25
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +165 -152
  18. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +29 -14
  20. data/lib/active_support/cache.rb +363 -291
  21. data/lib/active_support/callbacks.rb +118 -134
  22. data/lib/active_support/code_generator.rb +15 -10
  23. data/lib/active_support/concern.rb +4 -2
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/configurable.rb +10 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +1 -2
  28. data/lib/active_support/core_ext/array.rb +0 -1
  29. data/lib/active_support/core_ext/class/subclasses.rb +17 -34
  30. data/lib/active_support/core_ext/date/blank.rb +4 -0
  31. data/lib/active_support/core_ext/date/conversions.rb +1 -2
  32. data/lib/active_support/core_ext/date.rb +0 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  34. data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
  35. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  36. data/lib/active_support/core_ext/date_time/conversions.rb +2 -2
  37. data/lib/active_support/core_ext/date_time.rb +0 -1
  38. data/lib/active_support/core_ext/digest/uuid.rb +7 -10
  39. data/lib/active_support/core_ext/enumerable.rb +3 -75
  40. data/lib/active_support/core_ext/erb/util.rb +201 -0
  41. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  42. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  43. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  44. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  45. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  46. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  47. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  48. data/lib/active_support/core_ext/module/delegation.rb +20 -119
  49. data/lib/active_support/core_ext/module/deprecation.rb +12 -12
  50. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  51. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  52. data/lib/active_support/core_ext/numeric/conversions.rb +5 -3
  53. data/lib/active_support/core_ext/numeric.rb +0 -1
  54. data/lib/active_support/core_ext/object/blank.rb +45 -1
  55. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  56. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  57. data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
  58. data/lib/active_support/core_ext/object/json.rb +17 -7
  59. data/lib/active_support/core_ext/object/with.rb +46 -0
  60. data/lib/active_support/core_ext/object/with_options.rb +4 -4
  61. data/lib/active_support/core_ext/object.rb +1 -0
  62. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  63. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  64. data/lib/active_support/core_ext/pathname.rb +1 -0
  65. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  66. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  67. data/lib/active_support/core_ext/range.rb +1 -2
  68. data/lib/active_support/core_ext/securerandom.rb +1 -5
  69. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  70. data/lib/active_support/core_ext/string/filters.rb +21 -15
  71. data/lib/active_support/core_ext/string/indent.rb +1 -1
  72. data/lib/active_support/core_ext/string/inflections.rb +16 -5
  73. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  74. data/lib/active_support/core_ext/string/output_safety.rb +34 -177
  75. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  76. data/lib/active_support/core_ext/time/calculations.rb +36 -30
  77. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  78. data/lib/active_support/core_ext/time/conversions.rb +1 -3
  79. data/lib/active_support/core_ext/time/zones.rb +4 -4
  80. data/lib/active_support/core_ext/time.rb +0 -1
  81. data/lib/active_support/core_ext.rb +0 -1
  82. data/lib/active_support/current_attributes.rb +53 -46
  83. data/lib/active_support/deep_mergeable.rb +53 -0
  84. data/lib/active_support/delegation.rb +202 -0
  85. data/lib/active_support/dependencies/autoload.rb +9 -16
  86. data/lib/active_support/deprecation/behaviors.rb +65 -42
  87. data/lib/active_support/deprecation/constant_accessor.rb +47 -25
  88. data/lib/active_support/deprecation/deprecators.rb +104 -0
  89. data/lib/active_support/deprecation/disallowed.rb +3 -5
  90. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  91. data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
  92. data/lib/active_support/deprecation/reporting.rb +49 -27
  93. data/lib/active_support/deprecation.rb +39 -9
  94. data/lib/active_support/deprecator.rb +7 -0
  95. data/lib/active_support/descendants_tracker.rb +66 -172
  96. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  97. data/lib/active_support/duration/iso8601_serializer.rb +1 -4
  98. data/lib/active_support/duration.rb +13 -7
  99. data/lib/active_support/encrypted_configuration.rb +30 -9
  100. data/lib/active_support/encrypted_file.rb +9 -4
  101. data/lib/active_support/environment_inquirer.rb +22 -2
  102. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  103. data/lib/active_support/error_reporter.rb +160 -36
  104. data/lib/active_support/evented_file_update_checker.rb +0 -1
  105. data/lib/active_support/execution_wrapper.rb +4 -5
  106. data/lib/active_support/file_update_checker.rb +5 -3
  107. data/lib/active_support/fork_tracker.rb +4 -32
  108. data/lib/active_support/gem_version.rb +4 -4
  109. data/lib/active_support/gzip.rb +2 -0
  110. data/lib/active_support/hash_with_indifferent_access.rb +41 -25
  111. data/lib/active_support/html_safe_translation.rb +19 -6
  112. data/lib/active_support/i18n.rb +1 -1
  113. data/lib/active_support/i18n_railtie.rb +20 -13
  114. data/lib/active_support/inflector/inflections.rb +2 -0
  115. data/lib/active_support/inflector/methods.rb +23 -11
  116. data/lib/active_support/inflector/transliterate.rb +3 -1
  117. data/lib/active_support/isolated_execution_state.rb +26 -22
  118. data/lib/active_support/json/decoding.rb +2 -1
  119. data/lib/active_support/json/encoding.rb +25 -43
  120. data/lib/active_support/key_generator.rb +9 -1
  121. data/lib/active_support/lazy_load_hooks.rb +6 -4
  122. data/lib/active_support/locale/en.yml +2 -0
  123. data/lib/active_support/log_subscriber.rb +74 -34
  124. data/lib/active_support/logger.rb +22 -60
  125. data/lib/active_support/logger_thread_safe_level.rb +10 -32
  126. data/lib/active_support/message_encryptor.rb +197 -53
  127. data/lib/active_support/message_encryptors.rb +141 -0
  128. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  129. data/lib/active_support/message_pack/extensions.rb +305 -0
  130. data/lib/active_support/message_pack/serializer.rb +63 -0
  131. data/lib/active_support/message_pack.rb +50 -0
  132. data/lib/active_support/message_verifier.rb +220 -89
  133. data/lib/active_support/message_verifiers.rb +135 -0
  134. data/lib/active_support/messages/codec.rb +65 -0
  135. data/lib/active_support/messages/metadata.rb +111 -45
  136. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  137. data/lib/active_support/messages/rotator.rb +34 -32
  138. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  139. data/lib/active_support/multibyte/chars.rb +4 -2
  140. data/lib/active_support/multibyte/unicode.rb +9 -37
  141. data/lib/active_support/notifications/fanout.rb +248 -87
  142. data/lib/active_support/notifications/instrumenter.rb +93 -25
  143. data/lib/active_support/notifications.rb +29 -28
  144. data/lib/active_support/number_helper/number_converter.rb +16 -7
  145. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  146. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  147. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  148. data/lib/active_support/number_helper.rb +379 -318
  149. data/lib/active_support/option_merger.rb +2 -2
  150. data/lib/active_support/ordered_hash.rb +3 -3
  151. data/lib/active_support/ordered_options.rb +67 -15
  152. data/lib/active_support/parameter_filter.rb +84 -69
  153. data/lib/active_support/proxy_object.rb +8 -3
  154. data/lib/active_support/railtie.rb +25 -20
  155. data/lib/active_support/reloader.rb +12 -4
  156. data/lib/active_support/rescuable.rb +2 -0
  157. data/lib/active_support/secure_compare_rotator.rb +16 -9
  158. data/lib/active_support/string_inquirer.rb +4 -2
  159. data/lib/active_support/subscriber.rb +10 -27
  160. data/lib/active_support/syntax_error_proxy.rb +60 -0
  161. data/lib/active_support/tagged_logging.rb +64 -25
  162. data/lib/active_support/test_case.rb +156 -7
  163. data/lib/active_support/testing/assertions.rb +28 -12
  164. data/lib/active_support/testing/autorun.rb +0 -2
  165. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  166. data/lib/active_support/testing/deprecation.rb +20 -27
  167. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  168. data/lib/active_support/testing/isolation.rb +21 -9
  169. data/lib/active_support/testing/method_call_assertions.rb +7 -8
  170. data/lib/active_support/testing/parallelization/server.rb +3 -0
  171. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  172. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  173. data/lib/active_support/testing/stream.rb +1 -1
  174. data/lib/active_support/testing/strict_warnings.rb +43 -0
  175. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  176. data/lib/active_support/testing/time_helpers.rb +38 -16
  177. data/lib/active_support/time_with_zone.rb +12 -18
  178. data/lib/active_support/values/time_zone.rb +25 -14
  179. data/lib/active_support/version.rb +1 -1
  180. data/lib/active_support/xml_mini/jdom.rb +3 -10
  181. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  182. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  183. data/lib/active_support/xml_mini/rexml.rb +1 -1
  184. data/lib/active_support/xml_mini.rb +12 -3
  185. data/lib/active_support.rb +15 -3
  186. metadata +140 -19
  187. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  188. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
  189. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
  190. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  191. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
  192. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
  193. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  194. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
  195. data/lib/active_support/core_ext/uri.rb +0 -5
  196. data/lib/active_support/deprecation/instance_delegator.rb +0 -38
  197. data/lib/active_support/per_thread_registry.rb +0 -65
  198. data/lib/active_support/ruby_features.rb +0 -7
@@ -1,41 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/ruby_features"
3
+ require "active_support/descendants_tracker"
4
4
 
5
5
  class Class
6
- if ActiveSupport::RubyFeatures::CLASS_SUBCLASSES
7
- # Returns an array with all classes that are < than its receiver.
8
- #
9
- # class C; end
10
- # C.descendants # => []
11
- #
12
- # class B < C; end
13
- # C.descendants # => [B]
14
- #
15
- # class A < B; end
16
- # C.descendants # => [B, A]
17
- #
18
- # class D < C; end
19
- # C.descendants # => [B, A, D]
20
- def descendants
21
- subclasses.concat(subclasses.flat_map(&:descendants))
22
- end
23
- else
24
- def descendants
25
- ObjectSpace.each_object(singleton_class).reject do |k|
26
- k.singleton_class? || k == self
27
- end
28
- end
29
- end
30
-
31
- # Returns an array with the direct children of +self+.
6
+ # Returns an array with all classes that are < than its receiver.
7
+ #
8
+ # class C; end
9
+ # C.descendants # => []
32
10
  #
33
- # class Foo; end
34
- # class Bar < Foo; end
35
- # class Baz < Bar; end
11
+ # class B < C; end
12
+ # C.descendants # => [B]
36
13
  #
37
- # Foo.subclasses # => [Bar]
38
- def subclasses
39
- descendants.select { |descendant| descendant.superclass == self }
40
- end unless ActiveSupport::RubyFeatures::CLASS_SUBCLASSES
14
+ # class A < B; end
15
+ # C.descendants # => [B, A]
16
+ #
17
+ # class D < C; end
18
+ # C.descendants # => [B, A, D]
19
+ def descendants
20
+ subclasses.concat(subclasses.flat_map(&:descendants))
21
+ end
22
+
23
+ prepend ActiveSupport::DescendantsTracker::ReloadedClassesFiltering
41
24
  end
@@ -11,4 +11,8 @@ class Date # :nodoc:
11
11
  def blank?
12
12
  false
13
13
  end
14
+
15
+ def present?
16
+ true
17
+ end
14
18
  end
@@ -52,11 +52,10 @@ class Date
52
52
  strftime(formatter)
53
53
  end
54
54
  else
55
- to_default_s
55
+ to_s
56
56
  end
57
57
  end
58
58
  alias_method :to_formatted_s, :to_fs
59
- alias_method :to_default_s, :to_s
60
59
 
61
60
  # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
62
61
  def readable_inspect
@@ -4,5 +4,4 @@ require "active_support/core_ext/date/acts_like"
4
4
  require "active_support/core_ext/date/blank"
5
5
  require "active_support/core_ext/date/calculations"
6
6
  require "active_support/core_ext/date/conversions"
7
- require "active_support/core_ext/date/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
8
7
  require "active_support/core_ext/date/zones"
@@ -157,6 +157,16 @@ module DateAndTime
157
157
  end
158
158
  alias :at_end_of_quarter :end_of_quarter
159
159
 
160
+ # Returns the quarter for a date/time.
161
+ #
162
+ # Date.new(2010, 1, 31).quarter # => 1
163
+ # Date.new(2010, 4, 12).quarter # => 2
164
+ # Date.new(2010, 9, 15).quarter # => 3
165
+ # Date.new(2010, 12, 25).quarter # => 4
166
+ def quarter
167
+ (month / 3.0).ceil
168
+ end
169
+
160
170
  # Returns a new date/time at the beginning of the year.
161
171
  #
162
172
  # today = Date.today # => Fri, 10 Jul 2015
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/module/attribute_accessors"
4
+ require "active_support/core_ext/module/redefine_method"
4
5
 
5
6
  module DateAndTime
6
7
  module Compatibility
@@ -11,7 +12,33 @@ module DateAndTime
11
12
  # of the receiver. For backwards compatibility we're overriding
12
13
  # this behavior, but new apps will have an initializer that sets
13
14
  # this to true, because the new behavior is preferred.
14
- mattr_accessor :preserve_timezone, instance_writer: false, default: false
15
+ mattr_accessor :preserve_timezone, instance_accessor: false, default: nil
16
+
17
+ singleton_class.silence_redefinition_of_method :preserve_timezone
18
+
19
+ #--
20
+ # This re-implements the behaviour of the mattr_reader, instead
21
+ # of prepending on to it, to avoid overcomplicating a module that
22
+ # is in turn included in several places. This will all go away in
23
+ # Rails 8.0 anyway.
24
+ def self.preserve_timezone # :nodoc:
25
+ if @@preserve_timezone.nil?
26
+ # Only warn once, the first time the value is used (which should
27
+ # be the first time #to_time is called).
28
+ ActiveSupport.deprecator.warn(
29
+ "to_time will always preserve the timezone offset of the receiver in Rails 8.0. " \
30
+ "To opt in to the new behavior, set `ActiveSupport.to_time_preserves_timezone = true`."
31
+ )
32
+
33
+ @@preserve_timezone = false
34
+ end
35
+
36
+ @@preserve_timezone
37
+ end
38
+
39
+ def preserve_timezone # :nodoc:
40
+ Compatibility.preserve_timezone
41
+ end
15
42
 
16
43
  # Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
17
44
  #
@@ -11,4 +11,8 @@ class DateTime # :nodoc:
11
11
  def blank?
12
12
  false
13
13
  end
14
+
15
+ def present?
16
+ true
17
+ end
14
18
  end
@@ -36,11 +36,11 @@ class DateTime
36
36
  if formatter = ::Time::DATE_FORMATS[format]
37
37
  formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
38
38
  else
39
- to_default_s
39
+ to_s
40
40
  end
41
41
  end
42
42
  alias_method :to_formatted_s, :to_fs
43
- alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
43
+
44
44
 
45
45
  # Returns a formatted string of the offset from UTC, or an alternative
46
46
  # string if the time zone is already UTC.
@@ -5,4 +5,3 @@ require "active_support/core_ext/date_time/blank"
5
5
  require "active_support/core_ext/date_time/calculations"
6
6
  require "active_support/core_ext/date_time/compatibility"
7
7
  require "active_support/core_ext/date_time/conversions"
8
- require "active_support/core_ext/date_time/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
@@ -10,8 +10,6 @@ module Digest
10
10
  OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
11
11
  X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
12
12
 
13
- mattr_accessor :use_rfc4122_namespaced_uuids, instance_accessor: false, default: false
14
-
15
13
  # Generates a v5 non-random UUID (Universally Unique IDentifier).
16
14
  #
17
15
  # Using OpenSSL::Digest::MD5 generates version 3 UUIDs; OpenSSL::Digest::SHA1 generates version 5 UUIDs.
@@ -55,22 +53,21 @@ module Digest
55
53
  SecureRandom.uuid
56
54
  end
57
55
 
56
+ # Returns the nil UUID. This is a special form of UUID that is specified to
57
+ # have all 128 bits set to zero.
58
+ def self.nil_uuid
59
+ "00000000-0000-0000-0000-000000000000"
60
+ end
61
+
58
62
  def self.pack_uuid_namespace(namespace)
59
63
  if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
60
64
  namespace
61
- elsif use_rfc4122_namespaced_uuids == true
65
+ else
62
66
  match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/)
63
67
 
64
68
  raise ArgumentError, "Only UUIDs are valid namespace identifiers" unless match_data.present?
65
69
 
66
70
  match_data.captures.map { |s| s.to_i(16) }.pack("NnnnnN")
67
- else
68
- ActiveSupport::Deprecation.warn <<~WARNING.squish
69
- Providing a namespace ID that is not one of the constants defined on Digest::UUID generates an incorrect UUID value according to RFC 4122.
70
- To enable the correct behavior, set the Rails.application.config.active_support.use_rfc4122_namespaced_uuids configuration option to true.
71
- WARNING
72
-
73
- namespace
74
71
  end
75
72
  end
76
73
 
@@ -25,18 +25,6 @@ module Enumerable
25
25
  ActiveSupport::EnumerableCoreExt::SoleItemExpectedError = remove_const(:SoleItemExpectedError)
26
26
  singleton_class.prepend(ActiveSupport::EnumerableCoreExt::Constants)
27
27
 
28
- # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
29
- # when we omit an identity.
30
-
31
- # :stopdoc:
32
-
33
- # We can't use Refinements here because Refinements with Module which will be prepended
34
- # doesn't work well https://bugs.ruby-lang.org/issues/13446
35
- alias :_original_sum_with_required_identity :sum
36
- private :_original_sum_with_required_identity
37
-
38
- # :startdoc:
39
-
40
28
  # Calculates the minimum from the extracted elements.
41
29
  #
42
30
  # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
@@ -53,50 +41,6 @@ module Enumerable
53
41
  map(&key).max
54
42
  end
55
43
 
56
- # Calculates a sum from the elements.
57
- #
58
- # payments.sum { |p| p.price * p.tax_rate }
59
- # payments.sum(&:price)
60
- #
61
- # The latter is a shortcut for:
62
- #
63
- # payments.inject(0) { |sum, p| sum + p.price }
64
- #
65
- # It can also calculate the sum without the use of a block.
66
- #
67
- # [5, 15, 10].sum # => 30
68
- # ['foo', 'bar'].sum('') # => "foobar"
69
- # [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]
70
- #
71
- # The default sum of an empty list is zero. You can override this default:
72
- #
73
- # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
74
- def sum(identity = nil, &block)
75
- if identity
76
- _original_sum_with_required_identity(identity, &block)
77
- elsif block_given?
78
- map(&block).sum
79
- else
80
- first = true
81
-
82
- reduce(nil) do |sum, value|
83
- if first
84
- first = false
85
-
86
- unless value.is_a?(Numeric) || value.respond_to?(:coerce)
87
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
88
- Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
89
- Sum of non-numeric elements requires an initial argument.
90
- MSG
91
- end
92
- value
93
- else
94
- sum + value
95
- end
96
- end || 0
97
- end
98
- end
99
-
100
44
  # Convert an enumerable to a hash, using the block result as the key and the
101
45
  # element as the value.
102
46
  #
@@ -289,38 +233,22 @@ end
289
233
  class Range # :nodoc:
290
234
  # Optimize range sum to use arithmetic progression if a block is not given and
291
235
  # we have a range of numeric values.
292
- def sum(identity = nil)
236
+ def sum(initial_value = 0)
293
237
  if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
294
238
  super
295
239
  else
296
240
  actual_last = exclude_end? ? (last - 1) : last
297
241
  if actual_last >= first
298
- sum = identity || 0
242
+ sum = initial_value || 0
299
243
  sum + (actual_last - first + 1) * (actual_last + first) / 2
300
244
  else
301
- identity || 0
245
+ initial_value || 0
302
246
  end
303
247
  end
304
248
  end
305
249
  end
306
250
 
307
- # Using Refinements here in order not to expose our internal method
308
- using Module.new {
309
- refine Array do
310
- alias :orig_sum :sum
311
- end
312
- }
313
-
314
251
  class Array # :nodoc:
315
- def sum(init = nil, &block)
316
- if init.is_a?(Numeric) || first.is_a?(Numeric)
317
- init ||= 0
318
- orig_sum(init, &block)
319
- else
320
- super
321
- end
322
- end
323
-
324
252
  # Removes all blank elements from the +Array+ in place and returns self.
325
253
  # Uses Object#blank? for determining if an item is blank.
326
254
  #
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module ActiveSupport
6
+ module CoreExt
7
+ module ERBUtil
8
+ # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
9
+ # This method is not for public consumption! Seriously!
10
+ def html_escape(s) # :nodoc:
11
+ s = s.to_s
12
+ if s.html_safe?
13
+ s
14
+ else
15
+ super(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))
16
+ end
17
+ end
18
+ alias :unwrapped_html_escape :html_escape # :nodoc:
19
+
20
+ # A utility method for escaping HTML tag characters.
21
+ # This method is also aliased as <tt>h</tt>.
22
+ #
23
+ # puts html_escape('is a > 0 & a < 10?')
24
+ # # => is a &gt; 0 &amp; a &lt; 10?
25
+ def html_escape(s) # rubocop:disable Lint/DuplicateMethods
26
+ unwrapped_html_escape(s).html_safe
27
+ end
28
+ alias h html_escape
29
+ end
30
+
31
+ module ERBUtilPrivate
32
+ include ERBUtil
33
+ private :unwrapped_html_escape, :html_escape, :h
34
+ end
35
+ end
36
+ end
37
+
38
+ class ERB
39
+ module Util
40
+ HTML_ESCAPE = { "&" => "&amp;", ">" => "&gt;", "<" => "&lt;", '"' => "&quot;", "'" => "&#39;" }
41
+ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
42
+
43
+ # Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name
44
+ TAG_NAME_START_CODEPOINTS = "@:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}" \
45
+ "\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \
46
+ "\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}"
47
+ INVALID_TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_CODEPOINTS}]/
48
+ TAG_NAME_FOLLOWING_CODEPOINTS = "#{TAG_NAME_START_CODEPOINTS}\\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}"
49
+ INVALID_TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_FOLLOWING_CODEPOINTS}]/
50
+ SAFE_XML_TAG_NAME_REGEXP = /\A[#{TAG_NAME_START_CODEPOINTS}][#{TAG_NAME_FOLLOWING_CODEPOINTS}]*\z/
51
+ TAG_NAME_REPLACEMENT_CHAR = "_"
52
+
53
+ prepend ActiveSupport::CoreExt::ERBUtilPrivate
54
+ singleton_class.prepend ActiveSupport::CoreExt::ERBUtil
55
+
56
+ # A utility method for escaping HTML without affecting existing escaped entities.
57
+ #
58
+ # html_escape_once('1 < 2 &amp; 3')
59
+ # # => "1 &lt; 2 &amp; 3"
60
+ #
61
+ # html_escape_once('&lt;&lt; Accept & Checkout')
62
+ # # => "&lt;&lt; Accept &amp; Checkout"
63
+ def html_escape_once(s)
64
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE).html_safe
65
+ end
66
+
67
+ module_function :html_escape_once
68
+
69
+ # A utility method for escaping HTML entities in JSON strings. Specifically, the
70
+ # &, > and < characters are replaced with their equivalent unicode escaped form -
71
+ # \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
72
+ # escaped as they are treated as newline characters in some JavaScript engines.
73
+ # These sequences have identical meaning as the original characters inside the
74
+ # context of a JSON string, so assuming the input is a valid and well-formed
75
+ # JSON value, the output will have equivalent meaning when parsed:
76
+ #
77
+ # json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
78
+ # # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
79
+ #
80
+ # json_escape(json)
81
+ # # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
82
+ #
83
+ # JSON.parse(json) == JSON.parse(json_escape(json))
84
+ # # => true
85
+ #
86
+ # The intended use case for this method is to escape JSON strings before including
87
+ # them inside a script tag to avoid XSS vulnerability:
88
+ #
89
+ # <script>
90
+ # var currentUser = <%= raw json_escape(current_user.to_json) %>;
91
+ # </script>
92
+ #
93
+ # It is necessary to +raw+ the result of +json_escape+, so that quotation marks
94
+ # don't get converted to <tt>&quot;</tt> entities. +json_escape+ doesn't
95
+ # automatically flag the result as HTML safe, since the raw value is unsafe to
96
+ # use inside HTML attributes.
97
+ #
98
+ # If your JSON is being used downstream for insertion into the DOM, be aware of
99
+ # whether or not it is being inserted via <tt>html()</tt>. Most jQuery plugins do this.
100
+ # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
101
+ # content returned by your JSON.
102
+ #
103
+ # If you need to output JSON elsewhere in your HTML, you can just do something
104
+ # like this, as any unsafe characters (including quotation marks) will be
105
+ # automatically escaped for you:
106
+ #
107
+ # <div data-user-info="<%= current_user.to_json %>">...</div>
108
+ #
109
+ # WARNING: this helper only works with valid JSON. Using this on non-JSON values
110
+ # will open up serious XSS vulnerabilities. For example, if you replace the
111
+ # +current_user.to_json+ in the example above with user input instead, the browser
112
+ # will happily <tt>eval()</tt> that string as JavaScript.
113
+ #
114
+ # The escaping performed in this method is identical to those performed in the
115
+ # Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
116
+ # set to true. Because this transformation is idempotent, this helper can be
117
+ # applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
118
+ #
119
+ # Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
120
+ # is enabled, or if you are unsure where your JSON string originated from, it
121
+ # is recommended that you always apply this helper (other libraries, such as the
122
+ # JSON gem, do not provide this kind of protection by default; also some gems
123
+ # might override +to_json+ to bypass Active Support's encoder).
124
+ def json_escape(s)
125
+ result = s.to_s.dup
126
+ result.gsub!(">", '\u003e')
127
+ result.gsub!("<", '\u003c')
128
+ result.gsub!("&", '\u0026')
129
+ result.gsub!("\u2028", '\u2028')
130
+ result.gsub!("\u2029", '\u2029')
131
+ s.html_safe? ? result.html_safe : result
132
+ end
133
+
134
+ module_function :json_escape
135
+
136
+ # A utility method for escaping XML names of tags and names of attributes.
137
+ #
138
+ # xml_name_escape('1 < 2 & 3')
139
+ # # => "1___2___3"
140
+ #
141
+ # It follows the requirements of the specification: https://www.w3.org/TR/REC-xml/#NT-Name
142
+ def xml_name_escape(name)
143
+ name = name.to_s
144
+ return "" if name.blank?
145
+ return name if name.match?(SAFE_XML_TAG_NAME_REGEXP)
146
+
147
+ starting_char = name[0]
148
+ starting_char.gsub!(INVALID_TAG_NAME_START_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
149
+
150
+ return starting_char if name.size == 1
151
+
152
+ following_chars = name[1..-1]
153
+ following_chars.gsub!(INVALID_TAG_NAME_FOLLOWING_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
154
+
155
+ starting_char << following_chars
156
+ end
157
+ module_function :xml_name_escape
158
+
159
+ # Tokenizes a line of ERB. This is really just for error reporting and
160
+ # nobody should use it.
161
+ def self.tokenize(source) # :nodoc:
162
+ require "strscan"
163
+ source = StringScanner.new(source.chomp)
164
+ tokens = []
165
+
166
+ start_re = /<%(?:={1,2}|-|\#|%)?/m
167
+ finish_re = /(?:[-=])?%>/m
168
+
169
+ while !source.eos?
170
+ pos = source.pos
171
+ source.scan_until(/(?:#{start_re}|#{finish_re})/)
172
+ raise NotImplementedError if source.matched.nil?
173
+ len = source.pos - source.matched.bytesize - pos
174
+
175
+ case source.matched
176
+ when start_re
177
+ tokens << [:TEXT, source.string[pos, len]] if len > 0
178
+ tokens << [:OPEN, source.matched]
179
+ if source.scan(/(.*?)(?=#{finish_re}|\z)/m)
180
+ tokens << [:CODE, source.matched] unless source.matched.empty?
181
+ tokens << [:CLOSE, source.scan(finish_re)] unless source.eos?
182
+ else
183
+ raise NotImplementedError
184
+ end
185
+ when finish_re
186
+ tokens << [:CODE, source.string[pos, len]] if len > 0
187
+ tokens << [:CLOSE, source.matched]
188
+ else
189
+ raise NotImplementedError, source.matched
190
+ end
191
+
192
+ unless source.eos? || source.exist?(start_re) || source.exist?(finish_re)
193
+ tokens << [:TEXT, source.rest]
194
+ source.terminate
195
+ end
196
+ end
197
+
198
+ tokens
199
+ end
200
+ end
201
+ end
@@ -68,7 +68,7 @@ class Hash
68
68
  #
69
69
  # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
70
70
  #
71
- # The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
71
+ # The default XML builder is a fresh instance of +Builder::XmlMarkup+. You can
72
72
  # configure your own builder with the <tt>:builder</tt> option. The method also accepts
73
73
  # options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
74
74
  def to_xml(options = {})
@@ -1,6 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deep_mergeable"
4
+
3
5
  class Hash
6
+ include ActiveSupport::DeepMergeable
7
+
8
+ ##
9
+ # :method: deep_merge
10
+ # :call-seq: deep_merge(other_hash, &block)
11
+ #
4
12
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
5
13
  #
6
14
  # h1 = { a: true, b: { c: [1, 2, 3] } }
@@ -15,20 +23,20 @@ class Hash
15
23
  # h2 = { b: 250, c: { c1: 200 } }
16
24
  # h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
17
25
  # # => { a: 100, b: 450, c: { c1: 300 } }
18
- def deep_merge(other_hash, &block)
19
- dup.deep_merge!(other_hash, &block)
20
- end
26
+ #
27
+ #--
28
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge.
29
+
30
+ ##
31
+ # :method: deep_merge!
32
+ # :call-seq: deep_merge!(other_hash, &block)
33
+ #
34
+ # Same as #deep_merge, but modifies +self+.
35
+ #
36
+ #--
37
+ # Implemented by ActiveSupport::DeepMergeable#deep_merge!.
21
38
 
22
- # Same as +deep_merge+, but modifies +self+.
23
- def deep_merge!(other_hash, &block)
24
- merge!(other_hash) do |key, this_val, other_val|
25
- if this_val.is_a?(Hash) && other_val.is_a?(Hash)
26
- this_val.deep_merge(other_val, &block)
27
- elsif block_given?
28
- block.call(key, this_val, other_val)
29
- else
30
- other_val
31
- end
32
- end
39
+ def deep_merge?(other) # :nodoc:
40
+ other.is_a?(Hash)
33
41
  end
34
42
  end
@@ -8,13 +8,13 @@ class Hash
8
8
  # hash.stringify_keys
9
9
  # # => {"name"=>"Rob", "age"=>"28"}
10
10
  def stringify_keys
11
- transform_keys(&:to_s)
11
+ transform_keys { |k| Symbol === k ? k.name : k.to_s }
12
12
  end
13
13
 
14
14
  # Destructively converts all keys to strings. Same as
15
15
  # +stringify_keys+, but modifies +self+.
16
16
  def stringify_keys!
17
- transform_keys!(&:to_s)
17
+ transform_keys! { |k| Symbol === k ? k.name : k.to_s }
18
18
  end
19
19
 
20
20
  # Returns a new hash with all keys converted to symbols, as long as
@@ -82,14 +82,14 @@ class Hash
82
82
  # hash.deep_stringify_keys
83
83
  # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
84
84
  def deep_stringify_keys
85
- deep_transform_keys(&:to_s)
85
+ deep_transform_keys { |k| Symbol === k ? k.name : k.to_s }
86
86
  end
87
87
 
88
88
  # Destructively converts all keys to strings.
89
89
  # This includes the keys from the root hash and from all
90
90
  # nested hashes and arrays.
91
91
  def deep_stringify_keys!
92
- deep_transform_keys!(&:to_s)
92
+ deep_transform_keys! { |k| Symbol === k ? k.name : k.to_s }
93
93
  end
94
94
 
95
95
  # Returns a new hash with all keys converted to symbols, as long as
@@ -19,16 +19,27 @@ class Module
19
19
  end
20
20
  alias_method :attr_internal, :attr_internal_accessor
21
21
 
22
- class << self; attr_accessor :attr_internal_naming_format end
23
- self.attr_internal_naming_format = "@_%s"
22
+ class << self
23
+ attr_reader :attr_internal_naming_format
24
24
 
25
- private
26
- def attr_internal_ivar_name(attr)
27
- Module.attr_internal_naming_format % attr
25
+ def attr_internal_naming_format=(format)
26
+ if format.start_with?("@")
27
+ ActiveSupport.deprecator.warn <<~MESSAGE
28
+ Setting `attr_internal_naming_format` with a `@` prefix is deprecated and will be removed in Rails 8.0.
29
+
30
+ You can simply replace #{format.inspect} by #{format.delete_prefix("@").inspect}.
31
+ MESSAGE
32
+
33
+ format = format.delete_prefix("@")
34
+ end
35
+ @attr_internal_naming_format = format
28
36
  end
37
+ end
38
+ self.attr_internal_naming_format = "_%s"
29
39
 
40
+ private
30
41
  def attr_internal_define(attr_name, type)
31
- internal_name = attr_internal_ivar_name(attr_name).delete_prefix("@")
42
+ internal_name = Module.attr_internal_naming_format % attr_name
32
43
  # use native attr_* methods as they are faster on some Ruby implementations
33
44
  public_send("attr_#{type}", internal_name)
34
45
  attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer