activesupport 6.1.1 → 7.0.2.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +231 -383
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support/actionable_error.rb +1 -1
  6. data/lib/active_support/array_inquirer.rb +0 -2
  7. data/lib/active_support/benchmarkable.rb +2 -2
  8. data/lib/active_support/cache/file_store.rb +16 -10
  9. data/lib/active_support/cache/mem_cache_store.rb +133 -34
  10. data/lib/active_support/cache/memory_store.rb +23 -15
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +42 -67
  13. data/lib/active_support/cache/strategy/local_cache.rb +35 -61
  14. data/lib/active_support/cache.rb +196 -46
  15. data/lib/active_support/callbacks.rb +180 -81
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +5 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  19. data/lib/active_support/concurrency/share_lock.rb +2 -2
  20. data/lib/active_support/configurable.rb +6 -3
  21. data/lib/active_support/configuration_file.rb +7 -2
  22. data/lib/active_support/core_ext/array/access.rb +1 -5
  23. data/lib/active_support/core_ext/array/conversions.rb +13 -11
  24. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  25. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  26. data/lib/active_support/core_ext/array.rb +1 -0
  27. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  28. data/lib/active_support/core_ext/class/subclasses.rb +25 -17
  29. data/lib/active_support/core_ext/date/blank.rb +1 -1
  30. data/lib/active_support/core_ext/date/calculations.rb +4 -4
  31. data/lib/active_support/core_ext/date/conversions.rb +11 -11
  32. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  33. data/lib/active_support/core_ext/date.rb +1 -0
  34. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  36. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  37. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  38. data/lib/active_support/core_ext/date_time.rb +1 -0
  39. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  40. data/lib/active_support/core_ext/enumerable.rb +78 -26
  41. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  42. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  43. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  44. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
  45. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
  46. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  47. data/lib/active_support/core_ext/name_error.rb +2 -8
  48. data/lib/active_support/core_ext/numeric/conversions.rb +79 -76
  49. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  50. data/lib/active_support/core_ext/numeric.rb +1 -0
  51. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  52. data/lib/active_support/core_ext/object/blank.rb +2 -2
  53. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  54. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  55. data/lib/active_support/core_ext/object/json.rb +29 -24
  56. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  57. data/lib/active_support/core_ext/object/try.rb +20 -20
  58. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  59. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  60. data/lib/active_support/core_ext/pathname.rb +3 -0
  61. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  62. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  63. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  64. data/lib/active_support/core_ext/range/each.rb +1 -1
  65. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -25
  66. data/lib/active_support/core_ext/range.rb +1 -1
  67. data/lib/active_support/core_ext/string/filters.rb +1 -1
  68. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  69. data/lib/active_support/core_ext/string/output_safety.rb +60 -36
  70. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  71. data/lib/active_support/core_ext/time/calculations.rb +7 -5
  72. data/lib/active_support/core_ext/time/conversions.rb +13 -12
  73. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  74. data/lib/active_support/core_ext/time/zones.rb +4 -19
  75. data/lib/active_support/core_ext/time.rb +1 -0
  76. data/lib/active_support/core_ext/uri.rb +3 -27
  77. data/lib/active_support/core_ext.rb +1 -0
  78. data/lib/active_support/current_attributes.rb +32 -14
  79. data/lib/active_support/dependencies/interlock.rb +10 -18
  80. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  81. data/lib/active_support/dependencies.rb +58 -788
  82. data/lib/active_support/deprecation/behaviors.rb +4 -1
  83. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  84. data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
  85. data/lib/active_support/deprecation.rb +1 -1
  86. data/lib/active_support/descendants_tracker.rb +174 -68
  87. data/lib/active_support/digest.rb +5 -3
  88. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  89. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  90. data/lib/active_support/duration.rb +81 -51
  91. data/lib/active_support/encrypted_configuration.rb +13 -2
  92. data/lib/active_support/encrypted_file.rb +1 -1
  93. data/lib/active_support/environment_inquirer.rb +1 -1
  94. data/lib/active_support/error_reporter.rb +117 -0
  95. data/lib/active_support/evented_file_update_checker.rb +1 -1
  96. data/lib/active_support/execution_context/test_helper.rb +13 -0
  97. data/lib/active_support/execution_context.rb +53 -0
  98. data/lib/active_support/execution_wrapper.rb +43 -21
  99. data/lib/active_support/executor/test_helper.rb +7 -0
  100. data/lib/active_support/fork_tracker.rb +19 -10
  101. data/lib/active_support/gem_version.rb +4 -4
  102. data/lib/active_support/hash_with_indifferent_access.rb +9 -2
  103. data/lib/active_support/html_safe_translation.rb +43 -0
  104. data/lib/active_support/i18n.rb +1 -0
  105. data/lib/active_support/i18n_railtie.rb +1 -1
  106. data/lib/active_support/inflector/inflections.rb +23 -7
  107. data/lib/active_support/inflector/methods.rb +24 -48
  108. data/lib/active_support/isolated_execution_state.rb +64 -0
  109. data/lib/active_support/json/encoding.rb +3 -3
  110. data/lib/active_support/key_generator.rb +18 -1
  111. data/lib/active_support/locale/en.yml +2 -2
  112. data/lib/active_support/log_subscriber.rb +13 -3
  113. data/lib/active_support/logger_thread_safe_level.rb +4 -13
  114. data/lib/active_support/message_encryptor.rb +8 -3
  115. data/lib/active_support/message_verifier.rb +46 -14
  116. data/lib/active_support/messages/metadata.rb +2 -2
  117. data/lib/active_support/multibyte/chars.rb +10 -11
  118. data/lib/active_support/multibyte/unicode.rb +0 -12
  119. data/lib/active_support/multibyte.rb +1 -1
  120. data/lib/active_support/notifications/fanout.rb +91 -65
  121. data/lib/active_support/notifications/instrumenter.rb +32 -15
  122. data/lib/active_support/notifications.rb +16 -22
  123. data/lib/active_support/number_helper/number_converter.rb +1 -3
  124. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  125. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  126. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  127. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  128. data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
  129. data/lib/active_support/number_helper/rounding_helper.rb +2 -6
  130. data/lib/active_support/number_helper.rb +0 -2
  131. data/lib/active_support/option_merger.rb +8 -16
  132. data/lib/active_support/ordered_hash.rb +1 -1
  133. data/lib/active_support/parameter_filter.rb +6 -1
  134. data/lib/active_support/per_thread_registry.rb +5 -0
  135. data/lib/active_support/railtie.rb +69 -19
  136. data/lib/active_support/reloader.rb +1 -1
  137. data/lib/active_support/rescuable.rb +2 -2
  138. data/lib/active_support/ruby_features.rb +7 -0
  139. data/lib/active_support/secure_compare_rotator.rb +1 -1
  140. data/lib/active_support/security_utils.rb +1 -1
  141. data/lib/active_support/string_inquirer.rb +0 -2
  142. data/lib/active_support/subscriber.rb +7 -18
  143. data/lib/active_support/tagged_logging.rb +2 -2
  144. data/lib/active_support/test_case.rb +9 -21
  145. data/lib/active_support/testing/assertions.rb +35 -5
  146. data/lib/active_support/testing/deprecation.rb +52 -1
  147. data/lib/active_support/testing/isolation.rb +2 -2
  148. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  149. data/lib/active_support/testing/parallelization/server.rb +4 -0
  150. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  151. data/lib/active_support/testing/parallelization.rb +4 -0
  152. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  153. data/lib/active_support/testing/stream.rb +3 -5
  154. data/lib/active_support/testing/tagged_logging.rb +1 -1
  155. data/lib/active_support/testing/time_helpers.rb +13 -2
  156. data/lib/active_support/time_with_zone.rb +55 -14
  157. data/lib/active_support/values/time_zone.rb +31 -10
  158. data/lib/active_support/xml_mini/jdom.rb +1 -1
  159. data/lib/active_support/xml_mini/libxml.rb +5 -5
  160. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  161. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  162. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  163. data/lib/active_support/xml_mini/rexml.rb +1 -1
  164. data/lib/active_support/xml_mini.rb +5 -4
  165. data/lib/active_support.rb +17 -1
  166. metadata +26 -23
  167. data/lib/active_support/core_ext/marshal.rb +0 -26
  168. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -19,7 +19,7 @@ class Array
