activesupport 6.0.4.4 → 7.0.4.1

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

Potentially problematic release.


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

Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +257 -532
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +2 -2
  6. data/lib/active_support/backtrace_cleaner.rb +5 -5
  7. data/lib/active_support/benchmarkable.rb +3 -3
  8. data/lib/active_support/cache/file_store.rb +16 -10
  9. data/lib/active_support/cache/mem_cache_store.rb +163 -42
  10. data/lib/active_support/cache/memory_store.rb +57 -29
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +79 -98
  13. data/lib/active_support/cache/strategy/local_cache.rb +49 -57
  14. data/lib/active_support/cache.rb +378 -179
  15. data/lib/active_support/callbacks.rb +230 -122
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +49 -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 +9 -6
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +1 -5
  23. data/lib/active_support/core_ext/array/conversions.rb +13 -12
  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/inquiry.rb +2 -2
  27. data/lib/active_support/core_ext/array.rb +1 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  30. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  31. data/lib/active_support/core_ext/class/subclasses.rb +9 -22
  32. data/lib/active_support/core_ext/date/blank.rb +1 -1
  33. data/lib/active_support/core_ext/date/calculations.rb +9 -9
  34. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  35. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  36. data/lib/active_support/core_ext/date.rb +1 -0
  37. data/lib/active_support/core_ext/date_and_time/calculations.rb +17 -4
  38. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  39. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  41. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  42. data/lib/active_support/core_ext/date_time.rb +1 -0
  43. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  44. data/lib/active_support/core_ext/enumerable.rb +164 -23
  45. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  46. data/lib/active_support/core_ext/hash/conversions.rb +2 -3
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  49. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  50. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  51. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  52. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  53. data/lib/active_support/core_ext/load_error.rb +1 -1
  54. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  57. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  58. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  59. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  60. data/lib/active_support/core_ext/name_error.rb +23 -2
  61. data/lib/active_support/core_ext/numeric/conversions.rb +80 -73
  62. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  63. data/lib/active_support/core_ext/numeric.rb +1 -0
  64. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  65. data/lib/active_support/core_ext/object/blank.rb +2 -2
  66. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  68. data/lib/active_support/core_ext/object/json.rb +42 -26
  69. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  70. data/lib/active_support/core_ext/object/try.rb +20 -20
  71. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  72. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  73. data/lib/active_support/core_ext/pathname.rb +3 -0
  74. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  75. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  76. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  77. data/lib/active_support/core_ext/range/each.rb +1 -1
  78. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  79. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  80. data/lib/active_support/core_ext/range.rb +1 -1
  81. data/lib/active_support/core_ext/regexp.rb +8 -1
  82. data/lib/active_support/core_ext/securerandom.rb +1 -1
  83. data/lib/active_support/core_ext/string/access.rb +5 -24
  84. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  85. data/lib/active_support/core_ext/string/filters.rb +1 -1
  86. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  87. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  88. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  89. data/lib/active_support/core_ext/string/output_safety.rb +92 -41
  90. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  92. data/lib/active_support/core_ext/symbol.rb +3 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +25 -7
  94. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  95. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  96. data/lib/active_support/core_ext/time/zones.rb +7 -22
  97. data/lib/active_support/core_ext/time.rb +1 -0
  98. data/lib/active_support/core_ext/uri.rb +3 -23
  99. data/lib/active_support/core_ext.rb +2 -1
  100. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  101. data/lib/active_support/current_attributes.rb +39 -16
  102. data/lib/active_support/dependencies/interlock.rb +10 -18
  103. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  104. data/lib/active_support/dependencies.rb +58 -769
  105. data/lib/active_support/deprecation/behaviors.rb +23 -7
  106. data/lib/active_support/deprecation/disallowed.rb +56 -0
  107. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  108. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
  110. data/lib/active_support/deprecation/reporting.rb +50 -7
  111. data/lib/active_support/deprecation.rb +7 -2
  112. data/lib/active_support/descendants_tracker.rb +174 -64
  113. data/lib/active_support/digest.rb +5 -3
  114. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  115. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  116. data/lib/active_support/duration.rb +134 -55
  117. data/lib/active_support/encrypted_configuration.rb +13 -2
  118. data/lib/active_support/encrypted_file.rb +32 -3
  119. data/lib/active_support/environment_inquirer.rb +20 -0
  120. data/lib/active_support/error_reporter.rb +117 -0
  121. data/lib/active_support/evented_file_update_checker.rb +72 -138
  122. data/lib/active_support/execution_context/test_helper.rb +13 -0
  123. data/lib/active_support/execution_context.rb +53 -0
  124. data/lib/active_support/execution_wrapper.rb +43 -21
  125. data/lib/active_support/executor/test_helper.rb +7 -0
  126. data/lib/active_support/fork_tracker.rb +71 -0
  127. data/lib/active_support/gem_version.rb +3 -3
  128. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  129. data/lib/active_support/html_safe_translation.rb +43 -0
  130. data/lib/active_support/i18n.rb +1 -0
  131. data/lib/active_support/i18n_railtie.rb +14 -19
  132. data/lib/active_support/inflector/inflections.rb +24 -9
  133. data/lib/active_support/inflector/methods.rb +29 -49
  134. data/lib/active_support/inflector/transliterate.rb +5 -5
  135. data/lib/active_support/isolated_execution_state.rb +72 -0
  136. data/lib/active_support/json/decoding.rb +4 -4
  137. data/lib/active_support/json/encoding.rb +8 -4
  138. data/lib/active_support/key_generator.rb +23 -6
  139. data/lib/active_support/lazy_load_hooks.rb +28 -4
  140. data/lib/active_support/locale/en.yml +8 -4
  141. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  142. data/lib/active_support/log_subscriber.rb +23 -5
  143. data/lib/active_support/logger.rb +1 -1
  144. data/lib/active_support/logger_silence.rb +2 -26
  145. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  146. data/lib/active_support/message_encryptor.rb +16 -13
  147. data/lib/active_support/message_verifier.rb +50 -18
  148. data/lib/active_support/messages/metadata.rb +2 -2
  149. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  150. data/lib/active_support/messages/rotator.rb +6 -5
  151. data/lib/active_support/multibyte/chars.rb +13 -52
  152. data/lib/active_support/multibyte/unicode.rb +1 -87
  153. data/lib/active_support/multibyte.rb +1 -1
  154. data/lib/active_support/notifications/fanout.rb +110 -69
  155. data/lib/active_support/notifications/instrumenter.rb +37 -29
  156. data/lib/active_support/notifications.rb +55 -28
  157. data/lib/active_support/number_helper/number_converter.rb +2 -4
  158. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  159. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  160. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  161. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  162. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  163. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  164. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  165. data/lib/active_support/number_helper.rb +29 -16
  166. data/lib/active_support/option_merger.rb +11 -18
  167. data/lib/active_support/ordered_hash.rb +1 -1
  168. data/lib/active_support/ordered_options.rb +9 -3
  169. data/lib/active_support/parameter_filter.rb +21 -11
  170. data/lib/active_support/per_thread_registry.rb +6 -1
  171. data/lib/active_support/rails.rb +1 -4
  172. data/lib/active_support/railtie.rb +77 -5
  173. data/lib/active_support/reloader.rb +1 -1
  174. data/lib/active_support/rescuable.rb +16 -16
  175. data/lib/active_support/ruby_features.rb +7 -0
  176. data/lib/active_support/secure_compare_rotator.rb +51 -0
  177. data/lib/active_support/security_utils.rb +19 -12
  178. data/lib/active_support/string_inquirer.rb +2 -2
  179. data/lib/active_support/subscriber.rb +19 -25
  180. data/lib/active_support/tagged_logging.rb +31 -6
  181. data/lib/active_support/test_case.rb +13 -21
  182. data/lib/active_support/testing/assertions.rb +50 -13
  183. data/lib/active_support/testing/deprecation.rb +52 -1
  184. data/lib/active_support/testing/isolation.rb +2 -2
  185. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  186. data/lib/active_support/testing/parallelization/server.rb +82 -0
  187. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  188. data/lib/active_support/testing/parallelization.rb +16 -95
  189. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  190. data/lib/active_support/testing/stream.rb +3 -5
  191. data/lib/active_support/testing/tagged_logging.rb +1 -1
  192. data/lib/active_support/testing/time_helpers.rb +53 -5
  193. data/lib/active_support/time_with_zone.rb +126 -62
  194. data/lib/active_support/values/time_zone.rb +54 -23
  195. data/lib/active_support/version.rb +1 -1
  196. data/lib/active_support/xml_mini/jdom.rb +1 -1
  197. data/lib/active_support/xml_mini/libxml.rb +5 -5
  198. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  199. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  200. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  201. data/lib/active_support/xml_mini/rexml.rb +9 -2
  202. data/lib/active_support/xml_mini.rb +5 -4
  203. data/lib/active_support.rb +29 -1
  204. metadata +46 -45
  205. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  206. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  207. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  208. data/lib/active_support/core_ext/marshal.rb +0 -24
  209. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  210. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  211. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  212. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -10,6 +10,7 @@ class Date
