activesupport 6.0.3.7 → 7.0.0

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 (204) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +220 -533
  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 +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +3 -3
  8. data/lib/active_support/benchmarkable.rb +3 -3
  9. data/lib/active_support/cache/file_store.rb +18 -11
  10. data/lib/active_support/cache/mem_cache_store.rb +143 -37
  11. data/lib/active_support/cache/memory_store.rb +56 -28
  12. data/lib/active_support/cache/null_store.rb +10 -2
  13. data/lib/active_support/cache/redis_cache_store.rb +63 -88
  14. data/lib/active_support/cache/strategy/local_cache.rb +46 -57
  15. data/lib/active_support/cache.rb +273 -82
  16. data/lib/active_support/callbacks.rb +226 -118
  17. data/lib/active_support/code_generator.rb +65 -0
  18. data/lib/active_support/concern.rb +49 -5
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  20. data/lib/active_support/concurrency/share_lock.rb +2 -2
  21. data/lib/active_support/configurable.rb +9 -6
  22. data/lib/active_support/configuration_file.rb +51 -0
  23. data/lib/active_support/core_ext/array/access.rb +1 -5
  24. data/lib/active_support/core_ext/array/conversions.rb +9 -7
  25. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  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 +21 -40
  32. data/lib/active_support/core_ext/date/blank.rb +1 -1
  33. data/lib/active_support/core_ext/date/calculations.rb +4 -4
  34. data/lib/active_support/core_ext/date/conversions.rb +5 -4
  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 +13 -0
  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 +5 -5
  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 +139 -15
  45. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  46. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  48. data/lib/active_support/core_ext/hash/except.rb +1 -1
  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/load_error.rb +1 -1
  53. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  54. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  55. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  56. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  57. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  58. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  59. data/lib/active_support/core_ext/name_error.rb +23 -2
  60. data/lib/active_support/core_ext/numeric/conversions.rb +79 -72
  61. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  62. data/lib/active_support/core_ext/numeric.rb +1 -0
  63. data/lib/active_support/core_ext/object/blank.rb +2 -2
  64. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  65. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  66. data/lib/active_support/core_ext/object/json.rb +42 -26
  67. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  68. data/lib/active_support/core_ext/object/try.rb +20 -20
  69. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  70. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  71. data/lib/active_support/core_ext/pathname.rb +3 -0
  72. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  73. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  74. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  75. data/lib/active_support/core_ext/range/each.rb +1 -1
  76. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  77. data/lib/active_support/core_ext/range.rb +1 -1
  78. data/lib/active_support/core_ext/regexp.rb +8 -1
  79. data/lib/active_support/core_ext/string/access.rb +5 -24
  80. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  81. data/lib/active_support/core_ext/string/filters.rb +1 -1
  82. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  83. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  84. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  85. data/lib/active_support/core_ext/string/output_safety.rb +69 -45
  86. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  87. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  88. data/lib/active_support/core_ext/symbol.rb +3 -0
  89. data/lib/active_support/core_ext/time/calculations.rb +26 -6
  90. data/lib/active_support/core_ext/time/conversions.rb +6 -3
  91. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  92. data/lib/active_support/core_ext/time/zones.rb +4 -19
  93. data/lib/active_support/core_ext/time.rb +1 -0
  94. data/lib/active_support/core_ext/uri.rb +3 -23
  95. data/lib/active_support/core_ext.rb +2 -1
  96. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  97. data/lib/active_support/current_attributes.rb +39 -16
  98. data/lib/active_support/dependencies/interlock.rb +10 -18
  99. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  100. data/lib/active_support/dependencies.rb +58 -764
  101. data/lib/active_support/deprecation/behaviors.rb +19 -3
  102. data/lib/active_support/deprecation/disallowed.rb +56 -0
  103. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  104. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  105. data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
  106. data/lib/active_support/deprecation/reporting.rb +50 -7
  107. data/lib/active_support/deprecation.rb +6 -1
  108. data/lib/active_support/descendants_tracker.rb +177 -64
  109. data/lib/active_support/digest.rb +5 -3
  110. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  111. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  112. data/lib/active_support/duration.rb +134 -55
  113. data/lib/active_support/encrypted_configuration.rb +11 -1
  114. data/lib/active_support/encrypted_file.rb +20 -3
  115. data/lib/active_support/environment_inquirer.rb +20 -0
  116. data/lib/active_support/error_reporter.rb +117 -0
  117. data/lib/active_support/evented_file_update_checker.rb +70 -134
  118. data/lib/active_support/execution_context/test_helper.rb +13 -0
  119. data/lib/active_support/execution_context.rb +53 -0
  120. data/lib/active_support/execution_wrapper.rb +30 -4
  121. data/lib/active_support/executor/test_helper.rb +7 -0
  122. data/lib/active_support/fork_tracker.rb +71 -0
  123. data/lib/active_support/gem_version.rb +3 -3
  124. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  125. data/lib/active_support/html_safe_translation.rb +43 -0
  126. data/lib/active_support/i18n.rb +1 -0
  127. data/lib/active_support/i18n_railtie.rb +14 -19
  128. data/lib/active_support/inflector/inflections.rb +24 -9
  129. data/lib/active_support/inflector/methods.rb +29 -49
  130. data/lib/active_support/inflector/transliterate.rb +4 -4
  131. data/lib/active_support/isolated_execution_state.rb +56 -0
  132. data/lib/active_support/json/decoding.rb +4 -4
  133. data/lib/active_support/json/encoding.rb +8 -4
  134. data/lib/active_support/key_generator.rb +19 -2
  135. data/lib/active_support/locale/en.yml +8 -4
  136. data/lib/active_support/log_subscriber.rb +21 -3
  137. data/lib/active_support/logger.rb +1 -1
  138. data/lib/active_support/logger_silence.rb +2 -26
  139. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  140. data/lib/active_support/message_encryptor.rb +12 -10
  141. data/lib/active_support/message_verifier.rb +50 -18
  142. data/lib/active_support/messages/metadata.rb +11 -3
  143. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  144. data/lib/active_support/messages/rotator.rb +6 -5
  145. data/lib/active_support/multibyte/chars.rb +13 -52
  146. data/lib/active_support/multibyte/unicode.rb +1 -87
  147. data/lib/active_support/multibyte.rb +1 -1
  148. data/lib/active_support/notifications/fanout.rb +110 -69
  149. data/lib/active_support/notifications/instrumenter.rb +37 -29
  150. data/lib/active_support/notifications.rb +47 -26
  151. data/lib/active_support/number_helper/number_converter.rb +2 -4
  152. data/lib/active_support/number_helper/number_to_currency_converter.rb +10 -9
  153. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  154. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  155. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  156. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  157. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  158. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  159. data/lib/active_support/number_helper.rb +29 -16
  160. data/lib/active_support/option_merger.rb +9 -16
  161. data/lib/active_support/ordered_hash.rb +1 -1
  162. data/lib/active_support/ordered_options.rb +8 -2
  163. data/lib/active_support/parameter_filter.rb +21 -11
  164. data/lib/active_support/per_thread_registry.rb +6 -1
  165. data/lib/active_support/rails.rb +1 -4
  166. data/lib/active_support/railtie.rb +77 -5
  167. data/lib/active_support/rescuable.rb +6 -6
  168. data/lib/active_support/ruby_features.rb +7 -0
  169. data/lib/active_support/secure_compare_rotator.rb +51 -0
  170. data/lib/active_support/security_utils.rb +19 -12
  171. data/lib/active_support/string_inquirer.rb +2 -2
  172. data/lib/active_support/subscriber.rb +19 -25
  173. data/lib/active_support/tagged_logging.rb +31 -6
  174. data/lib/active_support/test_case.rb +9 -21
  175. data/lib/active_support/testing/assertions.rb +49 -12
  176. data/lib/active_support/testing/deprecation.rb +52 -1
  177. data/lib/active_support/testing/isolation.rb +2 -2
  178. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  179. data/lib/active_support/testing/parallelization/server.rb +82 -0
  180. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  181. data/lib/active_support/testing/parallelization.rb +16 -95
  182. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  183. data/lib/active_support/testing/stream.rb +3 -5
  184. data/lib/active_support/testing/tagged_logging.rb +1 -1
  185. data/lib/active_support/testing/time_helpers.rb +53 -5
  186. data/lib/active_support/time_with_zone.rb +120 -55
  187. data/lib/active_support/values/time_zone.rb +49 -18
  188. data/lib/active_support/xml_mini/jdom.rb +1 -1
  189. data/lib/active_support/xml_mini/libxml.rb +5 -5
  190. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  191. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  192. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  193. data/lib/active_support/xml_mini/rexml.rb +9 -2
  194. data/lib/active_support/xml_mini.rb +5 -4
  195. data/lib/active_support.rb +29 -1
  196. metadata +46 -45
  197. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  198. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  199. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  200. data/lib/active_support/core_ext/marshal.rb +0 -24
  201. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  202. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  203. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  204. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -10,56 +10,36 @@ module ActiveSupport
