activesupport 6.1.0 → 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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +263 -352
  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/backtrace_cleaner.rb +2 -2
  8. data/lib/active_support/benchmarkable.rb +2 -2
  9. data/lib/active_support/cache/file_store.rb +16 -10
  10. data/lib/active_support/cache/mem_cache_store.rb +154 -39
  11. data/lib/active_support/cache/memory_store.rb +24 -16
  12. data/lib/active_support/cache/null_store.rb +10 -2
  13. data/lib/active_support/cache/redis_cache_store.rb +59 -78
  14. data/lib/active_support/cache/strategy/local_cache.rb +38 -61
  15. data/lib/active_support/cache.rb +306 -148
  16. data/lib/active_support/callbacks.rb +184 -85
  17. data/lib/active_support/code_generator.rb +65 -0
  18. data/lib/active_support/concern.rb +5 -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 +8 -5
  22. data/lib/active_support/configuration_file.rb +7 -2
  23. data/lib/active_support/core_ext/array/access.rb +1 -5
  24. data/lib/active_support/core_ext/array/conversions.rb +13 -12
  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/inquiry.rb +2 -2
  28. data/lib/active_support/core_ext/array.rb +1 -0
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  30. data/lib/active_support/core_ext/class/subclasses.rb +25 -17
  31. data/lib/active_support/core_ext/date/blank.rb +1 -1
  32. data/lib/active_support/core_ext/date/calculations.rb +9 -9
  33. data/lib/active_support/core_ext/date/conversions.rb +14 -14
  34. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  35. data/lib/active_support/core_ext/date.rb +1 -0
  36. data/lib/active_support/core_ext/date_and_time/calculations.rb +4 -4
  37. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  38. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  39. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  40. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  41. data/lib/active_support/core_ext/date_time.rb +1 -0
  42. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  43. data/lib/active_support/core_ext/enumerable.rb +101 -32
  44. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  45. data/lib/active_support/core_ext/hash/conversions.rb +0 -1
  46. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  47. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  48. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  49. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  50. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
  51. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
  52. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  53. data/lib/active_support/core_ext/name_error.rb +2 -8
  54. data/lib/active_support/core_ext/numeric/conversions.rb +80 -77
  55. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  56. data/lib/active_support/core_ext/numeric.rb +1 -0
  57. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  58. data/lib/active_support/core_ext/object/blank.rb +2 -2
  59. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  60. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  61. data/lib/active_support/core_ext/object/json.rb +37 -25
  62. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  63. data/lib/active_support/core_ext/object/try.rb +20 -20
  64. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  65. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  66. data/lib/active_support/core_ext/pathname.rb +3 -0
  67. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  68. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  69. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  70. data/lib/active_support/core_ext/range/each.rb +1 -1
  71. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -25
  72. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  73. data/lib/active_support/core_ext/range.rb +1 -1
  74. data/lib/active_support/core_ext/securerandom.rb +1 -1
  75. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  76. data/lib/active_support/core_ext/string/filters.rb +1 -1
  77. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  78. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  79. data/lib/active_support/core_ext/string/output_safety.rb +91 -39
  80. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  81. data/lib/active_support/core_ext/time/calculations.rb +9 -7
  82. data/lib/active_support/core_ext/time/conversions.rb +14 -12
  83. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  84. data/lib/active_support/core_ext/time/zones.rb +7 -22
  85. data/lib/active_support/core_ext/time.rb +1 -0
  86. data/lib/active_support/core_ext/uri.rb +3 -27
  87. data/lib/active_support/core_ext.rb +2 -1
  88. data/lib/active_support/current_attributes.rb +32 -14
  89. data/lib/active_support/dependencies/interlock.rb +10 -18
  90. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  91. data/lib/active_support/dependencies.rb +58 -788
  92. data/lib/active_support/deprecation/behaviors.rb +8 -5
  93. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  94. data/lib/active_support/deprecation/proxy_wrappers.rb +2 -2
  95. data/lib/active_support/deprecation.rb +2 -2
  96. data/lib/active_support/descendants_tracker.rb +174 -68
  97. data/lib/active_support/digest.rb +5 -3
  98. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  99. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  100. data/lib/active_support/duration.rb +81 -51
  101. data/lib/active_support/encrypted_configuration.rb +13 -2
  102. data/lib/active_support/encrypted_file.rb +13 -1
  103. data/lib/active_support/environment_inquirer.rb +1 -1
  104. data/lib/active_support/error_reporter.rb +117 -0
  105. data/lib/active_support/evented_file_update_checker.rb +3 -5
  106. data/lib/active_support/execution_context/test_helper.rb +13 -0
  107. data/lib/active_support/execution_context.rb +53 -0
  108. data/lib/active_support/execution_wrapper.rb +43 -21
  109. data/lib/active_support/executor/test_helper.rb +7 -0
  110. data/lib/active_support/fork_tracker.rb +19 -10
  111. data/lib/active_support/gem_version.rb +5 -5
  112. data/lib/active_support/hash_with_indifferent_access.rb +9 -2
  113. data/lib/active_support/html_safe_translation.rb +43 -0
  114. data/lib/active_support/i18n.rb +1 -0
  115. data/lib/active_support/i18n_railtie.rb +1 -1
  116. data/lib/active_support/inflector/inflections.rb +23 -7
  117. data/lib/active_support/inflector/methods.rb +24 -48
  118. data/lib/active_support/inflector/transliterate.rb +1 -1
  119. data/lib/active_support/isolated_execution_state.rb +72 -0
  120. data/lib/active_support/json/encoding.rb +3 -3
  121. data/lib/active_support/key_generator.rb +22 -5
  122. data/lib/active_support/lazy_load_hooks.rb +28 -4
  123. data/lib/active_support/locale/en.yml +2 -2
  124. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  125. data/lib/active_support/log_subscriber.rb +15 -5
  126. data/lib/active_support/logger_thread_safe_level.rb +4 -13
  127. data/lib/active_support/message_encryptor.rb +12 -6
  128. data/lib/active_support/message_verifier.rb +46 -14
  129. data/lib/active_support/messages/metadata.rb +2 -2
  130. data/lib/active_support/multibyte/chars.rb +10 -11
  131. data/lib/active_support/multibyte/unicode.rb +0 -12
  132. data/lib/active_support/multibyte.rb +1 -1
  133. data/lib/active_support/notifications/fanout.rb +91 -65
  134. data/lib/active_support/notifications/instrumenter.rb +32 -15
  135. data/lib/active_support/notifications.rb +24 -24
  136. data/lib/active_support/number_helper/number_converter.rb +1 -3
  137. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  138. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  139. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  140. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  141. data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
  142. data/lib/active_support/number_helper/rounding_helper.rb +2 -6
  143. data/lib/active_support/number_helper.rb +0 -2
  144. data/lib/active_support/option_merger.rb +10 -18
  145. data/lib/active_support/ordered_hash.rb +1 -1
  146. data/lib/active_support/ordered_options.rb +1 -1
  147. data/lib/active_support/parameter_filter.rb +6 -1
  148. data/lib/active_support/per_thread_registry.rb +5 -0
  149. data/lib/active_support/railtie.rb +69 -19
  150. data/lib/active_support/reloader.rb +1 -1
  151. data/lib/active_support/rescuable.rb +16 -16
  152. data/lib/active_support/ruby_features.rb +7 -0
  153. data/lib/active_support/secure_compare_rotator.rb +2 -2
  154. data/lib/active_support/security_utils.rb +1 -1
  155. data/lib/active_support/string_inquirer.rb +0 -2
  156. data/lib/active_support/subscriber.rb +7 -18
  157. data/lib/active_support/tagged_logging.rb +2 -2
  158. data/lib/active_support/test_case.rb +13 -21
  159. data/lib/active_support/testing/assertions.rb +36 -6
  160. data/lib/active_support/testing/deprecation.rb +52 -1
  161. data/lib/active_support/testing/isolation.rb +2 -2
  162. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  163. data/lib/active_support/testing/parallelization/server.rb +4 -0
  164. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  165. data/lib/active_support/testing/parallelization.rb +4 -0
  166. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  167. data/lib/active_support/testing/stream.rb +3 -5
  168. data/lib/active_support/testing/tagged_logging.rb +1 -1
  169. data/lib/active_support/testing/time_helpers.rb +13 -2
  170. data/lib/active_support/time_with_zone.rb +60 -20
  171. data/lib/active_support/values/time_zone.rb +36 -15
  172. data/lib/active_support/version.rb +1 -1
  173. data/lib/active_support/xml_mini/jdom.rb +1 -1
  174. data/lib/active_support/xml_mini/libxml.rb +5 -5
  175. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  176. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  177. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  178. data/lib/active_support/xml_mini/rexml.rb +1 -1
  179. data/lib/active_support/xml_mini.rb +5 -4
  180. data/lib/active_support.rb +17 -1
  181. metadata +29 -26
  182. data/lib/active_support/core_ext/marshal.rb +0 -26
  183. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -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,19 +64,30 @@ 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