19
19
  # ["1", "2"]
20
20
  # ["3", "4"]
21
21
  # ["5"]
22
- def in_groups_of(number, fill_with = nil)
22
+ def in_groups_of(number, fill_with = nil, &block)
23
23
  if number.to_i <= 0
24
24
  raise ArgumentError,
25
25
  "Group size must be a positive integer, was #{number.inspect}"
@@ -36,7 +36,7 @@ class Array
36
36
  end
37
37
 
38
38
  if block_given?
39
- collection.each_slice(number) { |slice| yield(slice) }
39
+ collection.each_slice(number, &block)
40
40
  else
41
41
  collection.each_slice(number).to_a
42
42
  end
@@ -59,7 +59,7 @@ class Array
59
59
  # ["1", "2", "3"]
60
60
  # ["4", "5"]
61
61
  # ["6", "7"]
62
- def in_groups(number, fill_with = nil)
62
+ def in_groups(number, fill_with = nil, &block)
63
63
  # size.div number gives minor group size;
64
64
  # size % number gives how many objects need extra accommodation;
65
65
  # each group hold either division or division + 1 items.
@@ -79,7 +79,7 @@ class Array
79
79
  end
80
80
 
81
81
  if block_given?
82
- groups.each { |g| yield(g) }
82
+ groups.each(&block)
83
83
  else