10
10
  end
11
11
 
12
12
  def round(number)
13
+ precision = absolute_precision(number)
13
14
  return number unless precision
14
- number = convert_to_decimal(number)
15
- if significant && precision > 0
16
- round_significant(number)
17
- else
18
- round_without_significant(number)
19
- end
15
+
16
+ rounded_number = convert_to_decimal(number).round(precision, options.fetch(:round_mode, :default).to_sym)
17
+ rounded_number.zero? ? rounded_number.abs : rounded_number # prevent showing negative zeros
20
18
  end
21
19
 
22
20
  def digit_count(number)
23
21
  return 1 if number.zero?
24
- (Math.log10(absolute_number(number)) + 1).floor
22
+ (Math.log10(number.abs) + 1).floor
25
23
  end
26
24
 
27
25
  private
28
- def round_without_significant(number)
29
- number = number.round(precision)
30
- number = number.to_i if precision == 0 && number.finite?
31
- number = number.abs if number.zero? # prevent showing negative zeros
32
- number
33
- end
34
-
35
- def round_significant(number)
36
- return 0 if number.zero?
37
- digits = digit_count(number)
38
- multiplier = 10**(digits - precision)
39
- (number / BigDecimal(multiplier.to_f.to_s)).round * multiplier
40
- end
41
-
42
26
  def convert_to_decimal(number)