@@ -75,17 +123,17 @@ module Enumerable
75
123
  #
76
124
  # %i( created_at updated_at ).index_with(Time.now)
77
125
  # # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
78
- def index_with(default = INDEX_WITH_DEFAULT)
126
+ def index_with(default = (no_default = true))
79
127
  if block_given?
80
128
  result = {}
81
129
  each { |elem| result[elem] = yield(elem) }
82
130
  result
83
- elsif default != INDEX_WITH_DEFAULT
131
+ elsif no_default
132
+ to_enum(:index_with) { size if respond_to?(:size) }
133
+ else
84
134
  result = {}
85
135
  each { |elem| result[elem] = default }
86
136
  result
87
- else
88
- to_enum(:index_with) { size if respond_to?(:size) }
89
137
  end
90
138
  end
91
139
 
@@ -136,11 +184,7 @@ module Enumerable
136
184
  elements.flatten!(1)
137
185
  reject { |element| elements.include?(element) }
138
186
  end
139
-
140
- # Alias for #excluding.
141
- def without(*elements)
142
- excluding(*elements)
143
- end
187
+ alias :without :excluding
144
188
 
145
189
  # Extract the given key from each element in the enumerable.
146
190
  #
@@ -178,40 +222,66 @@ module Enumerable
178
222
  # Returns a new +Array+ without the blank items.
