activesupport 7.1.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +122 -943
  3. data/lib/active_support/array_inquirer.rb +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +15 -3
  5. data/lib/active_support/broadcast_logger.rb +33 -4
  6. data/lib/active_support/cache/entry.rb +7 -1
  7. data/lib/active_support/cache/file_store.rb +16 -11
  8. data/lib/active_support/cache/mem_cache_store.rb +32 -82
  9. data/lib/active_support/cache/memory_store.rb +17 -14
  10. data/lib/active_support/cache/redis_cache_store.rb +54 -36
  11. data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
  12. data/lib/active_support/cache/strategy/local_cache.rb +9 -6
  13. data/lib/active_support/cache.rb +98 -84
  14. data/lib/active_support/callbacks.rb +74 -113
  15. data/lib/active_support/code_generator.rb +15 -10
  16. data/lib/active_support/core_ext/array/conversions.rb +0 -2
  17. data/lib/active_support/core_ext/class/subclasses.rb +15 -35
  18. data/lib/active_support/core_ext/date/blank.rb +4 -0
  19. data/lib/active_support/core_ext/date/conversions.rb +1 -3
  20. data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
  21. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  22. data/lib/active_support/core_ext/date_time/conversions.rb +0 -4
  23. data/lib/active_support/core_ext/digest/uuid.rb +6 -0
  24. data/lib/active_support/core_ext/erb/util.rb +5 -0
  25. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  26. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  27. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  28. data/lib/active_support/core_ext/module/delegation.rb +20 -148
  29. data/lib/active_support/core_ext/module/deprecation.rb +1 -4
  30. data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
  31. data/lib/active_support/core_ext/object/blank.rb +45 -1
  32. data/lib/active_support/core_ext/object/duplicable.rb +24 -15
  33. data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
  34. data/lib/active_support/core_ext/object/json.rb +2 -2
  35. data/lib/active_support/core_ext/object/with.rb +5 -3
  36. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  37. data/lib/active_support/core_ext/pathname/blank.rb +4 -0
  38. data/lib/active_support/core_ext/range/overlap.rb +1 -1
  39. data/lib/active_support/core_ext/securerandom.rb +8 -24
  40. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  41. data/lib/active_support/core_ext/string/filters.rb +1 -1
  42. data/lib/active_support/core_ext/string/indent.rb +1 -1
  43. data/lib/active_support/core_ext/string/output_safety.rb +0 -7
  44. data/lib/active_support/core_ext/time/calculations.rb +18 -28
  45. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  46. data/lib/active_support/core_ext/time/conversions.rb +0 -2
  47. data/lib/active_support/core_ext.rb +0 -1
  48. data/lib/active_support/current_attributes.rb +33 -40
  49. data/lib/active_support/delegation.rb +202 -0
  50. data/lib/active_support/dependencies/autoload.rb +0 -12
  51. data/lib/active_support/deprecation/behaviors.rb +18 -16
  52. data/lib/active_support/deprecation/constant_accessor.rb +47 -26
  53. data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
  54. data/lib/active_support/deprecation/reporting.rb +13 -5
  55. data/lib/active_support/deprecation.rb +8 -5
  56. data/lib/active_support/descendants_tracker.rb +9 -87
  57. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  58. data/lib/active_support/duration/iso8601_serializer.rb +1 -2
  59. data/lib/active_support/duration.rb +11 -6
  60. data/lib/active_support/error_reporter.rb +41 -3
  61. data/lib/active_support/evented_file_update_checker.rb +0 -1
  62. data/lib/active_support/execution_wrapper.rb +0 -1
  63. data/lib/active_support/file_update_checker.rb +1 -1
  64. data/lib/active_support/fork_tracker.rb +2 -38
  65. data/lib/active_support/gem_version.rb +1 -1
  66. data/lib/active_support/hash_with_indifferent_access.rb +6 -8
  67. data/lib/active_support/html_safe_translation.rb +19 -6
  68. data/lib/active_support/inflector/methods.rb +2 -2
  69. data/lib/active_support/log_subscriber.rb +9 -14
  70. data/lib/active_support/logger.rb +15 -2
  71. data/lib/active_support/logger_thread_safe_level.rb +0 -8
  72. data/lib/active_support/message_encryptors.rb +1 -0
  73. data/lib/active_support/message_pack/extensions.rb +15 -2
  74. data/lib/active_support/message_verifier.rb +12 -0
  75. data/lib/active_support/message_verifiers.rb +1 -0
  76. data/lib/active_support/messages/codec.rb +1 -1
  77. data/lib/active_support/messages/metadata.rb +1 -1
  78. data/lib/active_support/multibyte/chars.rb +2 -2
  79. data/lib/active_support/notifications/fanout.rb +29 -26
  80. data/lib/active_support/notifications/instrumenter.rb +43 -32
  81. data/lib/active_support/notifications.rb +28 -27
  82. data/lib/active_support/number_helper/number_converter.rb +5 -3
  83. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  84. data/lib/active_support/number_helper.rb +379 -318
  85. data/lib/active_support/option_merger.rb +2 -2
  86. data/lib/active_support/ordered_options.rb +55 -17
  87. data/lib/active_support/proxy_object.rb +8 -5
  88. data/lib/active_support/railtie.rb +4 -11
  89. data/lib/active_support/string_inquirer.rb +1 -1
  90. data/lib/active_support/syntax_error_proxy.rb +12 -1
  91. data/lib/active_support/tagged_logging.rb +4 -1
  92. data/lib/active_support/test_case.rb +3 -1
  93. data/lib/active_support/testing/assertions.rb +5 -5
  94. data/lib/active_support/testing/constant_stubbing.rb +30 -8
  95. data/lib/active_support/testing/deprecation.rb +5 -12
  96. data/lib/active_support/testing/isolation.rb +18 -8
  97. data/lib/active_support/testing/method_call_assertions.rb +2 -16
  98. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  99. data/lib/active_support/testing/strict_warnings.rb +6 -4
  100. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  101. data/lib/active_support/testing/time_helpers.rb +8 -4
  102. data/lib/active_support/time_with_zone.rb +7 -3
  103. data/lib/active_support/values/time_zone.rb +10 -1
  104. data/lib/active_support/xml_mini.rb +11 -2
  105. data/lib/active_support.rb +3 -2
  106. metadata +35 -15
  107. data/lib/active_support/deprecation/instance_delegator.rb +0 -65
  108. data/lib/active_support/ruby_features.rb +0 -7