43
27
  case number
44
28
  when Float, String
45
29
  BigDecimal(number.to_s)
46
30
  when Rational
47
- BigDecimal(number, digit_count(number.to_i) + precision)
31
+ BigDecimal(number, digit_count(number.to_i) + options[:precision])
48
32
  else
49
33
  number.to_d
50
34
  end
51
35
  end
52
36
 
53
- def precision
54
- options[:precision]
55
- end
56
-
57
- def significant
58
- options[:significant]
59
- end
60
-
61
- def absolute_number(number)
62
- number.respond_to?(:abs) ? number.abs : number.to_d.abs
37
+ def absolute_precision(number)
38
+ if options[:significant] && options[:precision] > 0
39
+ options[:precision] - digit_count(convert_to_decimal(number))
40
+ else
41
+ options[:precision]
42
+ end
63
43
  end
64
44
  end
65
45
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/dependencies/autoload"
4
-
5
3
  module ActiveSupport
6
4
  module NumberHelper
7
5
  extend ActiveSupport::Autoload
@@ -73,6 +71,8 @@ module ActiveSupport
73
71
  # (defaults to current locale).
74
72
  # * <tt>:precision</tt> - Sets the level of precision (defaults
75
73
  # to 2).
74
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
75
+ # (defaults to :default. See BigDecimal::mode)
76
76
  # * <tt>:unit</tt> - Sets the denomination of the currency
77
77
  # (defaults to "$").
78
78
  # * <tt>:separator</tt> - Sets the separator between the units
@@ -99,8 +99,6 @@ module ActiveSupport
99
99
  # number_to_currency(1234567890.506, locale: :fr) # => "1 234 567 890,51 €"
100
100
  # number_to_currency('123a456') # => "$123a456"
101
101
  #
102
- # number_to_currency("123a456", raise: true) # => InvalidNumberError
103
- #
104
102
  # number_to_currency(-0.456789, precision: 0)
105
103
  # # => "$0"
106
104
  # number_to_currency(-1234567890.50, negative_format: '(%u%n)')
@@ -111,6 +109,8 @@ module ActiveSupport
111
109
  # # => "1234567890,50 &pound;"
112
110
  # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
113
111
  # # => "$1,234,567,890.5"
112
+ # number_to_currency(1234567890.50, precision: 0, round_mode: :up)
113
+ # # => "$1,234,567,891"
114
114
  def number_to_currency(number, options = {})
115
115
  NumberToCurrencyConverter.convert(number, options)
116
116
  end
@@ -124,6 +124,8 @@ module ActiveSupport
124
124
  # (defaults to current locale).
125
125
  # * <tt>:precision</tt> - Sets the precision of the number
126
126
  # (defaults to 3). Keeps the number's precision if +nil+.
127
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
128
+ # (defaults to :default. See BigDecimal::mode)
127
129
  # * <tt>:significant</tt> - If +true+, precision will be the number
128
130
  # of significant_digits. If +false+, the number of fractional
129
131
  # digits (defaults to +false+).