84
84
  groups
85
85
  end
@@ -90,11 +90,11 @@ class Array
90
90
  #
91
91
  # [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
92
92
  # (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
93
- def split(value = nil)
93
+ def split(value = nil, &block)
94
94
  arr = dup
95
95
  result = []
96
96
  if block_given?
97
- while (idx = arr.index { |i| yield i })
97
+ while (idx = arr.index(&block))
98
98
  result << arr.shift(idx)
99
99
  arr.shift
100
100
  end
@@ -3,6 +3,7 @@
3
3
  require "active_support/core_ext/array/wrap"
4
4
  require "active_support/core_ext/array/access"
5
5
  require "active_support/core_ext/array/conversions"
6
+ require "active_support/core_ext/array/deprecated_conversions" unless ENV["RAILS_DISABLE_DEPRECATED_TO_S_CONVERSION"]
6
7
  require "active_support/core_ext/array/extract"
7
8
  require "active_support/core_ext/array/extract_options"
8
9
  require "active_support/core_ext/array/grouping"
@@ -4,7 +4,7 @@ require "bigdecimal"
4
4
  require "bigdecimal/util"
5
5
 
6
6
  module ActiveSupport
7
- module BigDecimalWithDefaultFormat #:nodoc:
7
+ module BigDecimalWithDefaultFormat # :nodoc:
8
8
  def to_s(format = "F")
9
9
  super(format)
10
10
  end
@@ -1,22 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/ruby_features"
4
+
3
5
  class Class
4
- # Returns an array with all classes that are < than its receiver.
5
- #
6
- # class C; end
7
- # C.descendants # => []
8
- #
9
- # class B < C; end
10
- # C.descendants # => [B]
11
- #
12
- # class A < B; end
13
- # C.descendants # => [B, A]
14
- #
15
- # class D < C; end
16
- # C.descendants # => [B, A, D]
17
- def descendants
18
- ObjectSpace.each_object(singleton_class).reject do |k|
19
- k.singleton_class? || k == self
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
20
28
  end
21
29
  end
22
30
 
@@ -29,5 +37,5 @@ class Class
29
37
  # Foo.subclasses # => [Bar]
30
38
  def subclasses
31
39
  descendants.select { |descendant| descendant.superclass == self }
32
- end
40
+ end unless ActiveSupport::RubyFeatures::CLASS_SUBCLASSES
33
41
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "date"
4
4
 
5
- class Date #:nodoc:
5
+ class Date # :nodoc:
6
6
  # No Date is blank:
7
7
  #
8
8
  # Date.today.blank? # => false
@@ -17,7 +17,7 @@ class Date
17
17
  # If <tt>Date.beginning_of_week</tt> has not been set for the current request, returns the week start specified in <tt>config.beginning_of_week</tt>.