179
223
  # Uses Object#blank? for determining if an item is blank.
180
224
  #
181
- # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
182
- # # => [1, 2, true]
225
+ # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
226
+ # # => [1, 2, true]
183
227
  #
184
- # Set.new([nil, "", 1, 2])
185
- # # => [2, 1] (or [1, 2])
228
+ # Set.new([nil, "", 1, 2])
229
+ # # => [2, 1] (or [1, 2])
186
230
  #
187
231
  # When called on a +Hash+, returns a new +Hash+ without the blank values.
188
232
  #
189
- # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
190
- # #=> { b: 1, f: true }
233
+ # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
234
+ # # => { b: 1, f: true }
191
235
  def compact_blank
192
236
  reject(&:blank?)
193
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
194
264
  end
195
265
 
196
266
  class Hash
197
267
  # Hash#reject has its own definition, so this needs one too.
198
- def compact_blank #:nodoc:
268
+ def compact_blank # :nodoc:
199
269
  reject { |_k, v| v.blank? }
200
270
  end
201
271
 
202
272
  # Removes all blank values from the +Hash+ in place and returns self.
203
273
  # Uses Object#blank? for determining if a value is blank.
204
274
  #
205
- # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
206
- # h.compact_blank!
207
- # # => { b: 1, f: true }
275
+ # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
276
+ # h.compact_blank!
277
+ # # => { b: 1, f: true }
208
278
  def compact_blank!
209
279
  # use delete_if rather than reject! because it always returns self even if nothing changed
210
280
  delete_if { |_k, v| v.blank? }
211
281
  end
212
282
  end
213
283
 
214
- class Range #:nodoc:
284
+ class Range # :nodoc:
215
285
  # Optimize range sum to use arithmetic progression if a block is not given and
216
286
  # we have a range of numeric values.
217
287
  def sum(identity = nil)
@@ -236,8 +306,7 @@ using Module.new {
236
306
  end
237
307
  }
238
308
 
239
- class Array #:nodoc:
240
- # Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
309
+ class Array # :nodoc:
241
310
  def sum(init = nil, &block)
242
311
  if init.is_a?(Numeric) || first.is_a?(Numeric)
243
312
  init ||= 0