10
10
  short: "%d %b",
11
11
  long: "%B %d, %Y",
12
12
  db: "%Y-%m-%d",
13
+ inspect: "%Y-%m-%d",
13
14
  number: "%Y%m%d",
14
15
  long_ordinal: lambda { |date|
15
16
  day_format = ActiveSupport::Inflector.ordinalize(date.day)
@@ -21,21 +22,21 @@ class Date
21
22
 
22
23
  # Convert to a formatted string. See DATE_FORMATS for predefined formats.
23
24
  #
24
- # This method is aliased to <tt>to_s</tt>.
25
+ # This method is aliased to <tt>to_formatted_s</tt>.
25
26
  #
26
27
  # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
27
28
  #
29
+ # date.to_fs(:db) # => "2007-11-10"
28
30
  # date.to_formatted_s(:db) # => "2007-11-10"
29
- # date.to_s(:db) # => "2007-11-10"
30
31
  #
31
- # date.to_formatted_s(:short) # => "10 Nov"
32
- # date.to_formatted_s(:number) # => "20071110"
33
- # date.to_formatted_s(:long) # => "November 10, 2007"
34
- # date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
35
- # date.to_formatted_s(:rfc822) # => "10 Nov 2007"
36
- # 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"
37
38
  #
38
- # == Adding your own date formats to to_formatted_s
39
+ # == Adding your own date formats to to_fs
39
40
  # You can add your own formats to the Date::DATE_FORMATS hash.
40
41
  # Use the format name as the hash key and either a strftime string
41
42
  # or Proc instance that takes a date argument as the value.
@@ -43,7 +44,7 @@ class Date
43
44
  # # config/initializers/date_formats.rb
44
45
  # Date::DATE_FORMATS[:month_and_year] = '%B %Y'
45
46
  # Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
46
- def to_formatted_s(format = :default)
47
+ def to_fs(format = :default)
47
48
  if formatter = DATE_FORMATS[format]
48
49
  if formatter.respond_to?(:call)
49
50
  formatter.call(self).to_s
@@ -54,8 +55,8 @@ class Date
54
55
  to_default_s
55
56
  end
56
57
  end
58
+ alias_method :to_formatted_s, :to_fs
57
59
  alias_method :to_default_s, :to_s
58
- alias_method :to_s, :to_formatted_s
59
60
 
60
61
  # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
61
62
  def readable_inspect
@@ -67,7 +68,7 @@ class Date
67
68
  silence_redefinition_of_method :to_time
68
69
 
69
70
  # Converts a Date instance to a Time, where the time is set to the beginning of the day.
70
- # The timezone can be either :local or :utc (default :local).
71
+ # The timezone can be either +:local+ or +:utc+ (default +:local+).
71
72
  #
72
73
  # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
73
74
  #
@@ -76,11 +77,11 @@ class Date
76
77
  #
77
78
  # date.to_time(:utc) # => 2007-11-10 00:00:00 UTC
78
79
  #
79
- # NOTE: The :local timezone is Ruby's *process* timezone, i.e. ENV['TZ'].
80
- # If the *application's* timezone is needed, then use +in_time_zone+ instead.
80
+ # NOTE: The +:local+ timezone is Ruby's *process* timezone, i.e. <tt>ENV['TZ']</tt>.
81
+ # If the <b>application's</b> timezone is needed, then use +in_time_zone+ instead.
81
82
  def to_time(form = :local)
82
83
  raise ArgumentError, "Expected :local or :utc, got #{form.inspect}." unless [:local, :utc].include?(form)
83
- ::Time.send(form, year, month, day)
84
+ ::Time.public_send(form, year, month, day)
84
85
  end
85
86
 
86
87
  silence_redefinition_of_method :xmlschema
@@ -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"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/object/try"
4
+ require "active_support/core_ext/date_time/conversions"
4
5
 
5
6
  module DateAndTime
6
7
  module Calculations
@@ -30,6 +31,18 @@ module DateAndTime
30
31
  to_date == ::Date.current
31
32
  end
32
33
 
34
+ # Returns true if the date/time is tomorrow.
35
+ def tomorrow?
36
+ to_date == ::Date.current.tomorrow
37
+ end
38
+ alias :next_day? :tomorrow?
39
+
40
+ # Returns true if the date/time is yesterday.
41
+ def yesterday?
42
+ to_date == ::Date.current.yesterday
43
+ end
44
+ alias :prev_day? :yesterday?
45
+
33
46
  # Returns true if the date/time is in the past.
34
47
  def past?
35
48
  self < self.class.current
@@ -188,7 +201,7 @@ module DateAndTime
188
201
  end
189
202
  end
190
203
 
191
- # Short-hand for months_since(3)
204
+ # Short-hand for <tt>months_since(3)</tt>.
192
205
  def next_quarter
193
206
  months_since(3)
194
207
  end
@@ -213,18 +226,18 @@ module DateAndTime
213
226
  end
214
227
  alias_method :last_weekday, :prev_weekday
215
228
 
216
- # Short-hand for months_ago(1).
229
+ # Short-hand for <tt>months_ago(1)</tt>.
217
230
  def last_month
218
231
  months_ago(1)
219
232
  end
220
233
 
221
- # Short-hand for months_ago(3).
234
+ # Short-hand for <tt>months_ago(3)</tt>.
222
235
  def prev_quarter
223
236
  months_ago(3)
224
237
  end
225
238
  alias_method :last_quarter, :prev_quarter
226
239
 
227
- # Short-hand for years_ago(1).
240
+ # Short-hand for <tt>years_ago(1)</tt>.
228
241
  def last_year
229
242
  years_ago(1)
230
243
  end
@@ -12,5 +12,20 @@ module DateAndTime
12
12
  # this behavior, but new apps will have an initializer that sets
13
13
  # this to true, because the new behavior is preferred.
14
14
  mattr_accessor :preserve_timezone, instance_writer: false, default: false
15
+
16
+ # Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
17
+ #
18
+ # When +true+, it returns local times with a UTC offset, with +false+ local
19
+ # times are returned as UTC.
20
+ #
21
+ # # Given this zone:
22
+ # zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
23
+ #
24
+ # # With `utc_to_local_returns_utc_offset_times = false`, local time is converted to UTC:
25
+ # zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 UTC
26
+ #
27
+ # # With `utc_to_local_returns_utc_offset_times = true`, local time is returned with UTC offset:
28
+ # zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 -0500
29
+ mattr_accessor :utc_to_local_returns_utc_offset_times, instance_writer: false, default: false
15
30
  end
16
31
  end
@@ -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
@@ -1,8 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module ActiveSupport
4
+ module EnumerableCoreExt # :nodoc:
5
+ module Constants
6
+ private
7
+ def const_missing(name)
8
+ if name == :SoleItemExpectedError
9
+ ::ActiveSupport::EnumerableCoreExt::SoleItemExpectedError
10
+ else
11
+ super
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+
3
18
  module Enumerable
4
- INDEX_WITH_DEFAULT = Object.new
5
- private_constant :INDEX_WITH_DEFAULT
19
+ # Error generated by +sole+ when called on an enumerable that doesn't have
20
+ # exactly one item.
21
+ class SoleItemExpectedError < StandardError; end
22
+
23
+ # HACK: For performance reasons, Enumerable shouldn't have any constants of its own.
24
+ # So we move SoleItemExpectedError into ActiveSupport::EnumerableCoreExt.
25
+ ActiveSupport::EnumerableCoreExt::SoleItemExpectedError = remove_const(:SoleItemExpectedError)
26
+ singleton_class.prepend(ActiveSupport::EnumerableCoreExt::Constants)
6
27
 
7
28
  # Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
8
29
  # when we omit an identity.
@@ -16,6 +37,22 @@ module Enumerable
16
37
 
17
38
  # :startdoc:
18
39
 
40
+ # Calculates the minimum from the extracted elements.
41
+ #
42
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
43
+ # payments.minimum(:price) # => 5
44
+ def minimum(key)
45
+ map(&key).min
46
+ end
47
+
48
+ # Calculates the maximum from the extracted elements.
49
+ #
50
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
51
+ # payments.maximum(:price) # => 15
52
+ def maximum(key)
53
+ map(&key).max
54
+ end
55
+
19
56
  # Calculates a sum from the elements.
20
57
  #
21
58
  # payments.sum { |p| p.price * p.tax_rate }
@@ -27,24 +64,36 @@ module Enumerable
27
64
  #
28
65
  # It can also calculate the sum without the use of a block.
29
66
  #
30
- # [5, 15, 10].sum # => 30
31
- # ['foo', 'bar'].sum # => "foobar"
32
- # [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
67
+ # [5, 15, 10].sum # => 30
68
+ # ['foo', 'bar'].sum('') # => "foobar"
69
+ # [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]
33
70
  #
34
71
  # The default sum of an empty list is zero. You can override this default:
35
72
  #
36
- # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
73
+ # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
37
74
  def sum(identity = nil, &block)
38
75
  if identity
39
76
  _original_sum_with_required_identity(identity, &block)
40
77
  elsif block_given?
41
- map(&block).sum(identity)
78
+ map(&block).sum
79
+ # we check `first(1) == []` to check if we have an
80
+ # empty Enumerable; checking `empty?` would return
81
+ # true for `[nil]`, which we want to deprecate to
82
+ # keep consistent with Ruby
83
+ elsif first.is_a?(Numeric) || first(1) == []
84
+ identity ||= 0
85
+ _original_sum_with_required_identity(identity, &block)
42
86
  else
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
43
91
  inject(:+) || 0
44
92
  end
45
93
  end
46
94
 
47
- # Convert an enumerable to a hash keying it by the block return value.
95
+ # Convert an enumerable to a hash, using the block result as the key and the
96
+ # element as the value.
48
97
  #
49
98
  # people.index_by(&:login)
50
99
  # # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
@@ -61,23 +110,30 @@ module Enumerable
61
110
  end
62
111
  end
63
112
 
64
- # Convert an enumerable to a hash keying it with the enumerable items and with the values returned in the block.
113
+ # Convert an enumerable to a hash, using the element as the key and the block
114
+ # result as the value.
65
115
  #
66
116
  # post = Post.new(title: "hey there", body: "what's up?")
67
117
  #
68
118
  # %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
69
119
  # # => { title: "hey there", body: "what's up?" }
70
- def index_with(default = INDEX_WITH_DEFAULT)
120
+ #
121
+ # If an argument is passed instead of a block, it will be used as the value
122
+ # for all elements:
123
+ #
124
+ # %i( created_at updated_at ).index_with(Time.now)
125
+ # # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
126
+ def index_with(default = (no_default = true))
71
127
  if block_given?
72
128
  result = {}
73
129
  each { |elem| result[elem] = yield(elem) }
74
130
  result
75
- elsif default != INDEX_WITH_DEFAULT
131
+ elsif no_default
132
+ to_enum(:index_with) { size if respond_to?(:size) }
133
+ else
76
134
  result = {}
77
135
  each { |elem| result[elem] = default }
78
136
  result
79
- else
80
- to_enum(:index_with) { size if respond_to?(:size) }
81
137
  end
82
138
  end
83
139
 
@@ -128,13 +184,9 @@ module Enumerable
128
184
  elements.flatten!(1)
129
185
  reject { |element| elements.include?(element) }
130
186
  end
187
+ alias :without :excluding
131
188
 
132
- # Alias for #excluding.
133
- def without(*elements)
134
- excluding(*elements)
135
- end
136
-
137
- # Convert an enumerable to an array based on the given key.
189
+ # Extract the given key from each element in the enumerable.
138
190
  #
139
191
  # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
140
192
  # # => ["David", "Rafael", "Aaron"]
@@ -145,12 +197,91 @@ module Enumerable
145
197
  if keys.many?
146
198
  map { |element| keys.map { |key| element[key] } }
147
199
  else
148
- map { |element| element[keys.first] }
200
+ key = keys.first
201
+ map { |element| element[key] }
149
202
  end
150
203
  end
204
+
205
+ # Extract the given key from the first element in the enumerable.
206
+ #
207
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
208
+ # # => "David"
209
+ #
210
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
211
+ # # => [1, "David"]
212
+ def pick(*keys)
213
+ return if none?
214
+
215
+ if keys.many?
216
+ keys.map { |key| first[key] }
217
+ else
218
+ first[keys.first]
219
+ end
220
+ end
221
+
222
+ # Returns a new +Array+ without the blank items.
223
+ # Uses Object#blank? for determining if an item is blank.
224
+ #
225
+ # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
226
+ # # => [1, 2, true]
227
+ #
228
+ # Set.new([nil, "", 1, 2])
229
+ # # => [2, 1] (or [1, 2])
230
+ #
231
+ # When called on a +Hash+, returns a new +Hash+ without the blank values.
232
+ #
233
+ # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
234
+ # # => { b: 1, f: true }
235
+ def compact_blank
236
+ reject(&:blank?)
237
+ end
238
+
239
+ # Returns a new +Array+ where the order has been set to that provided in the +series+, based on the +key+ of the
240
+ # objects in the original enumerable.
241
+ #
242
+ # [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
243
+ # # => [ Person.find(1), Person.find(5), Person.find(3) ]
244
+ #
245
+ # If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
246
+ # If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
247
+ def in_order_of(key, series)
248
+ index_by(&key).values_at(*series).compact
249
+ end
250
+
251
+ # Returns the sole item in the enumerable. If there are no items, or more
252
+ # than one item, raises +Enumerable::SoleItemExpectedError+.
253
+ #
254
+ # ["x"].sole # => "x"
255
+ # Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
256
+ # { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
257
+ def sole
258
+ case count
259
+ when 1 then return first # rubocop:disable Style/RedundantReturn
260
+ when 0 then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "no item found"
261
+ when 2.. then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "multiple items found"
262
+ end
263
+ end
264
+ end
265
+
266
+ class Hash
267
+ # Hash#reject has its own definition, so this needs one too.
268
+ def compact_blank # :nodoc:
269
+ reject { |_k, v| v.blank? }
270
+ end
271
+
272
+ # Removes all blank values from the +Hash+ in place and returns self.
273
+ # Uses Object#blank? for determining if a value is blank.
274
+ #
275
+ # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
276
+ # h.compact_blank!
277
+ # # => { b: 1, f: true }
278
+ def compact_blank!
279
+ # use delete_if rather than reject! because it always returns self even if nothing changed
280
+ delete_if { |_k, v| v.blank? }
281
+ end
151
282
  end
152
283
 
153
- class Range #:nodoc:
284
+ class Range # :nodoc:
154
285
  # Optimize range sum to use arithmetic progression if a block is not given and
155
286
  # we have a range of numeric values.
156
287
  def sum(identity = nil)
@@ -175,8 +306,7 @@ using Module.new {
175
306
  end
176
307
  }
177
308
 
178
- class Array #:nodoc:
179
- # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
309
+ class Array # :nodoc:
180
310
  def sum(init = nil, &block)
181
311
  if init.is_a?(Numeric) || first.is_a?(Numeric)
182
312
  init ||= 0
@@ -185,4 +315,15 @@ class Array #:nodoc:
185
315
  super
186
316
  end
187
317
  end
318
+
319
+ # Removes all blank elements from the +Array+ in place and returns self.
320
+ # Uses Object#blank? for determining if an item is blank.
321
+ #
322
+ # a = [1, "", nil, 2, " ", [], {}, false, true]
323
+ # a.compact_blank!
324
+ # # => [1, 2, true]
325
+ def compact_blank!
326
+ # use delete_if rather than reject! because it always returns self even if nothing changed
327
+ delete_if(&:blank?)
328
+ end
188
329
  end
@@ -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