18
18
  # If no config.beginning_of_week was specified, returns :monday.
19
19
  def beginning_of_week
20
- Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
20
+ ::ActiveSupport::IsolatedExecutionState[:beginning_of_week] || beginning_of_week_default || :monday
21
21
  end
22
22
 
23
23
  # Sets <tt>Date.beginning_of_week</tt> to a week start (e.g. :monday) for current request/thread.
@@ -25,7 +25,7 @@ class Date
25
25
  # This method accepts any of the following day symbols:
26
26
  # :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
27
27
  def beginning_of_week=(week_start)
28
- Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
28
+ ::ActiveSupport::IsolatedExecutionState[:beginning_of_week] = find_beginning_of_week!(week_start)
29
29
  end
30
30
 
31
31
  # Returns week start day symbol (e.g. :monday), or raises an +ArgumentError+ for invalid day symbol.
@@ -87,7 +87,7 @@ class Date
87
87
  end
88
88
  alias :at_end_of_day :end_of_day
89
89
 
90
- def plus_with_duration(other) #:nodoc:
90
+ def plus_with_duration(other) # :nodoc:
91
91
  if ActiveSupport::Duration === other
92
92
  other.since(self)
93
93
  else
@@ -97,7 +97,7 @@ class Date
97
97
  alias_method :plus_without_duration, :+
98
98
  alias_method :+, :plus_with_duration
99
99
 
100
- def minus_with_duration(other) #:nodoc:
100
+ def minus_with_duration(other) # :nodoc:
101
101
  if ActiveSupport::Duration === other
102
102
  plus_with_duration(-other)
103
103
  else
@@ -22,21 +22,21 @@ class Date
22
22
 
23
23
  # Convert to a formatted string. See DATE_FORMATS for predefined formats.
24
24
  #
25
- # This method is aliased to <tt>to_s</tt>.
25
+ # This method is aliased to <tt>to_formatted_s</tt>.
26
26
  #
27
27
  # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
28
28
  #
29
+ # date.to_fs(:db) # => "2007-11-10"
29
30
  # date.to_formatted_s(:db) # => "2007-11-10"
30
- # date.to_s(:db) # => "2007-11-10"
31
31
  #
32
- # date.to_formatted_s(:short) # => "10 Nov"
33
- # date.to_formatted_s(:number) # => "20071110"
34
- # date.to_formatted_s(:long) # => "November 10, 2007"
35
- # date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
36
- # date.to_formatted_s(:rfc822) # => "10 Nov 2007"
37
- # date.to_formatted_s(:iso8601) # => "2007-11-10"
32
+ # date.to_fs(:short) # => "10 Nov"
33
+ # date.to_fs(:number) # => "20071110"
34
+ # date.to_fs(:long) # => "November 10, 2007"
35
+ # date.to_fs(:long_ordinal) # => "November 10th, 2007"
36
+ # date.to_fs(:rfc822) # => "10 Nov 2007"
37
+ # date.to_fs(:iso8601) # => "2007-11-10"
38
38
  #
39
- # == Adding your own date formats to to_formatted_s
39
+ # == Adding your own date formats to to_fs
40
40
  # You can add your own formats to the Date::DATE_FORMATS hash.
41
41
  # Use the format name as the hash key and either a strftime string
42
42
  # or Proc instance that takes a date argument as the value.
@@ -44,7 +44,7 @@ class Date
44
44
  # # config/initializers/date_formats.rb
45
45
  # Date::DATE_FORMATS[:month_and_year] = '%B %Y'
46
46
  # Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
47
- def to_formatted_s(format = :default)
47
+ def to_fs(format = :default)
48
48
  if formatter = DATE_FORMATS[format]
49
49
  if formatter.respond_to?(:call)
50
50
  formatter.call(self).to_s
@@ -55,8 +55,8 @@ class Date
55
55
  to_default_s
56
56
  end
57
57
  end
58
+ alias_method :to_formatted_s, :to_fs
58
59
  alias_method :to_default_s, :to_s
59
- alias_method :to_s, :to_formatted_s
60
60
 
61
61
  # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