@@ -250,9 +319,9 @@ class Array #:nodoc:
250
319
  # Removes all blank elements from the +Array+ in place and returns self.
251
320
  # Uses Object#blank? for determining if an item is blank.
252
321
  #
253
- # a = [1, "", nil, 2, " ", [], {}, false, true]
254
- # a.compact_blank!
255
- # # => [1, 2, true]
322
+ # a = [1, "", nil, 2, " ", [], {}, false, true]
323
+ # a.compact_blank!
324
+ # # => [1, 2, true]
256
325
  def compact_blank!
257
326
  # use delete_if rather than reject! because it always returns self even if nothing changed
258
327
  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
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/xml_mini"
4
3
  require "active_support/core_ext/object/blank"
5
4
  require "active_support/core_ext/object/to_param"
6
5
  require "active_support/core_ext/object/to_query"
@@ -3,7 +3,7 @@
3
3
  require "active_support/hash_with_indifferent_access"
4
4
 
5
5
  class Hash
6
- # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
6
+ # Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver:
7
7
  #
8
8
  # { a: 1 }.with_indifferent_access['a'] # => 1
9
9
  def with_indifferent_access
@@ -13,8 +13,8 @@ class Hash
13
13
  # Called when object is nested under an object that receives
14
14
  # #with_indifferent_access. This method will be called on the current object
15
15
  # by the enclosing object and is aliased to #with_indifferent_access by
16
- # default. Subclasses of Hash may overwrite this method to return +self+ if
17
- # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
16
+ # default. Subclasses of Hash may override this method to return +self+ if
17
+ # converting to an ActiveSupport::HashWithIndifferentAccess would not be
18
18
  # desirable.
19
19
  #
20
20
  # b = { b: 1 }
@@ -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
@@ -11,14 +11,14 @@ module Kernel
11
11
  # end
12
12
  #
13
13
  # noisy_call # warning voiced
14
- def silence_warnings
15
- with_warnings(nil) { yield }
14
+ def silence_warnings(&block)
15
+ with_warnings(nil, &block)
16
16
  end
17
17
 
18
18
  # Sets $VERBOSE to +true+ for the duration of the block and back to its
19
19
  # original value afterwards.
20
- def enable_warnings
21
- with_warnings(true) { yield }
20
+ def enable_warnings(&block)
21
+ with_warnings(true, &block)
22
22
  end
23
23
 
24
24
  # Sets $VERBOSE for the duration of the block and back to its original
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kernel
4
- # class_eval on an object acts like singleton_class.class_eval.
4
+ # class_eval on an object acts like +singleton_class.class_eval+.
5
5
  def class_eval(*args, &block)
6
6
  singleton_class.class_eval(*args, &block)
7
7
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # == Attribute Accessors
4
+ #
3
5
  # Extends the module object with class/module and instance accessors for
4
6
  # class/module attributes, just like the native attr* accessors for instance
5
7
  # attributes.
@@ -1,11 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # == Attribute Accessors per Thread
4
+ #
3
5
  # Extends the module object with class/module and instance accessors for
4
6
  # class/module attributes, just like the native attr* accessors for instance
5
7
  # attributes, but does so on a per-thread basis.
6
8
  #
7
9
  # So the values are scoped within the Thread.current space under the class name
8
10
  # of the module.
11
+ #
12
+ # Note that it can also be scoped per-fiber if +Rails.application.config.active_support.isolation_level+
13
+ # is set to +:fiber+.
9
14
  class Module
10
15
  # Defines a per-thread class attribute and creates class and instance reader methods.
11
16
  # The underlying per-thread class variable is set to +nil+, if it is not previously defined.
@@ -14,9 +19,9 @@ class Module
14
19
  # thread_mattr_reader :user
15
20
  # end
16
21
  #
17
- # Current.user # => nil
18
- # Thread.current[:attr_Current_user] = "DHH"
22
+ # Current.user = "DHH"
19
23
  # Current.user # => "DHH"
24
+ # Thread.new { Current.user }.value # => nil
20
25
  #
21
26
  # The attribute name must be a valid method name in Ruby.
22
27
  #
@@ -41,7 +46,8 @@ class Module
41
46
  # to work with inheritance via polymorphism.
42
47
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
43
48
  def self.#{sym}