@@ -126,6 +126,7 @@ module ActiveSupport
126
126
  # indicate whether old option sets are still in use or can be removed from
127
127
  # rotation.
128
128
 
129
+ ##
129
130
  private
130
131
  def build(salt, secret_generator:, secret_generator_options:, **options)
131
132
  MessageVerifier.new(secret_generator.call(salt, **secret_generator_options), **options)
@@ -28,7 +28,7 @@ module ActiveSupport
28
28
 
29
29
  def decode(encoded, url_safe: @url_safe)
30
30
  url_safe ? ::Base64.urlsafe_decode64(encoded) : ::Base64.strict_decode64(encoded)
31
- rescue ArgumentError => error
31
+ rescue StandardError => error
32
32
  throw :invalid_message_format, error
33
33
  end
34
34
 
@@ -82,7 +82,7 @@ module ActiveSupport
82
82
  throw :invalid_message_content, "expired"
83
83
  end
84
84
 
85
- if hash["pur"] != purpose&.to_s
85
+ if hash["pur"].to_s != purpose.to_s
86
86
  throw :invalid_message_content, "mismatched purpose"
87
87
  end
88
88
 
@@ -59,8 +59,8 @@ module ActiveSupport # :nodoc:
59
59
  end
60
60
 
61
61
  # Forward all undefined methods to the wrapped string.
62
- def method_missing(method, *args, &block)
63
- result = @wrapped_string.__send__(method, *args, &block)
62
+ def method_missing(method, ...)
63
+ result = @wrapped_string.__send__(method, ...)
64
64
  if method.end_with?("!")
65
65
  self if result
66
66
  else
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "mutex_m"
4
3
  require "concurrent/map"
5
4
  require "set"
6
5
  require "active_support/core_ext/object/try"
@@ -18,26 +17,30 @@ module ActiveSupport
18
17
  end