62
62
  def readable_inspect
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ class Date
6
+ NOT_SET = Object.new # :nodoc:
7
+ def to_s(format = NOT_SET) # :nodoc:
8
+ if formatter = DATE_FORMATS[format]
9
+ ActiveSupport::Deprecation.warn(
10
+ "Date#to_s(#{format.inspect}) is deprecated. Please use Date#to_fs(#{format.inspect}) instead."
11
+ )
12
+ if formatter.respond_to?(:call)
13
+ formatter.call(self).to_s
14
+ else
15
+ strftime(formatter)
16
+ end
17
+ elsif format == NOT_SET
18
+ to_default_s
19
+ else
20
+ ActiveSupport::Deprecation.warn(
21
+ "Date#to_s(#{format.inspect}) is deprecated. Please use Date#to_fs(#{format.inspect}) instead."
22
+ )
23
+ to_default_s
24
+ end
25
+ end
26
+ end
@@ -4,4 +4,5 @@ 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"]
7
8
  require "active_support/core_ext/date/zones"
@@ -15,7 +15,7 @@ module DateAndTime
15
15
 
16
16
  # Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
17
17
  #
18
- # When `true`, it returns local times with an UTC offset, with `false` local
18
+ # When `true`, it returns local times with a UTC offset, with `false` local
19
19
  # times are returned as UTC.
20
20
  #
21
21
  # # Given this zone:
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "date"
4
4
 
5
- class DateTime #:nodoc:
5
+ class DateTime # :nodoc:
6
6
  # No DateTime is ever blank:
7
7
  #
8
8
  # DateTime.now.blank? # => false
@@ -9,21 +9,21 @@ require "active_support/values/time_zone"
9
9
  class DateTime
10
10
  # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
11
11
  #
12
- # This method is aliased to <tt>to_s</tt>.
12
+ # This method is aliased to <tt>to_formatted_s</tt>.
13
13
  #
14
14
  # === Examples
15
15
  # datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
16
16
  #
17
- # datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
18
- # datetime.to_s(:db) # => "2007-12-04 00:00:00"
19
- # datetime.to_s(:number) # => "20071204000000"
20
- # datetime.to_formatted_s(:short) # => "04 Dec 00:00"
21
- # datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
22
- # datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
23
- # datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
24
- # datetime.to_formatted_s(:iso8601) # => "2007-12-04T00:00:00+00:00"
17
+ # datetime.to_fs(:db) # => "2007-12-04 00:00:00"
18
+ # datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
19
+ # datetime.to_fs(:number) # => "20071204000000"
20
+ # datetime.to_fs(:short) # => "04 Dec 00:00"
21
+ # datetime.to_fs(:long) # => "December 04, 2007 00:00"
22
+ # datetime.to_fs(:long_ordinal) # => "December 4th, 2007 00:00"
23
+ # datetime.to_fs(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
24
+ # datetime.to_fs(:iso8601) # => "2007-12-04T00:00:00+00:00"
25
25
  #
26
- # == Adding your own datetime formats to to_formatted_s
26
+ # == Adding your own datetime formats to to_fs
27
27
  # DateTime formats are shared with Time. You can add your own to the
28
28
  # Time::DATE_FORMATS hash. Use the format name as the hash key and
29
29
  # either a strftime string or Proc instance that takes a time or
@@ -32,15 +32,15 @@ class DateTime
32
32
  # # config/initializers/time_formats.rb
33
33
  # Time::DATE_FORMATS[:month_and_year] = '%B %Y'
34
34
  # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
35
- def to_formatted_s(format = :default)
35
+ def to_fs(format = :default)
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
39
  to_default_s
40
40
  end
41
41
  end
42
+ alias_method :to_formatted_s, :to_fs
42
43
  alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
43
- alias_method :to_s, :to_formatted_s
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.
@@ -54,7 +54,7 @@ class DateTime
54
54
 
55
55
  # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
56
56
  def readable_inspect
57
- to_s(:rfc822)
57
+ to_fs(:rfc822)
58
58
  end
59
59
  alias_method :default_inspect, :inspect