@@ -139,15 +141,16 @@ module ActiveSupport
139
141
  #
140
142
  # ==== Examples
141
143
  #
142
- # number_to_percentage(100) # => "100.000%"
143
- # number_to_percentage('98') # => "98.000%"
144
- # number_to_percentage(100, precision: 0) # => "100%"
145
- # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
146
- # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
147
- # number_to_percentage(1000, locale: :fr) # => "1000,000%"
148
- # number_to_percentage(1000, precision: nil) # => "1000%"
149
- # number_to_percentage('98a') # => "98a%"
150
- # number_to_percentage(100, format: '%n %') # => "100.000 %"
144
+ # number_to_percentage(100) # => "100.000%"
145
+ # number_to_percentage('98') # => "98.000%"
146
+ # number_to_percentage(100, precision: 0) # => "100%"
147
+ # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
148
+ # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
149
+ # number_to_percentage(1000, locale: :fr) # => "1000,000%"
150
+ # number_to_percentage(1000, precision: nil) # => "1000%"
151
+ # number_to_percentage('98a') # => "98a%"
152
+ # number_to_percentage(100, format: '%n %') # => "100.000 %"
153
+ # number_to_percentage(302.24398923423, precision: 5, round_mode: :down) # => "302.24398%"
151
154
  def number_to_percentage(number, options = {})
152
155
  NumberToPercentageConverter.convert(number, options)
153
156
  end
@@ -198,6 +201,8 @@ module ActiveSupport
198
201
  # (defaults to current locale).
199
202
  # * <tt>:precision</tt> - Sets the precision of the number
200
203
  # (defaults to 3). Keeps the number's precision if +nil+.
204
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
205
+ # (defaults to :default. See BigDecimal::mode)
201
206
  # * <tt>:significant</tt> - If +true+, precision will be the number
202
207
  # of significant_digits. If +false+, the number of fractional
203
208
  # digits (defaults to +false+).
@@ -219,6 +224,7 @@ module ActiveSupport
219
224
  # number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
220
225
  # number_to_rounded(13, precision: 5, significant: true) # => "13.000"
221
226
  # number_to_rounded(13, precision: nil) # => "13"
227
+ # number_to_rounded(389.32314, precision: 0, round_mode: :up) # => "390"
222
228
  # number_to_rounded(111.234, locale: :fr) # => "111,234"
223
229
  #
224
230
  # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
@@ -232,7 +238,7 @@ module ActiveSupport
232
238
  end
233
239
 
234
240
  # Formats the bytes in +number+ into a more understandable
235
- # representation (e.g., giving it 1500 yields 1.5 KB). This
241
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
236
242
  # method is useful for reporting file sizes to users. You can
237
243
  # customize the format in the +options+ hash.
238
244
  #
@@ -245,6 +251,8 @@ module ActiveSupport
245
251
  # (defaults to current locale).
246
252
  # * <tt>:precision</tt> - Sets the precision of the number
247
253
  # (defaults to 3).
254
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
255
+ # (defaults to :default. See BigDecimal::mode)
248
256
  # * <tt>:significant</tt> - If +true+, precision will be the number
249
257
  # of significant_digits. If +false+, the number of fractional
250
258
  # digits (defaults to +true+)
@@ -268,6 +276,7 @@ module ActiveSupport
268
276
  # number_to_human_size(1234567890123456789) # => "1.07 EB"
269
277
  # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
270
278
  # number_to_human_size(483989, precision: 2) # => "470 KB"
279
+ # number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB"
271
280
  # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
272
281
  # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
273
282
  # number_to_human_size(524288000, precision: 5) # => "500 MB"
@@ -276,7 +285,7 @@ module ActiveSupport
276
285
  end
277
286
 
278
287
  # Pretty prints (formats and approximates) a number in a way it
279
- # is more readable by humans (eg.: 1200000000 becomes "1.2
288
+ # is more readable by humans (e.g.: 1200000000 becomes "1.2
280
289
  # Billion"). This is useful for numbers that can get very large
281
290
  # (and too hard to read).
282
291
  #
@@ -284,7 +293,7 @@ module ActiveSupport
284
293
  # size.
285
294
  #
286
295
  # You can also define your own unit-quantifier names if you want
287
- # to use other decimal units (eg.: 1500 becomes "1.5
296
+ # to use other decimal units (e.g.: 1500 becomes "1.5
288
297
  # kilometers", 0.150 becomes "150 milliliters", etc). You may
289
298
  # define a wide range of unit quantifiers, even fractional ones