19
18
 
20
19
  module FanoutIteration # :nodoc:
21
- def iterate_guarding_exceptions(listeners)
22
- exceptions = nil
23
-
24
- listeners.each do |s|
25
- yield s
26
- rescue Exception => e
27
- exceptions ||= []
28
- exceptions << e
29
- end
20
+ private
21
+ def iterate_guarding_exceptions(collection)
22
+ exceptions = nil
30
23
 
31
- if exceptions
32
- if exceptions.size == 1
33
- raise exceptions.first
34
- else
35
- raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
24
+ collection.each do |s|
25
+ yield s
26
+ rescue Exception => e
27
+ exceptions ||= []
28
+ exceptions << e
36
29
  end
37
- end
38
30
 
39
- listeners
40
- end
31
+ if exceptions
32
+ exceptions = exceptions.flat_map do |exception|
33
+ exception.is_a?(InstrumentationSubscriberError) ? exception.exceptions : [exception]
34
+ end
35
+ if exceptions.size == 1
36
+ raise exceptions.first
37
+ else
38
+ raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
39
+ end
40
+ end
41
+
42
+ collection
43
+ end
41
44
  end
42
45
 
43
46
  # This is a default queue implementation that ships with Notifications.
@@ -45,15 +48,13 @@ module ActiveSupport
45
48
  #
46
49
  # This class is thread safe. All methods are reentrant.
47
50
  class Fanout
48
- include Mutex_m
49
-
50
51
  def initialize
52
+ @mutex = Mutex.new
51
53
  @string_subscribers = Concurrent::Map.new { |h, k| h.compute_if_absent(k) { [] } }
52
54
  @other_subscribers = []
53
55
  @all_listeners_for = Concurrent::Map.new
54
56
  @groups_for = Concurrent::Map.new
55
57
  @silenceable_groups_for = Concurrent::Map.new
56
- super
57
58
  end
58
59
 
59
60
  def inspect # :nodoc:
@@ -63,7 +64,7 @@ module ActiveSupport
63
64
 
64
65
  def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
65
66
  subscriber = Subscribers.new(pattern, callable || block, monotonic)
66
- synchronize do
67
+ @mutex.synchronize do
67
68
  case pattern
68
69
  when String
69
70
  @string_subscribers[pattern] << subscriber
@@ -79,7 +80,7 @@ module ActiveSupport
79
80
  end
80
81
 
81
82
  def unsubscribe(subscriber_or_name)
82
- synchronize do
83
+ @mutex.synchronize do
83
84
  case subscriber_or_name
84
85
  when String
85
86
  @string_subscribers[subscriber_or_name].clear
@@ -225,6 +226,8 @@ module ActiveSupport
225
226
  # handle.finish
226
227
  # end
227
228
  class Handle
229
+ include FanoutIteration
230
+
228
231
  def initialize(notifier, name, id, payload) # :nodoc:
229
232
  @name = name
230
233
  @id = id
@@ -239,7 +242,7 @@ module ActiveSupport
239
242
  ensure_state! :initialized
240
243
  @state = :started
241
244
 
242
- @groups.each do |group|
245
+ iterate_guarding_exceptions(@groups) do |group|
243
246
  group.start(@name, @id, @payload)
244
247
  end
245
248
  end
@@ -252,7 +255,7 @@ module ActiveSupport
252
255
  ensure_state! :started
253
256
  @state = :finished
254
257
 
255
- @groups.each do |group|
258
+ iterate_guarding_exceptions(@groups) do |group|
256
259
  group.finish(name, id, payload)
257
260
  end
258
261
  end
@@ -294,7 +297,7 @@ module ActiveSupport
294
297
 
295
298
  def all_listeners_for(name)
296
299
  # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
297
- @all_listeners_for[name] || synchronize do
300
+ @all_listeners_for[name] || @mutex.synchronize do
298
301
  # use synchronisation when accessing @subscribers
299
302
  @all_listeners_for[name] ||=
300
303
  @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
@@ -65,16 +65,16 @@ module ActiveSupport
65
65
  end
66
66
  end
67
67
 