60
60
  alias_method :inspect, :readable_inspect
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+
5
+ class DateTime
6
+ NOT_SET = Object.new # :nodoc:
7
+ def to_s(format = NOT_SET) # :nodoc:
8
+ if formatter = ::Time::DATE_FORMATS[format]
9
+ ActiveSupport::Deprecation.warn(
10
+ "DateTime#to_s(#{format.inspect}) is deprecated. Please use DateTime#to_fs(#{format.inspect}) instead."
11
+ )
12
+ formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
13
+ elsif format == NOT_SET
14
+ to_default_s
15
+ else
16
+ ActiveSupport::Deprecation.warn(
17
+ "DateTime#to_s(#{format.inspect}) is deprecated. Please use DateTime#to_fs(#{format.inspect}) instead."
18
+ )
19
+ to_default_s
20
+ end
21
+ end
22
+ end
@@ -5,3 +5,4 @@ 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"]
@@ -1,29 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "securerandom"
4
+ require "openssl"
4
5
 
5
6
  module Digest
6
7
  module UUID
7
- DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
8
- URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
9
- OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
10
- X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" #:nodoc:
8
+ DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
9
+ URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
10
+ OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
11
+ X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
12
+
13
+ mattr_accessor :use_rfc4122_namespaced_uuids, instance_accessor: false, default: false
11
14
 
12
15
  # Generates a v5 non-random UUID (Universally Unique IDentifier).
13
16
  #
14
- # Using Digest::MD5 generates version 3 UUIDs; Digest::SHA1 generates version 5 UUIDs.
17
+ # Using OpenSSL::Digest::MD5 generates version 3 UUIDs; OpenSSL::Digest::SHA1 generates version 5 UUIDs.
15
18
  # uuid_from_hash always generates the same UUID for a given name and namespace combination.
16
19
  #
17
20
  # See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt
18
- def self.uuid_from_hash(hash_class, uuid_namespace, name)
19
- if hash_class == Digest::MD5
21
+ def self.uuid_from_hash(hash_class, namespace, name)
22
+ if hash_class == Digest::MD5 || hash_class == OpenSSL::Digest::MD5
20
23
  version = 3
21
- elsif hash_class == Digest::SHA1
24
+ elsif hash_class == Digest::SHA1 || hash_class == OpenSSL::Digest::SHA1
22
25
  version = 5
23
26
  else
24
- raise ArgumentError, "Expected Digest::SHA1 or Digest::MD5, got #{hash_class.name}."
27
+ raise ArgumentError, "Expected OpenSSL::Digest::SHA1 or OpenSSL::Digest::MD5, got #{hash_class.name}."
25
28
  end
26
29
 
30
+ uuid_namespace = pack_uuid_namespace(namespace)
31
+
27
32
  hash = hash_class.new
28
33
  hash.update(uuid_namespace)
29
34
  hash.update(name)
@@ -35,19 +40,40 @@ module Digest
35
40
  "%08x-%04x-%04x-%04x-%04x%08x" % ary
36
41
  end
37
42
 
38
- # Convenience method for uuid_from_hash using Digest::MD5.
43
+ # Convenience method for uuid_from_hash using OpenSSL::Digest::MD5.
39
44
  def self.uuid_v3(uuid_namespace, name)
40
- uuid_from_hash(Digest::MD5, uuid_namespace, name)
45
+ uuid_from_hash(OpenSSL::Digest::MD5, uuid_namespace, name)
41
46
  end
42
47
 
43
- # Convenience method for uuid_from_hash using Digest::SHA1.
48
+ # Convenience method for uuid_from_hash using OpenSSL::Digest::SHA1.
44
49
  def self.uuid_v5(uuid_namespace, name)
45
- uuid_from_hash(Digest::SHA1, uuid_namespace, name)
50
+ uuid_from_hash(OpenSSL::Digest::SHA1, uuid_namespace, name)
46
51
  end
47
52
 
48
53
  # Convenience method for SecureRandom.uuid.
49
54
  def self.uuid_v4
50
55
  SecureRandom.uuid
51
56
  end