44
- Thread.current["attr_" + name + "_#{sym}"]
49
+ @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}"
50
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
45
51
  end
46
52
  EOS
47
53
 
@@ -53,7 +59,7 @@ class Module
53
59
  EOS
54
60
  end
55
61
 
56
- Thread.current["attr_" + name + "_#{sym}"] = default unless default.nil?
62
+ ::ActiveSupport::IsolatedExecutionState["attr_#{name}_#{sym}"] = default unless default.nil?
57
63
  end
58
64
  end
59
65
  alias :thread_cattr_reader :thread_mattr_reader
@@ -84,7 +90,8 @@ class Module
84
90
  # to work with inheritance via polymorphism.
85
91
  class_eval(<<-EOS, __FILE__, __LINE__ + 1)
86
92
  def self.#{sym}=(obj)
87
- Thread.current["attr_" + name + "_#{sym}"] = obj
93
+ @__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}"
94
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj
88
95
  end
89
96
  EOS
90
97
 
@@ -111,16 +118,18 @@ class Module
111
118
  # Account.user # => "DHH"
112
119
  # Account.new.user # => "DHH"
113
120
  #
121
+ # Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes.
114
122
  # If a subclass changes the value, the parent class' value is not changed.
115
- # Similarly, if the parent class changes the value, the value of subclasses
116
- # is not changed.
123
+ # If the parent class changes the value, the value of subclasses is not changed.
117
124
  #
118
125
  # class Customer < Account
119
126
  # end
120
127
  #
121
- # Customer.user = "Rafael"
122
- # Customer.user # => "Rafael"
123
- # Account.user # => "DHH"
128
+ # Account.user # => "DHH"
129
+ # Customer.user # => nil
130
+ # Customer.user = "Rafael"
131
+ # Customer.user # => "Rafael"
132
+ # Account.user # => "DHH"
124
133
  #
125
134
  # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
126
135
  # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
@@ -199,13 +199,7 @@ class Module
199
199
 
200
200
  # Attribute writer methods only accept one argument. Makes sure []=
201
201
  # methods still accept two arguments.
202
- definition = if /[^\]]=$/.match?(method)
203
- "arg"
204
- elsif RUBY_VERSION >= "2.7"
205
- "..."
206
- else
207
- "*args, &block"
208
- end
202
+ definition = /[^\]]=\z/.match?(method) ? "arg" : "..."
209
203
 
210
204
  # The following generated method calls the target exactly once, storing
211
205
  # the returned value in a dummy variable.
@@ -324,7 +318,7 @@ class Module
324
318
  end
325
319
  end
326
320
  end
327
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
321
+ ruby2_keywords(:method_missing)
328
322
  RUBY
329
323
  end
330
324
  end
@@ -53,13 +53,7 @@ class NameError
53
53
  UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
54
54
  private_constant :UNBOUND_METHOD_MODULE_NAME
55
55
 
56
- if UnboundMethod.method_defined?(:bind_call)
57
- def real_mod_name(mod)
58
- UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
59
- end
60
- else
61
- def real_mod_name(mod)
62
- UNBOUND_METHOD_MODULE_NAME.bind(mod).call
63
- end
56
+ def real_mod_name(mod)
57
+ UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
64
58
  end
65
59
  end
@@ -7,7 +7,9 @@ module ActiveSupport
7
7
  module NumericWithFormat
8
8
  # Provides options for converting numbers into formatted strings.
9
9
  # Options are provided for phone numbers, currency, percentage,
10
- # precision, positional notation, file size and pretty printing.
10
+ # precision, positional notation, file size, and pretty printing.
11
+ #
12
+ # This method is aliased to <tt>to_formatted_s</tt>.
11
13
  #
12
14
  # ==== Options
13
15
  #
@@ -16,102 +18,102 @@ module ActiveSupport
16
18
  # ==== Examples
17
19
  #
18
20
  # Phone Numbers:
19
- # 5551234.to_s(:phone) # => "555-1234"
20
- # 1235551234.to_s(:phone) # => "123-555-1234"
21
- # 1235551234.to_s(:phone, area_code: true) # => "(123) 555-1234"
22
- # 1235551234.to_s(:phone, delimiter: ' ') # => "123 555 1234"
23
- # 1235551234.to_s(:phone, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
24
- # 1235551234.to_s(:phone, country_code: 1) # => "+1-123-555-1234"
25
- # 1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
21
+ # 5551234.to_fs(:phone) # => "555-1234"
22
+ # 1235551234.to_fs(:phone) # => "123-555-1234"
23
+ # 1235551234.to_fs(:phone, area_code: true) # => "(123) 555-1234"
24
+ # 1235551234.to_fs(:phone, delimiter: ' ') # => "123 555 1234"
25
+ # 1235551234.to_fs(:phone, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
26
+ # 1235551234.to_fs(:phone, country_code: 1) # => "+1-123-555-1234"
27
+ # 1235551234.to_fs(:phone, country_code: 1, extension: 1343, delimiter: '.')
26
28
  # # => "+1.123.555.1234 x 1343"
27
29
  #
28
30
  # Currency:
29
- # 1234567890.50.to_s(:currency) # => "$1,234,567,890.50"
30
- # 1234567890.506.to_s(:currency) # => "$1,234,567,890.51"
31
- # 1234567890.506.to_s(:currency, precision: 3) # => "$1,234,567,890.506"
32
- # 1234567890.506.to_s(:currency, round_mode: :down) # => "$1,234,567,890.50"
33
- # 1234567890.506.to_s(:currency, locale: :fr) # => "1 234 567 890,51 €"
34
- # -1234567890.50.to_s(:currency, negative_format: '(%u%n)')
31
+ # 1234567890.50.to_fs(:currency) # => "$1,234,567,890.50"
32
+ # 1234567890.506.to_fs(:currency) # => "$1,234,567,890.51"
33
+ # 1234567890.506.to_fs(:currency, precision: 3) # => "$1,234,567,890.506"
34
+ # 1234567890.506.to_fs(:currency, round_mode: :down) # => "$1,234,567,890.50"
35
+ # 1234567890.506.to_fs(:currency, locale: :fr) # => "1 234 567 890,51 €"
36
+ # -1234567890.50.to_fs(:currency, negative_format: '(%u%n)')
35
37
  # # => "($1,234,567,890.50)"
36
- # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '')
38
+ # 1234567890.50.to_fs(:currency, unit: '&pound;', separator: ',', delimiter: '')
37
39
  # # => "&pound;1234567890,50"
38
- # 1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
40
+ # 1234567890.50.to_fs(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
39
41
  # # => "1234567890,50 &pound;"
40
42
  #
41
43
  # Percentage:
42
- # 100.to_s(:percentage) # => "100.000%"
43
- # 100.to_s(:percentage, precision: 0) # => "100%"
44
- # 1000.to_s(:percentage, delimiter: '.', separator: ',') # => "1.000,000%"
45
- # 302.24398923423.to_s(:percentage, precision: 5) # => "302.24399%"
46
- # 302.24398923423.to_s(:percentage, round_mode: :down) # => "302.243%"
47
- # 1000.to_s(:percentage, locale: :fr) # => "1 000,000%"
48
- # 100.to_s(:percentage, format: '%n %') # => "100.000 %"
44
+ # 100.to_fs(:percentage) # => "100.000%"
45
+ # 100.to_fs(:percentage, precision: 0) # => "100%"
46
+ # 1000.to_fs(:percentage, delimiter: '.', separator: ',') # => "1.000,000%"
47
+ # 302.24398923423.to_fs(:percentage, precision: 5) # => "302.24399%"
48
+ # 302.24398923423.to_fs(:percentage, round_mode: :down) # => "302.243%"
49
+ # 1000.to_fs(:percentage, locale: :fr) # => "1 000,000%"
50
+ # 100.to_fs(:percentage, format: '%n %') # => "100.000 %"
49
51
  #
50
52
  # Delimited:
51
- # 12345678.to_s(:delimited) # => "12,345,678"
52
- # 12345678.05.to_s(:delimited) # => "12,345,678.05"
53
- # 12345678.to_s(:delimited, delimiter: '.') # => "12.345.678"
54
- # 12345678.to_s(:delimited, delimiter: ',') # => "12,345,678"
55
- # 12345678.05.to_s(:delimited, separator: ' ') # => "12,345,678 05"
56
- # 12345678.05.to_s(:delimited, locale: :fr) # => "12 345 678,05"
57
- # 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
53
+ # 12345678.to_fs(:delimited) # => "12,345,678"
54
+ # 12345678.05.to_fs(:delimited) # => "12,345,678.05"
55
+ # 12345678.to_fs(:delimited, delimiter: '.') # => "12.345.678"
56
+ # 12345678.to_fs(:delimited, delimiter: ',') # => "12,345,678"
57
+ # 12345678.05.to_fs(:delimited, separator: ' ') # => "12,345,678 05"
58
+ # 12345678.05.to_fs(:delimited, locale: :fr) # => "12 345 678,05"
59
+ # 98765432.98.to_fs(:delimited, delimiter: ' ', separator: ',')
58
60
  # # => "98 765 432,98"
59
61
  #
60
62
  # Rounded:
61
- # 111.2345.to_s(:rounded) # => "111.235"
62
- # 111.2345.to_s(:rounded, precision: 2) # => "111.23"
63
- # 111.2345.to_s(:rounded, precision: 2, round_mode: :up) # => "111.24"
64
- # 13.to_s(:rounded, precision: 5) # => "13.00000"
65
- # 389.32314.to_s(:rounded, precision: 0) # => "389"
66
- # 111.2345.to_s(:rounded, significant: true) # => "111"
67
- # 111.2345.to_s(:rounded, precision: 1, significant: true) # => "100"
68
- # 13.to_s(:rounded, precision: 5, significant: true) # => "13.000"
69
- # 111.234.to_s(:rounded, locale: :fr) # => "111,234"
70
- # 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
63
+ # 111.2345.to_fs(:rounded) # => "111.235"
64
+ # 111.2345.to_fs(:rounded, precision: 2) # => "111.23"
65
+ # 111.2345.to_fs(:rounded, precision: 2, round_mode: :up) # => "111.24"
66
+ # 13.to_fs(:rounded, precision: 5) # => "13.00000"
67
+ # 389.32314.to_fs(:rounded, precision: 0) # => "389"
68
+ # 111.2345.to_fs(:rounded, significant: true) # => "111"
69
+ # 111.2345.to_fs(:rounded, precision: 1, significant: true) # => "100"
70
+ # 13.to_fs(:rounded, precision: 5, significant: true) # => "13.000"
71
+ # 111.234.to_fs(:rounded, locale: :fr) # => "111,234"
72
+ # 13.to_fs(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
71
73
  # # => "13"
72
- # 389.32314.to_s(:rounded, precision: 4, significant: true) # => "389.3"
73
- # 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
74
+ # 389.32314.to_fs(:rounded, precision: 4, significant: true) # => "389.3"
75
+ # 1111.2345.to_fs(:rounded, precision: 2, separator: ',', delimiter: '.')
74
76
  # # => "1.111,23"
75
77
  #
76
78
  # Human-friendly size in Bytes:
77
- # 123.to_s(:human_size) # => "123 Bytes"
78
- # 1234.to_s(:human_size) # => "1.21 KB"
79
- # 12345.to_s(:human_size) # => "12.1 KB"
80
- # 1234567.to_s(:human_size) # => "1.18 MB"
81
- # 1234567890.to_s(:human_size) # => "1.15 GB"
82
- # 1234567890123.to_s(:human_size) # => "1.12 TB"
83
- # 1234567890123456.to_s(:human_size) # => "1.1 PB"
84
- # 1234567890123456789.to_s(:human_size) # => "1.07 EB"
85
- # 1234567.to_s(:human_size, precision: 2) # => "1.2 MB"
86
- # 1234567.to_s(:human_size, precision: 2, round_mode: :up) # => "1.3 MB"
87
- # 483989.to_s(:human_size, precision: 2) # => "470 KB"
88
- # 1234567.to_s(:human_size, precision: 2, separator: ',') # => "1,2 MB"
89
- # 1234567890123.to_s(:human_size, precision: 5) # => "1.1228 TB"
90
- # 524288000.to_s(:human_size, precision: 5) # => "500 MB"
79
+ # 123.to_fs(:human_size) # => "123 Bytes"
80
+ # 1234.to_fs(:human_size) # => "1.21 KB"
81
+ # 12345.to_fs(:human_size) # => "12.1 KB"
82
+ # 1234567.to_fs(:human_size) # => "1.18 MB"
83
+ # 1234567890.to_fs(:human_size) # => "1.15 GB"
84
+ # 1234567890123.to_fs(:human_size) # => "1.12 TB"
85
+ # 1234567890123456.to_fs(:human_size) # => "1.1 PB"
86
+ # 1234567890123456789.to_fs(:human_size) # => "1.07 EB"
87
+ # 1234567.to_fs(:human_size, precision: 2) # => "1.2 MB"
88
+ # 1234567.to_fs(:human_size, precision: 2, round_mode: :up) # => "1.3 MB"
89
+ # 483989.to_fs(:human_size, precision: 2) # => "470 KB"
90
+ # 1234567.to_fs(:human_size, precision: 2, separator: ',') # => "1,2 MB"
91
+ # 1234567890123.to_fs(:human_size, precision: 5) # => "1.1228 TB"
92
+ # 524288000.to_fs(:human_size, precision: 5) # => "500 MB"
91
93
  #