68
- # Returns a "handle" for an event with the given +name+ and +payload+
68
+ # Returns a "handle" for an event with the given +name+ and +payload+.
69
69
  #
70
- # +#start+ and +#finish+ must each be called exactly once on the returned object.
70
+ # #start and #finish must each be called exactly once on the returned object.
71
71
  #
72
- # Where possible, it's best to use +#instrument+, which will record the
72
+ # Where possible, it's best to use #instrument, which will record the
73
73
  # start and finish of the event and correctly handle any exceptions.
74
74
  # +build_handle+ is a low-level API intended for cases where using
75
- # +#instrument+ isn't possible.
75
+ # +instrument+ isn't possible.
76
76
  #
77
- # See ActiveSupport::Notifications::Fanout::Handle
77
+ # See ActiveSupport::Notifications::Fanout::Handle.
78
78
  def build_handle(name, payload)
79
79
  @notifier.build_handle(name, @id, payload)
80
80
  end
@@ -104,7 +104,7 @@ module ActiveSupport
104
104
  end
105
105
 
106
106
  class Event
107
- attr_reader :name, :time, :end, :transaction_id
107
+ attr_reader :name, :transaction_id
108
108
  attr_accessor :payload
109
109
 
110
110
  def initialize(name, start, ending, transaction_id, payload)
@@ -117,9 +117,19 @@ module ActiveSupport
117
117
  @cpu_time_finish = 0.0
118
118
  @allocation_count_start = 0
119
119
  @allocation_count_finish = 0
120
+ @gc_time_start = 0
121
+ @gc_time_finish = 0
120
122
  end
121
123
 
122
- def record
124
+ def time
125
+ @time / 1000.0 if @time
126
+ end
127
+
128
+ def end
129
+ @end / 1000.0 if @end
130
+ end
131
+
132
+ def record # :nodoc:
123
133
  start!
124
134
  begin
125
135
  yield payload if block_given?
@@ -136,57 +146,48 @@ module ActiveSupport
136
146
  def start!
137
147
  @time = now
138
148
  @cpu_time_start = now_cpu
149
+ @gc_time_start = now_gc
139
150
  @allocation_count_start = now_allocations
140
151
  end
141
152
 
142
153
  # Record information at the time this event finishes
143
154
  def finish!
144
155
  @cpu_time_finish = now_cpu
156
+ @gc_time_finish = now_gc
145
157
  @end = now
146
158
  @allocation_count_finish = now_allocations
147
159
  end
148
160
 
149
- # Returns the CPU time (in milliseconds) passed since the call to
150
- # +start!+ and the call to +finish!+
161
+ # Returns the CPU time (in milliseconds) passed between the call to
162
+ # #start! and the call to #finish!.
151
163
  def cpu_time
152
164
  @cpu_time_finish - @cpu_time_start
153
165
  end
154
166
 
155
- # Returns the idle time time (in milliseconds) passed since the call to
156
- # +start!+ and the call to +finish!+
167
+ # Returns the idle time time (in milliseconds) passed between the call to
168
+ # #start! and the call to #finish!.
157
169
  def idle_time
158
170
  diff = duration - cpu_time
159
171
  diff > 0.0 ? diff : 0.0
160
172
  end
161
173
 
162
- # Returns the number of allocations made since the call to +start!+ and
163
- # the call to +finish!+
174
+ # Returns the number of allocations made between the call to #start! and
175
+ # the call to #finish!.
164
176
  def allocations
165
177
  @allocation_count_finish - @allocation_count_start
166
178
  end
167
179
 
168
- def children # :nodoc:
169
- ActiveSupport.deprecator.warn <<~EOM
170
- ActiveSupport::Notifications::Event#children is deprecated and will
171
- be removed in Rails 7.2.
172
- EOM
173
- []
174
- end
175
-
176
- def parent_of?(event) # :nodoc:
177
- ActiveSupport.deprecator.warn <<~EOM
178
- ActiveSupport::Notifications::Event#parent_of? is deprecated and will
179
- be removed in Rails 7.2.
180
- EOM
181
- start = (time - event.time) * 1000
182
- start <= 0 && (start + duration >= event.duration)
180
+ # Returns the time spent in GC (in milliseconds) between the call to #start!
181
+ # and the call to #finish!
182
+ def gc_time
183
+ (@gc_time_finish - @gc_time_start) / 1_000_000.0
183
184
  end