290
299
  # (centi, deci, mili, etc).
@@ -295,6 +304,8 @@ module ActiveSupport
295
304
  # (defaults to current locale).
296
305
  # * <tt>:precision</tt> - Sets the precision of the number
297
306
  # (defaults to 3).
307
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
308
+ # (defaults to :default. See BigDecimal::mode)
298
309
  # * <tt>:significant</tt> - If +true+, precision will be the number
299
310
  # of significant_digits. If +false+, the number of fractional
300
311
  # digits (defaults to +true+)
@@ -332,6 +343,8 @@ module ActiveSupport
332
343
  # number_to_human(1234567890123456789) # => "1230 Quadrillion"
333
344
  # number_to_human(489939, precision: 2) # => "490 Thousand"
334
345
  # number_to_human(489939, precision: 4) # => "489.9 Thousand"
346
+ # number_to_human(489939, precision: 2
347
+ # , round_mode: :down) # => "480 Thousand"
335
348
  # number_to_human(1234567, precision: 4,
336
349
  # significant: false) # => "1.2346 Million"
337
350
  # number_to_human(1234567, precision: 1,
@@ -3,9 +3,9 @@
3
3
  require "active_support/core_ext/hash/deep_merge"
4
4
 
5
5
  module ActiveSupport
6
- class OptionMerger #:nodoc:
6
+ class OptionMerger # :nodoc:
7
7
  instance_methods.each do |method|
8
- undef_method(method) if !/^(__|instance_eval|class|object_id)/.match?(method)
8
+ undef_method(method) unless method.start_with?("__", "instance_eval", "class", "object_id")
9
9
  end
10
10
 
11
11
  def initialize(context, options)
@@ -24,22 +24,15 @@ module ActiveSupport
24
24
  options = @options
25
25
  end
26
26
 
27
- invoke_method(method, arguments, options, &block)
28
- end
29
-
30
- if RUBY_VERSION >= "2.7"
31
- def invoke_method(method, arguments, options, &block)
32
- if options
33
- @context.__send__(method, *arguments, **options, &block)
34
- else
35
- @context.__send__(method, *arguments, &block)
36
- end
37
- end
38
- else
39
- def invoke_method(method, arguments, options, &block)
40
- arguments << options if options
27
+ if options
28
+ @context.__send__(method, *arguments, **options, &block)
29
+ else
41
30
  @context.__send__(method, *arguments, &block)
42
31
  end
43
32
  end
33
+
34
+ def respond_to_missing?(*arguments)
35
+ @context.respond_to?(*arguments)
36
+ end
44
37
  end
45
38
  end
@@ -21,7 +21,7 @@ module ActiveSupport
21
21
  #
22
22
  # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
23
23
  # with other implementations.
24
- class OrderedHash < ::Hash
24
+ class OrderedHash < ::Hash # :nodoc:
25
25
  def to_yaml_type
26
26
  "!tag:yaml.org,2002:omap"
27
27
  end
@@ -3,7 +3,9 @@
3
3
  require "active_support/core_ext/object/blank"
4
4
 
5
5
  module ActiveSupport
6
- # Usually key value pairs are handled something like this:
6
+ # +OrderedOptions+ inherits from +Hash+ and provides dynamic accessor methods.
7
+ #
8
+ # With a +Hash+, key-value pairs are typically managed like this:
7
9
  #
8
10
  # h = {}
9
11
  # h[:boy] = 'John'
@@ -12,7 +14,7 @@ module ActiveSupport
12
14
  # h[:girl] # => 'Mary'
13
15
  # h[:dog] # => nil
14
16
  #
15
- # Using +OrderedOptions+, the above code could be reduced to:
17
+ # Using +OrderedOptions+, the above code can be written as:
16
18
  #
17
19
  # h = ActiveSupport::OrderedOptions.new
18
20
  # h.boy = 'John'
@@ -60,6 +62,10 @@ module ActiveSupport
60
62
  def extractable_options?
61
63
  true
62
64
  end
65
+
66
+ def inspect
67
+ "#<#{self.class.name} #{super}>"
68
+ end
63
69
  end
64
70
 
65
71
  # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/object/duplicable"
4
- require "active_support/core_ext/array/extract"
5
4
 
6
5
  module ActiveSupport
7
6
  # +ParameterFilter+ allows you to specify keys for sensitive data from
@@ -17,12 +16,17 @@ module ActiveSupport
17
16
  # ActiveSupport::ParameterFilter.new([:foo, "bar"])
18
17
  # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
19
18
  #