57
+
58
+ def self.pack_uuid_namespace(namespace)
59
+ if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
60
+ namespace
61
+ elsif use_rfc4122_namespaced_uuids == true
62
+ match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/)
63
+
64
+ raise ArgumentError, "Only UUIDs are valid namespace identifiers" unless match_data.present?
65
+
66
+ 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
+ end
75
+ end
76
+
77
+ private_class_method :pack_uuid_namespace
52
78
  end
53
79
  end
@@ -4,6 +4,10 @@ module Enumerable
4
4
  INDEX_WITH_DEFAULT = Object.new
5
5
  private_constant :INDEX_WITH_DEFAULT
6
6
 
7
+ # Error generated by +sole+ when called on an enumerable that doesn't have
8
+ # exactly one item.
9
+ class SoleItemExpectedError < StandardError; end
10
+
7
11
  # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
8
12
  # when we omit an identity.
9
13
 
@@ -16,6 +20,22 @@ module Enumerable
16
20
 
17
21
  # :startdoc:
18
22
 
23
+ # Calculates the minimum from the extracted elements.
24
+ #
25
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
26
+ # payments.minimum(:price) # => 5
27
+ def minimum(key)
28
+ map(&key).min
29
+ end
30
+
31
+ # Calculates the maximum from the extracted elements.
32
+ #
33
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
34
+ # payments.maximum(:price) # => 15
35
+ def maximum(key)
36
+ map(&key).max
37
+ end
38
+
19
39
  # Calculates a sum from the elements.
20
40
  #
21
41
  # payments.sum { |p| p.price * p.tax_rate }
@@ -27,19 +47,30 @@ module Enumerable
27
47
  #
28
48
  # It can also calculate the sum without the use of a block.
29
49
  #
30
- # [5, 15, 10].sum # => 30
31
- # ['foo', 'bar'].sum # => "foobar"
32
- # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
50
+ # [5, 15, 10].sum # => 30
51
+ # ['foo', 'bar'].sum('') # => "foobar"
52
+ # [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]
33
53
  #
34
54
  # The default sum of an empty list is zero. You can override this default:
35
55
  #
36
- # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
56
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
37
57
  def sum(identity = nil, &block)
38
58
  if identity
39
59
  _original_sum_with_required_identity(identity, &block)
40
60
  elsif block_given?
41
- map(&block).sum(identity)
61
+ map(&block).sum
62
+ # we check `first(1) == []` to check if we have an
63
+ # empty Enumerable; checking `empty?` would return
64
+ # true for `[nil]`, which we want to deprecate to
65
+ # keep consistent with Ruby
66
+ elsif first.is_a?(Numeric) || first(1) == []
67
+ identity ||= 0
68
+ _original_sum_with_required_identity(identity, &block)
42
69
  else
70
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
71
+ Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
72
+ Sum of non-numeric elements requires an initial argument.
73
+ MSG
43
74
  inject(:+) || 0
44
75
  end
45
76
  end
@@ -136,11 +167,7 @@ module Enumerable
136
167
  elements.flatten!(1)
137
168
  reject { |element| elements.include?(element) }
138
169
  end
139
-
140
- # Alias for #excluding.
141
- def without(*elements)
142
- excluding(*elements)
143
- end
170
+ alias :without :excluding
144
171
 
145
172
  # Extract the given key from each element in the enumerable.
146
173
  #
@@ -178,40 +205,66 @@ module Enumerable
178
205
  # Returns a new +Array+ without the blank items.
179
206
  # Uses Object#blank? for determining if an item is blank.
180
207
  #
181
- # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
182
- # # => [1, 2, true]
208
+ # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
209
+ # # => [1, 2, true]
183
210
  #
184
- # Set.new([nil, "", 1, 2])
185
- # # => [2, 1] (or [1, 2])
211
+ # Set.new([nil, "", 1, 2])
212
+ # # => [2, 1] (or [1, 2])
186
213
  #
187
214
  # When called on a +Hash+, returns a new +Hash+ without the blank values.
188
215
  #
189
- # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
190
- # #=> { b: 1, f: true }
216
+ # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
217
+ # # => { b: 1, f: true }
191
218
  def compact_blank
192
219
  reject(&:blank?)
193
220
  end