184
185
 
185
186
  # Returns the difference in milliseconds between when the execution of the
186
187
  # event started and when it ended.
187
188
  #
188
- # ActiveSupport::Notifications.subscribe('wait') do |*args|
189
- # @event = ActiveSupport::Notifications::Event.new(*args)
189
+ # ActiveSupport::Notifications.subscribe('wait') do |event|
190
+ # @event = event
190
191
  # end
191
192
  #
192
193
  # ActiveSupport::Notifications.instrument('wait') do
@@ -195,7 +196,7 @@ module ActiveSupport
195
196
  #
196
197
  # @event.duration # => 1000.138
197
198
  def duration
198
- self.end - time
199
+ @end - @time
199
200
  end
200
201
 
201
202
  private
@@ -210,11 +211,21 @@ module ActiveSupport
210
211
  Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
211
212
  end
212
213
  rescue
213
- def now_cpu # rubocop:disable Lint/DuplicateMethods
214
+ def now_cpu
214
215
  0.0
215
216
  end
216
217
  end
217
218
 
219
+ if GC.respond_to?(:total_time)
220
+ def now_gc
221
+ GC.total_time
222
+ end
223
+ else
224
+ def now_gc
225
+ 0
226
+ end
227
+ end
228
+
218
229
  if GC.stat.key?(:total_allocated_objects)
219
230
  def now_allocations
220
231
  GC.stat(:total_allocated_objects)
@@ -29,6 +29,16 @@ module ActiveSupport
29
29
  # You can consume those events and the information they provide by registering
30
30
  # a subscriber.
31
31
  #
32
+ # ActiveSupport::Notifications.subscribe('render') do |event|
33
+ # event.name # => "render"
34
+ # event.duration # => 10 (in milliseconds)
35
+ # event.payload # => { extra: :information }
36
+ # event.allocations # => 1826 (objects)
37
+ # end
38
+ #
39
+ # +Event+ objects record CPU time and allocations. If you don't need this
40
+ # it's also possible to pass a block that accepts five arguments:
41
+ #
32
42
  # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
33
43
  # name # => String, name of the event (such as 'render' from above)
34
44
  # start # => Time, when the instrumented block started execution
@@ -42,20 +52,18 @@ module ActiveSupport
42
52
  #
43
53
  # ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
44
54
  # name # => String, name of the event (such as 'render' from above)
45
- # start # => Monotonic time, when the instrumented block started execution
46
- # finish # => Monotonic time, when the instrumented block ended execution
55
+ # start # => Float, monotonic time when the instrumented block started execution
56
+ # finish # => Float, monotonic time when the instrumented block ended execution
47
57
  # id # => String, unique ID for the instrumenter that fired the event
48
58
  # payload # => Hash, the payload
49
59
  # end
50
60
  #
51
- # The +start+ and +finish+ values above represent monotonic time.
52
- #
53
61
  # For instance, let's store all "render" events in an array:
54
62
  #
55
63
  # events = []
56
64
  #
57
- # ActiveSupport::Notifications.subscribe('render') do |*args|
58
- # events << ActiveSupport::Notifications::Event.new(*args)
65
+ # ActiveSupport::Notifications.subscribe('render') do |event|
66
+ # events << event
59
67
  # end
60
68
  #
61
69
  # That code returns right away, you are just subscribing to "render" events.
@@ -66,14 +74,10 @@ module ActiveSupport
66
74
  # end
67
75
  #
68
76
  # event = events.first
69
- # event.name # => "render"
70
- # event.duration # => 10 (in milliseconds)
71
- # event.payload # => { extra: :information }
72
- #
73
- # The block in the <tt>subscribe</tt> call gets the name of the event, start
74
- # timestamp, end timestamp, a string with a unique identifier for that event's instrumenter
75
- # (something like "535801666f04d0298cd6"), and a hash with the payload, in
76
- # that order.
77
+ # event.name # => "render"
78
+ # event.duration # => 10 (in milliseconds)
79
+ # event.payload # => { extra: :information }
80
+ # event.allocations # => 1826 (objects)
77
81
  #