19
+ # ActiveSupport::ParameterFilter.new([/\Apin\z/i, /\Apin_/i])
20
+ # => replaces the value for the exact (case-insensitive) key 'pin' and all
21
+ # (case-insensitive) keys beginning with 'pin_', with "[FILTERED]".
22
+ # Does not match keys with 'pin' as a substring, such as 'shipping_id'.
23
+ #
20
24
  # ActiveSupport::ParameterFilter.new(["credit_card.code"])
21
25
  # => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
22
26
  # change { file: { code: "xxxx"} }
23
27
  #
24
28
  # ActiveSupport::ParameterFilter.new([-> (k, v) do
25
- # v.reverse! if k =~ /secret/i
29
+ # v.reverse! if /secret/i.match?(k)
26
30
  # end])
27
31
  # => reverses the value to all keys matching /secret/i
28
32
  class ParameterFilter
@@ -34,7 +38,7 @@ module ActiveSupport
34
38
  #
35
39
  # ==== Options
36
40
  #
37
- # * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
41
+ # * <tt>:mask</tt> - A replaced object when filtered. Defaults to <tt>"[FILTERED]"</tt>.
38
42
  def initialize(filters = [], mask: FILTERED)
39
43
  @filters = filters
40
44
  @mask = mask
@@ -59,24 +63,30 @@ module ActiveSupport
59
63
  def self.compile(filters, mask:)
60
64
  return lambda { |params| params.dup } if filters.empty?
61
65
 
62
- strings, regexps, blocks = [], [], []
66
+ strings, regexps, blocks, deep_regexps, deep_strings = [], [], [], nil, nil
63
67
 
64
68
  filters.each do |item|
65
69
  case item
66
70
  when Proc
67
71
  blocks << item
68
72
  when Regexp
69
- regexps << item
73
+ if item.to_s.include?("\\.")
74
+ (deep_regexps ||= []) << item
75
+ else
76
+ regexps << item
77
+ end
70
78
  else
71
- strings << Regexp.escape(item.to_s)
79
+ s = Regexp.escape(item.to_s)
80
+ if s.include?("\\.")
81
+ (deep_strings ||= []) << s
82
+ else
83
+ strings << s
84
+ end
72
85
  end
73
86
  end
74
87
 
75
- deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.") }
76
- deep_strings = strings.extract! { |s| s.include?("\\.") }
77
-
78
88
  regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
79
- deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
89
+ (deep_regexps ||= []) << Regexp.new(deep_strings.join("|"), true) if deep_strings&.any?
80
90
 
81
91
  new regexps, deep_regexps, blocks, mask: mask
82
92
  end
@@ -85,7 +95,7 @@ module ActiveSupport
85
95
 
86
96
  def initialize(regexps, deep_regexps, blocks, mask:)
87
97
  @regexps = regexps
88
- @deep_regexps = deep_regexps.any? ? deep_regexps : nil
98
+ @deep_regexps = deep_regexps&.any? ? deep_regexps : nil
89
99
  @blocks = blocks
90
100
  @mask = mask
91
101
  end
@@ -40,7 +40,11 @@ module ActiveSupport
40
40
  # If the class has an initializer, it must accept no arguments.
41
41
  module PerThreadRegistry
42
42
  def self.extended(object)
43
- object.instance_variable_set "@per_thread_registry_key", object.name.freeze
43
+ ActiveSupport::Deprecation.warn(<<~MSG)
44
+ ActiveSupport::PerThreadRegistry is deprecated and will be removed in Rails 7.1.
45
+ Use `Module#thread_mattr_accessor` instead.
46
+ MSG
47
+ object.instance_variable_set :@per_thread_registry_key, object.name.freeze
44
48
  end
45
49
 
46
50
  def instance
@@ -56,5 +60,6 @@ module ActiveSupport
56
60
 
57
61
  send(name, *args, &block)
58
62
  end
63
+ ruby2_keywords(:method_missing)
59
64
  end
60
65
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This is private interface.
3
+ # This is a private interface.
4
4
  #
5
5
  # Rails components cherry pick from Active Support as needed, but there are a
6
6
  # few features that are used for sure in some way or another and it is not worth
@@ -13,9 +13,6 @@
13
13
  # Defines Object#blank? and Object#present?.
14
14
  require "active_support/core_ext/object/blank"
15
15
 
16
- # Rails own autoload, eager_load, etc.
17
- require "active_support/dependencies/autoload"
18
-
19
16
  # Support for ClassMethods and the included macro.