221
+
222
+ # Returns a new +Array+ where the order has been set to that provided in the +series+, based on the +key+ of the
223
+ # objects in the original enumerable.
224
+ #
225
+ # [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
226
+ # # => [ Person.find(1), Person.find(5), Person.find(3) ]
227
+ #
228
+ # If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
229
+ # If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
230
+ def in_order_of(key, series)
231
+ index_by(&key).values_at(*series).compact
232
+ end
233
+
234
+ # Returns the sole item in the enumerable. If there are no items, or more
235
+ # than one item, raises +Enumerable::SoleItemExpectedError+.
236
+ #
237
+ # ["x"].sole # => "x"
238
+ # Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
239
+ # { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
240
+ def sole
241
+ case count
242
+ when 1 then return first # rubocop:disable Style/RedundantReturn
243
+ when 0 then raise SoleItemExpectedError, "no item found"
244
+ when 2.. then raise SoleItemExpectedError, "multiple items found"
245
+ end
246
+ end
194
247
  end
195
248
 
196
249
  class Hash
197
250
  # Hash#reject has its own definition, so this needs one too.
198
- def compact_blank #:nodoc:
251
+ def compact_blank # :nodoc:
199
252
  reject { |_k, v| v.blank? }
200
253
  end
201
254
 
202
255
  # Removes all blank values from the +Hash+ in place and returns self.
203
256
  # Uses Object#blank? for determining if a value is blank.
204
257
  #
205
- # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
206
- # h.compact_blank!
207
- # # => { b: 1, f: true }
258
+ # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
259
+ # h.compact_blank!
260
+ # # => { b: 1, f: true }
208
261
  def compact_blank!
209
262
  # use delete_if rather than reject! because it always returns self even if nothing changed
210
263
  delete_if { |_k, v| v.blank? }
211
264
  end
212
265
  end
213
266
 
214
- class Range #:nodoc:
267
+ class Range # :nodoc:
215
268
  # Optimize range sum to use arithmetic progression if a block is not given and
216
269
  # we have a range of numeric values.
217
270
  def sum(identity = nil)
@@ -236,8 +289,7 @@ using Module.new {
236
289
  end
237
290
  }
238
291
 
239
- class Array #:nodoc:
240
- # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
292
+ class Array # :nodoc:
241
293
  def sum(init = nil, &block)
242
294
  if init.is_a?(Numeric) || first.is_a?(Numeric)
243
295
  init ||= 0
@@ -250,9 +302,9 @@ class Array #:nodoc:
250
302
  # Removes all blank elements from the +Array+ in place and returns self.
251
303
  # Uses Object#blank? for determining if an item is blank.
252
304
  #
253
- # a = [1, "", nil, 2, " ", [], {}, false, true]
254
- # a.compact_blank!
255
- # # => [1, 2, true]
305
+ # a = [1, "", nil, 2, " ", [], {}, false, true]
306
+ # a.compact_blank!
307
+ # # => [1, 2, true]
256
308
  def compact_blank!
257
309
  # use delete_if rather than reject! because it always returns self even if nothing changed
258
310
  delete_if(&:blank?)
@@ -53,7 +53,7 @@ class File
53
53
  end
54
54
 
55
55
  # Private utility method.
56
- def self.probe_stat_in(dir) #:nodoc:
56
+ def self.probe_stat_in(dir) # :nodoc:
57
57
  basename = [
58
58
  ".permissions_check",
59
59
  Thread.current.object_id,
@@ -64,6 +64,8 @@ class File
64
64
  file_name = join(dir, basename)
65
65
  FileUtils.touch(file_name)
66
66
  stat(file_name)
67
+ rescue Errno::ENOENT
68
+ file_name = nil
67
69
  ensure
68
70
  FileUtils.rm_f(file_name) if file_name
69
71
  end
@@ -116,7 +116,7 @@ class Hash
116
116
  def _deep_transform_keys_in_object(object, &block)
117
117
  case object
118
118
  when Hash
119
- object.each_with_object({}) do |(key, value), result|
119
+ object.each_with_object(self.class.new) do |(key, value), result|
120
120
  result[yield(key)] = _deep_transform_keys_in_object(value, &block)
121
121
  end
122
122
  when Array