92
94
  # Human-friendly format:
93
- # 123.to_s(:human) # => "123"
94
- # 1234.to_s(:human) # => "1.23 Thousand"
95
- # 12345.to_s(:human) # => "12.3 Thousand"
96
- # 1234567.to_s(:human) # => "1.23 Million"
97
- # 1234567890.to_s(:human) # => "1.23 Billion"
98
- # 1234567890123.to_s(:human) # => "1.23 Trillion"
99
- # 1234567890123456.to_s(:human) # => "1.23 Quadrillion"
100
- # 1234567890123456789.to_s(:human) # => "1230 Quadrillion"
101
- # 489939.to_s(:human, precision: 2) # => "490 Thousand"
102
- # 489939.to_s(:human, precision: 2, round_mode: :down) # => "480 Thousand"
103
- # 489939.to_s(:human, precision: 4) # => "489.9 Thousand"
104
- # 1234567.to_s(:human, precision: 4,
105
- # significant: false) # => "1.2346 Million"
106
- # 1234567.to_s(:human, precision: 1,
95
+ # 123.to_fs(:human) # => "123"
96
+ # 1234.to_fs(:human) # => "1.23 Thousand"
97
+ # 12345.to_fs(:human) # => "12.3 Thousand"
98
+ # 1234567.to_fs(:human) # => "1.23 Million"
99
+ # 1234567890.to_fs(:human) # => "1.23 Billion"
100
+ # 1234567890123.to_fs(:human) # => "1.23 Trillion"
101
+ # 1234567890123456.to_fs(:human) # => "1.23 Quadrillion"
102
+ # 1234567890123456789.to_fs(:human) # => "1230 Quadrillion"
103
+ # 489939.to_fs(:human, precision: 2) # => "490 Thousand"
104
+ # 489939.to_fs(:human, precision: 2, round_mode: :down) # => "480 Thousand"
105
+ # 489939.to_fs(:human, precision: 4) # => "489.9 Thousand"
106
+ # 1234567.to_fs(:human, precision: 4,
107
+ # significant: false) # => "1.2346 Million"
108
+ # 1234567.to_fs(:human, precision: 1,
107
109
  # separator: ',',
108
- # significant: false) # => "1,2 Million"
109
- def to_s(format = nil, options = nil)
110
+ # significant: false) # => "1,2 Million"
111
+ def to_fs(format = nil, options = nil)
112
+ return to_s if format.nil?
113
+
110
114
  case format
111
- when nil
112
- super()
113
115
  when Integer, String
114
- super(format)
116
+ to_s(format)
115
117
  when :phone
116
118
  ActiveSupport::NumberHelper.number_to_phone(self, options || {})
117
119
  when :currency
@@ -127,11 +129,12 @@ module ActiveSupport
127
129
  when :human_size
128
130
  ActiveSupport::NumberHelper.number_to_human_size(self, options || {})
129
131
  when Symbol
130
- super()
132
+ to_s
131
133
  else
132
- super(format)
134
+ to_s(format)
133
135
  end
134
136
  end
137
+ alias_method :to_formatted_s, :to_fs
135
138
  end
136
139
  end
137
140