20
17
  require "active_support/concern"
21
18
 
@@ -6,9 +6,25 @@ require "active_support/i18n_railtie"
6
6
  module ActiveSupport
7
7
  class Railtie < Rails::Railtie # :nodoc:
8
8
  config.active_support = ActiveSupport::OrderedOptions.new
9
+ config.active_support.disable_to_s_conversion = false
9
10
 
10
11
  config.eager_load_namespaces << ActiveSupport
11
12
 
13
+ initializer "active_support.isolation_level" do |app|
14
+ if level = app.config.active_support.delete(:isolation_level)
15
+ ActiveSupport::IsolatedExecutionState.isolation_level = level
16
+ end
17
+ end
18
+
19
+ initializer "active_support.remove_deprecated_time_with_zone_name" do |app|
20
+ config.after_initialize do
21
+ if app.config.active_support.remove_deprecated_time_with_zone_name
22
+ require "active_support/time_with_zone"
23
+ TimeWithZone.singleton_class.remove_method(:name)
24
+ end
25
+ end
26
+ end
27
+
12
28
  initializer "active_support.set_authenticated_message_encryption" do |app|
13
29
  config.after_initialize do
14
30
  unless app.config.active_support.use_authenticated_message_encryption.nil?
@@ -18,15 +34,50 @@ module ActiveSupport
18
34
  end
19
35
  end
20
36
 
37
+ initializer "active_support.reset_execution_context" do |app|
38
+ app.reloader.before_class_unload { ActiveSupport::ExecutionContext.clear }
39
+ app.executor.to_run { ActiveSupport::ExecutionContext.clear }
40
+ app.executor.to_complete { ActiveSupport::ExecutionContext.clear }
41
+ end
42
+
21
43
  initializer "active_support.reset_all_current_attributes_instances" do |app|
44
+ executor_around_test_case = app.config.active_support.executor_around_test_case
45
+
22
46
  app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all }
23
47
  app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all }
24
48
  app.executor.to_complete { ActiveSupport::CurrentAttributes.reset_all }
49
+
50
+ ActiveSupport.on_load(:active_support_test_case) do
51
+ if executor_around_test_case
52
+ require "active_support/executor/test_helper"
53
+ include ActiveSupport::Executor::TestHelper
54
+ else
55
+ require "active_support/current_attributes/test_helper"
56
+ include ActiveSupport::CurrentAttributes::TestHelper
57
+
58
+ require "active_support/execution_context/test_helper"
59
+ include ActiveSupport::ExecutionContext::TestHelper
60
+ end
61
+ end
25
62
  end
26
63
 
27
64
  initializer "active_support.deprecation_behavior" do |app|
28
- if deprecation = app.config.active_support.deprecation
29
- ActiveSupport::Deprecation.behavior = deprecation
65
+ if app.config.active_support.report_deprecations == false
66
+ ActiveSupport::Deprecation.silenced = true
67
+ ActiveSupport::Deprecation.behavior = :silence
68
+ ActiveSupport::Deprecation.disallowed_behavior = :silence
69
+ else
70
+ if deprecation = app.config.active_support.deprecation
71
+ ActiveSupport::Deprecation.behavior = deprecation
72
+ end
73
+
74
+ if disallowed_deprecation = app.config.active_support.disallowed_deprecation
75
+ ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation
76
+ end
77
+
78
+ if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings
79
+ ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings
80
+ end
30
81
  end
31
82
  end
32
83
 
@@ -62,17 +113,38 @@ module ActiveSupport
62
113
  end
63
114
  end
64
115
 
116
+ initializer "active_support.set_error_reporter" do |app|
117
+ ActiveSupport.error_reporter = app.executor.error_reporter
118
+ end
119
+
65
120
  initializer "active_support.set_configs" do |app|
66
121
  app.config.active_support.each do |k, v|
67
122
  k = "#{k}="
68
- ActiveSupport.send(k, v) if ActiveSupport.respond_to? k
123
+ ActiveSupport.public_send(k, v) if ActiveSupport.respond_to? k
69
124
  end
70
125
  end
71
126
 
72
127
  initializer "active_support.set_hash_digest_class" do |app|
73
128
  config.after_initialize do