78
82
  # If an exception happens during that particular instrumentation the payload will
79
83
  # have a key <tt>:exception</tt> with an array of two elements as value: a string with
@@ -138,7 +142,7 @@ module ActiveSupport
138
142
  # You can subscribe to some event temporarily while some block runs. For
139
143
  # example, in
140
144
  #
141
- # callback = lambda {|*args| ... }
145
+ # callback = lambda {|event| ... }
142
146
  # ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
143
147
  # ...
144
148
  # end
@@ -161,7 +165,7 @@ module ActiveSupport
161
165
  #
162
166
  # The +subscribe+ method returns a subscriber object:
163
167
  #
164
- # subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
168
+ # subscriber = ActiveSupport::Notifications.subscribe("render") do |event|
165
169
  # ...
166
170
  # end
167
171
  #
@@ -214,11 +218,15 @@ module ActiveSupport
214
218
  # You can subscribe to events by passing a String to match exact event
215
219
  # names, or by passing a Regexp to match all events that match a pattern.
216
220
  #
217
- # ActiveSupport::Notifications.subscribe(/render/) do |*args|
218
- # @event = ActiveSupport::Notifications::Event.new(*args)
221
+ # If the block passed to the method only takes one argument,
222
+ # it will yield an +Event+ object to the block:
223
+ #
224
+ # ActiveSupport::Notifications.subscribe(/render/) do |event|
225
+ # @event = event
219
226
  # end
220
227
  #
221
- # The +block+ will receive five parameters with information about the event:
228
+ # Otherwise the +block+ will receive five arguments with information
229
+ # about the event:
222
230
  #
223
231
  # ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
224
232
  # name # => String, name of the event (such as 'render' from above)
@@ -228,16 +236,9 @@ module ActiveSupport
228
236
  # payload # => Hash, the payload
229
237
  # end
230
238
  #
231
- # If the block passed to the method only takes one parameter,
232
- # it will yield an event object to the block:
233
- #
234
- # ActiveSupport::Notifications.subscribe(/render/) do |event|
235
- # @event = event
236
- # end
237
- #
238
239
  # Raises an error if invalid event name type is passed:
239
240
  #
240
- # ActiveSupport::Notifications.subscribe(:render) {|*args| ...}
241
+ # ActiveSupport::Notifications.subscribe(:render) {|event| ...}
241
242
  # #=> ArgumentError (pattern must be specified as a String, Regexp or empty)
242
243
  #
243
244
  def subscribe(pattern = nil, callback = nil, &block)
@@ -164,11 +164,11 @@ module ActiveSupport
164
164
  end
165
165
 
166
166
  def translate_number_value_with_default(key, **i18n_options)
167
- I18n.translate(key, **{ default: default_value(key), scope: :number }.merge!(i18n_options))
167
+ I18n.translate(key, default: default_value(key), scope: :number, **i18n_options)
168
168
  end
169
169
 
170
170
  def translate_in_locale(key, **i18n_options)
171
- translate_number_value_with_default(key, **{ locale: options[:locale] }.merge(i18n_options))
171
+ translate_number_value_with_default(key, locale: options[:locale], **i18n_options)
172
172
  end
173
173
 
174
174
  def default_value(key)
@@ -179,8 +179,10 @@ module ActiveSupport
179
179
  case number
180
180
  when Float, Rational
181
181
  number.to_d(0)
182
- else
182
+ when String
183
183
  BigDecimal(number, exception: false)
184
+ else
185
+ number.to_d rescue nil
184
186
  end
185
187
  end
186
188
  end
@@ -43,13 +43,13 @@ module ActiveSupport
43
43
 
44
44
  def exponent
45
45
  max = STORAGE_UNITS.size - 1
46
- exp = (Math.log(number) / Math.log(base)).to_i
46
+ exp = (Math.log(number.abs) / Math.log(base)).to_i
47
47
  exp = max if exp > max # avoid overflow for the highest unit
48
48
  exp
49
49
  end
50
50
 
51
51
  def smaller_than_base?
52
- number.to_i < base
52
+ number.to_i.abs < base
53
53
  end
54
54
 
55
55
  def base