74
- if app.config.active_support.use_sha1_digests
75
- ActiveSupport::Digest.hash_digest_class = ::Digest::SHA1
129
+ if klass = app.config.active_support.hash_digest_class
130
+ ActiveSupport::Digest.hash_digest_class = klass
131
+ end
132
+ end
133
+ end
134
+
135
+ initializer "active_support.set_key_generator_hash_digest_class" do |app|
136
+ config.after_initialize do
137
+ if klass = app.config.active_support.key_generator_hash_digest_class
138
+ ActiveSupport::KeyGenerator.hash_digest_class = klass
139
+ end
140
+ end
141
+ end
142
+
143
+ initializer "active_support.set_rfc4122_namespaced_uuids" do |app|
144
+ config.after_initialize do
145
+ if app.config.active_support.use_rfc4122_namespaced_uuids
146
+ require "active_support/core_ext/digest"
147
+ ::Digest::UUID.use_rfc4122_namespaced_uuids = app.config.active_support.use_rfc4122_namespaced_uuids
76
148
  end
77
149
  end
78
150
  end
@@ -14,12 +14,12 @@ module ActiveSupport
14
14
  end
15
15
 
16
16
  module ClassMethods
17
- # Rescue exceptions raised in controller actions.
17
+ # Registers exception classes with a handler to be called by <tt>rescue_with_handler</tt>.
18
18
  #
19
19
  # <tt>rescue_from</tt> receives a series of exception classes or class
20
- # names, and a trailing <tt>:with</tt> option with the name of a method
21
- # or a Proc object to be called to handle them. Alternatively a block can
22
- # be given.
20
+ # names, and an exception handler specified by a trailing <tt>:with</tt>
21
+ # option containing the name of a method or a Proc object. Alternatively, a block
22
+ # can be given as the handler.
23
23
  #
24
24
  # Handlers that take one argument will be called with the exception, so
25
25
  # that the exception can be inspected when dealing with it.
@@ -100,7 +100,7 @@ module ActiveSupport
100
100
  end
101
101
  end
102
102
 
103
- def handler_for_rescue(exception, object: self) #:nodoc:
103
+ def handler_for_rescue(exception, object: self) # :nodoc:
104
104
  case rescuer = find_rescue_handler(exception)
105
105
  when Symbol
106
106
  method = object.method(rescuer)
@@ -167,7 +167,7 @@ module ActiveSupport
167
167
 
168
168
  # Internal handler lookup. Delegates to class method. Some libraries call
169
169
  # this directly, so keeping it around for compatibility.
170
- def handler_for_rescue(exception) #:nodoc:
170
+ def handler_for_rescue(exception) # :nodoc:
171
171
  self.class.handler_for_rescue exception, object: self
172
172
  end
173
173
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module RubyFeatures # :nodoc:
5
+ CLASS_SUBCLASSES = Class.method_defined?(:subclasses) # RUBY_VERSION >= "3.1"
6
+ end
7
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/security_utils"
4
+ require "active_support/messages/rotator"
5
+
6
+ module ActiveSupport
7
+ # The ActiveSupport::SecureCompareRotator is a wrapper around +ActiveSupport::SecurityUtils.secure_compare+
8
+ # and allows you to rotate a previously defined value to a new one.
9
+ #
10
+ # It can be used as follow:
11
+ #
12
+ # rotator = ActiveSupport::SecureCompareRotator.new('new_production_value')
13
+ # rotator.rotate('previous_production_value')
14
+ # rotator.secure_compare!('previous_production_value')
15
+ #
16
+ # One real use case example would be to rotate a basic auth credentials:
17
+ #
18
+ # class MyController < ApplicationController
19
+ # def authenticate_request
20
+ # rotator = ActiveSupport::SecureCompareRotator.new('new_password')
21
+ # rotator.rotate('old_password')
22
+ #
23
+ # authenticate_or_request_with_http_basic do |username, password|
24
+ # rotator.secure_compare!(password)
25
+ # rescue ActiveSupport::SecureCompareRotator::InvalidMatch
26
+ # false
27
+ # end
28
+ # end
29
+ # end
30
+ class SecureCompareRotator
31
+ include SecurityUtils
32
+ prepend Messages::Rotator
33
+
34
+ InvalidMatch = Class.new(StandardError)
35
+
36
+ def initialize(value, **_options)
37
+ @value = value
38
+ end
39
+
40
+ def secure_compare!(other_value, on_rotation: @on_rotation)
41
+ secure_compare(@value, other_value) ||
42
+ run_rotations(on_rotation) { |wrapper| wrapper.secure_compare!(other_value) } ||
43
+ raise(InvalidMatch)
44
+ end
45
+
46
+ private
47
+ def build_rotation(previous_value, _options)
48
+ self.class.new(previous_value)
49
+ end
50
+ end
51
+ end