activesupport 7.1.1 → 7.1.5

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +201 -0
  3. data/lib/active_support/backtrace_cleaner.rb +5 -0
  4. data/lib/active_support/broadcast_logger.rb +23 -14
  5. data/lib/active_support/cache/entry.rb +7 -1
  6. data/lib/active_support/cache/file_store.rb +1 -1
  7. data/lib/active_support/cache/mem_cache_store.rb +16 -8
  8. data/lib/active_support/cache/memory_store.rb +4 -4
  9. data/lib/active_support/cache/redis_cache_store.rb +21 -14
  10. data/lib/active_support/cache/strategy/local_cache.rb +9 -6
  11. data/lib/active_support/cache.rb +36 -8
  12. data/lib/active_support/code_generator.rb +15 -10
  13. data/lib/active_support/core_ext/date/conversions.rb +1 -1
  14. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  15. data/lib/active_support/core_ext/module/delegation.rb +41 -26
  16. data/lib/active_support/core_ext/object/duplicable.rb +24 -15
  17. data/lib/active_support/core_ext/object/json.rb +5 -3
  18. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  19. data/lib/active_support/core_ext/string/indent.rb +1 -1
  20. data/lib/active_support/deprecation/behaviors.rb +18 -16
  21. data/lib/active_support/deprecation/reporting.rb +8 -5
  22. data/lib/active_support/gem_version.rb +1 -1
  23. data/lib/active_support/html_safe_translation.rb +16 -6
  24. data/lib/active_support/inflector/methods.rb +2 -2
  25. data/lib/active_support/log_subscriber.rb +9 -2
  26. data/lib/active_support/messages/codec.rb +1 -1
  27. data/lib/active_support/messages/metadata.rb +1 -1
  28. data/lib/active_support/notifications/fanout.rb +25 -19
  29. data/lib/active_support/notifications/instrumenter.rb +11 -3
  30. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  31. data/lib/active_support/number_helper.rb +379 -318
  32. data/lib/active_support/ordered_options.rb +2 -2
  33. data/lib/active_support/railtie.rb +3 -3
  34. data/lib/active_support/syntax_error_proxy.rb +12 -1
  35. data/lib/active_support/tagged_logging.rb +4 -0
  36. data/lib/active_support/testing/assertions.rb +1 -1
  37. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  38. data/lib/active_support/testing/strict_warnings.rb +1 -0
  39. data/lib/active_support/testing/time_helpers.rb +5 -1
  40. data/lib/active_support/values/time_zone.rb +9 -0
  41. data/lib/active_support.rb +1 -1
  42. metadata +51 -9
@@ -9,16 +9,19 @@ module ActiveSupport
9
9
  @cache = METHOD_CACHES[namespace]
10
10
  @sources = []
11
11
  @methods = {}
12
+ @canonical_methods = {}
12
13
  end
13
14
 
14
- def define_cached_method(name, as: name)
15
- name = name.to_sym
16
- as = as.to_sym
17
- @methods.fetch(name) do
18
- unless @cache.method_defined?(as)
15
+ def define_cached_method(canonical_name, as: nil)
16
+ canonical_name = canonical_name.to_sym
17
+ as = (as || canonical_name).to_sym
18
+
19
+ @methods.fetch(as) do
20
+ unless @cache.method_defined?(canonical_name) || @canonical_methods[canonical_name]
19
21
  yield @sources
20
22
  end
21
- @methods[name] = as
23
+ @canonical_methods[canonical_name] = true
24
+ @methods[as] = canonical_name
22
25
  end
23
26
  end
24
27
 
@@ -26,8 +29,10 @@ module ActiveSupport
26
29
  unless @sources.empty?
27
30
  @cache.module_eval("# frozen_string_literal: true\n" + @sources.join(";"), path, line)
28
31
  end
29
- @methods.each do |name, as|
30
- owner.define_method(name, @cache.instance_method(as))
32
+ @canonical_methods.clear
33
+
34
+ @methods.each do |as, canonical_name|
35
+ owner.define_method(as, @cache.instance_method(canonical_name))
31
36
  end
32
37
  end
33
38
  end
@@ -52,8 +57,8 @@ module ActiveSupport
52
57
  @namespaces = Hash.new { |h, k| h[k] = MethodSet.new(k) }
53
58
  end
54
59
 
55
- def define_cached_method(name, namespace:, as: name, &block)
56
- @namespaces[namespace].define_cached_method(name, as: as, &block)
60
+ def define_cached_method(canonical_name, namespace:, as: nil, &block)
61
+ @namespaces[namespace].define_cached_method(canonical_name, as: as, &block)
57
62
  end
58
63
 
59
64
  def execute
@@ -52,7 +52,7 @@ class Date
52
52
  strftime(formatter)
53
53
  end
54
54
  else
55
- to_default_s
55
+ to_s
56
56
  end
57
57
  end
58
58
  alias_method :to_formatted_s, :to_fs
@@ -3,7 +3,7 @@
3
3
  require "active_support/concern"
4
4
 
5
5
  class Module
6
- # = Bite-sized separation of concerns
6
+ # == Bite-sized separation of concerns
7
7
  #
8
8
  # We often find ourselves with a medium-sized chunk of behavior that we'd
9
9
  # like to extract, but only mix in to a single class.
@@ -18,9 +18,9 @@ class Module
18
18
  # with a comment, as a least-bad alternative. Using modules in separate files
19
19
  # means tedious sifting to get a big-picture view.
20
20
  #
21
- # = Dissatisfying ways to separate small concerns
21
+ # == Dissatisfying ways to separate small concerns
22
22
  #
23
- # == Using comments:
23
+ # === Using comments:
24
24
  #
25
25
  # class Todo < ApplicationRecord
26
26
  # # Other todo implementation
@@ -37,7 +37,7 @@ class Module
37
37
  # end
38
38
  # end
39
39
  #
40
- # == With an inline module:
40
+ # === With an inline module:
41
41
  #
42
42
  # Noisy syntax.
43
43
  #
@@ -61,7 +61,7 @@ class Module
61
61
  # include EventTracking
62
62
  # end
63
63
  #
64
- # == Mix-in noise exiled to its own file:
64
+ # === Mix-in noise exiled to its own file:
65
65
  #
66
66
  # Once our chunk of behavior starts pushing the scroll-to-understand-it
67
67
  # boundary, we give in and move it to a separate file. At this size, the
@@ -75,7 +75,7 @@ class Module
75
75
  # include TodoEventTracking
76
76
  # end
77
77
  #
78
- # = Introducing Module#concerning
78
+ # == Introducing Module#concerning
79
79
  #
80
80
  # By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
81
81
  # separate bite-sized concerns.
@@ -317,37 +317,52 @@ class Module
317
317
  # of <tt>object</tt> add or remove instance variables.
318
318
  def delegate_missing_to(target, allow_nil: nil)
319
319
  target = target.to_s
320
- target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
320
+ target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target) || target == "__target"
321
321
 
322
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
323
- def respond_to_missing?(name, include_private = false)
324
- # It may look like an oversight, but we deliberately do not pass
325
- # +include_private+, because they do not get delegated.
322
+ if allow_nil
323
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
324
+ def respond_to_missing?(name, include_private = false)
325
+ # It may look like an oversight, but we deliberately do not pass
326
+ # +include_private+, because they do not get delegated.
326
327
 
327
- return false if name == :marshal_dump || name == :_dump
328
- #{target}.respond_to?(name) || super
329
- end
328
+ return false if name == :marshal_dump || name == :_dump
329
+ #{target}.respond_to?(name) || super
330
+ end
330
331
 
331
- def method_missing(method, *args, &block)
332
- if #{target}.respond_to?(method)
333
- #{target}.public_send(method, *args, &block)
334
- else
335
- begin
332
+ def method_missing(method, *args, &block)
333
+ __target = #{target}
334
+ if __target.nil? && !nil.respond_to?(method)
335
+ nil
336
+ elsif __target.respond_to?(method)
337
+ __target.public_send(method, *args, &block)
338
+ else
336
339
  super
337
- rescue NoMethodError
338
- if #{target}.nil?
339
- if #{allow_nil == true}
340
- nil
341
- else
342
- raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
343
- end
344
- else
345
- raise
346
- end
347
340
  end
348
341
  end
349
- end
350
- ruby2_keywords(:method_missing)
351
- RUBY
342
+ ruby2_keywords(:method_missing)
343
+ RUBY
344
+ else
345
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
346
+ def respond_to_missing?(name, include_private = false)
347
+ # It may look like an oversight, but we deliberately do not pass
348
+ # +include_private+, because they do not get delegated.
349
+
350
+ return false if name == :marshal_dump || name == :_dump
351
+ #{target}.respond_to?(name) || super
352
+ end
353
+
354
+ def method_missing(method, *args, &block)
355
+ __target = #{target}
356
+ if __target.nil? && !nil.respond_to?(method)
357
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
358
+ elsif __target.respond_to?(method)
359
+ __target.public_send(method, *args, &block)
360
+ else
361
+ super
362
+ end
363
+ end
364
+ ruby2_keywords(:method_missing)
365
+ RUBY
366
+ end
352
367
  end
353
368
  end
@@ -28,23 +28,32 @@ class Object
28
28
  end
29
29
  end
30
30
 
31
- class Method
32
- # Methods are not duplicable:
33
- #
34
- # method(:puts).duplicable? # => false
35
- # method(:puts).dup # => TypeError: allocator undefined for Method
36
- def duplicable?
37
- false
38
- end
31
+ methods_are_duplicable = begin
32
+ Object.instance_method(:duplicable?).dup
33
+ true
34
+ rescue TypeError
35
+ false
39
36
  end
40
37
 
41
- class UnboundMethod
42
- # Unbound methods are not duplicable:
43
- #
44
- # method(:puts).unbind.duplicable? # => false
45
- # method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
46
- def duplicable?
47
- false
38
+ unless methods_are_duplicable
39
+ class Method
40
+ # Methods are not duplicable:
41
+ #
42
+ # method(:puts).duplicable? # => false
43
+ # method(:puts).dup # => TypeError: allocator undefined for Method
44
+ def duplicable?
45
+ false
46
+ end
47
+ end
48
+
49
+ class UnboundMethod
50
+ # Unbound methods are not duplicable:
51
+ #
52
+ # method(:puts).unbind.duplicable? # => false
53
+ # method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
54
+ def duplicable?
55
+ false
56
+ end
48
57
  end
49
58
  end
50
59
 
@@ -233,9 +233,11 @@ class Pathname # :nodoc:
233
233
  end
234
234
  end
235
235
 
236
- class IPAddr # :nodoc:
237
- def as_json(options = nil)
238
- to_s
236
+ unless IPAddr.method_defined?(:as_json, false)
237
+ class IPAddr # :nodoc:
238
+ def as_json(options = nil)
239
+ to_s
240
+ end
239
241
  end
240
242
  end
241
243
 
@@ -68,7 +68,7 @@ class Object
68
68
  # You can access these methods using the class name instead:
69
69
  #
70
70
  # class Phone < ActiveRecord::Base
71
- # enum phone_number_type: { home: 0, office: 1, mobile: 2 }
71
+ # enum :phone_number_type, { home: 0, office: 1, mobile: 2 }
72
72
  #
73
73
  # with_options presence: true do
74
74
  # validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
@@ -24,7 +24,7 @@ class String
24
24
  #
25
25
  # The second argument, +indent_string+, specifies which indent string to
26
26
  # use. The default is +nil+, which tells the method to make a guess by
27
- # peeking at the first indented line, and fallback to a space if there is
27
+ # peeking at the first indented line, and fall back to a space if there is
28
28
  # none.
29
29
  #
30
30
  # " foo".indent(2) # => " foo"
@@ -57,15 +57,15 @@ module ActiveSupport
57
57
  # You can create a custom behavior or set any from the +DEFAULT_BEHAVIORS+
58
58
  # constant. Available behaviors are:
59
59
  #
60
- # [+raise+] Raise ActiveSupport::DeprecationException.
61
- # [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
62
- # [+log+] Log all deprecation warnings to +Rails.logger+.
63
- # [+notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
64
- # [+report+] Use ActiveSupport::ErrorReporter to report deprecations.
65
- # [+silence+] Do nothing. On \Rails, set <tt>config.active_support.report_deprecations = false</tt> to disable all behaviors.
60
+ # [+:raise+] Raise ActiveSupport::DeprecationException.
61
+ # [+:stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
62
+ # [+:log+] Log all deprecation warnings to +Rails.logger+.
63
+ # [+:notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
64
+ # [+:report+] Use ActiveSupport::ErrorReporter to report deprecations.
65
+ # [+:silence+] Do nothing. On \Rails, set <tt>config.active_support.report_deprecations = false</tt> to disable all behaviors.
66
66
  #
67
67
  # Setting behaviors only affects deprecations that happen after boot time.
68
- # For more information you can read the documentation of the +behavior=+ method.
68
+ # For more information you can read the documentation of the #behavior= method.
69
69
  module Behavior
70
70
  # Whether to print a backtrace along with the warning.
71
71
  attr_accessor :debug
@@ -85,12 +85,12 @@ module ActiveSupport
85
85
  #
86
86
  # Available behaviors:
87
87
  #
88
- # [+raise+] Raise ActiveSupport::DeprecationException.
89
- # [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
90
- # [+log+] Log all deprecation warnings to +Rails.logger+.
91
- # [+notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
92
- # [+report+] Use ActiveSupport::ErrorReporter to report deprecations.
93
- # [+silence+] Do nothing.
88
+ # [+:raise+] Raise ActiveSupport::DeprecationException.
89
+ # [+:stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
90
+ # [+:log+] Log all deprecation warnings to +Rails.logger+.
91
+ # [+:notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
92
+ # [+:report+] Use ActiveSupport::ErrorReporter to report deprecations.
93
+ # [+:silence+] Do nothing.
94
94
  #
95
95
  # Setting behaviors only affects deprecations that happen after boot time.
96
96
  # Deprecation warnings raised by gems are not affected by this setting
@@ -104,15 +104,17 @@ module ActiveSupport
104
104
  # # custom stuff
105
105
  # }
106
106
  #
107
- # If you are using \Rails, you can set <tt>config.active_support.report_deprecations = false</tt> to disable
108
- # all deprecation behaviors. This is similar to the +silence+ option but more performant.
107
+ # If you are using \Rails, you can set
108
+ # <tt>config.active_support.report_deprecations = false</tt> to disable
109
+ # all deprecation behaviors. This is similar to the +:silence+ option but
110
+ # more performant.
109
111
  def behavior=(behavior)
110
112
  @behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
111
113
  end
112
114
 
113
115
  # Sets the behavior for disallowed deprecations (those configured by
114
116
  # ActiveSupport::Deprecation#disallowed_warnings=) to the specified
115
- # value. As with +behavior=+, this can be a single value, array, or an
117
+ # value. As with #behavior=, this can be a single value, array, or an
116
118
  # object that responds to +call+.
117
119
  def disallowed_behavior=(behavior)
118
120
  @disallowed_behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || arity_coerce(b) }
@@ -142,7 +142,9 @@ module ActiveSupport
142
142
  return _extract_callstack(callstack) if callstack.first.is_a? String
143
143
 
144
144
  offending_line = callstack.find { |frame|
145
- frame.absolute_path && !ignored_callstack(frame.absolute_path)
145
+ # Code generated with `eval` doesn't have an `absolute_path`, e.g. templates.
146
+ path = frame.absolute_path || frame.path
147
+ path && !ignored_callstack?(path)
146
148
  } || callstack.first
147
149
 
148
150
  [offending_line.path, offending_line.lineno, offending_line.label]
@@ -150,7 +152,7 @@ module ActiveSupport
150
152
 
151
153
  def _extract_callstack(callstack)
152
154
  warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
153
- offending_line = callstack.find { |line| !ignored_callstack(line) } || callstack.first
155
+ offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
154
156
 
155
157
  if offending_line
156
158
  if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
@@ -161,10 +163,11 @@ module ActiveSupport
161
163
  end
162
164
  end
163
165
 
164
- RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
166
+ RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
167
+ LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
165
168
 
166
- def ignored_callstack(path)
167
- path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"])
169
+ def ignored_callstack?(path)
170
+ path.start_with?(RAILS_GEM_ROOT, LIB_DIR)
168
171
  end
169
172
  end
170
173
  end
@@ -9,7 +9,7 @@ module ActiveSupport
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 1
12
- TINY = 1
12
+ TINY = 5
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -7,18 +7,28 @@ module ActiveSupport
7
7
  def translate(key, **options)
8
8
  if html_safe_translation_key?(key)
9
9
  html_safe_options = html_escape_translation_options(options)
10
- translation = I18n.translate(key, **html_safe_options)
11
- html_safe_translation(translation)
10
+
11
+ exception = false
12
+ exception_handler = ->(*args) do
13
+ exception = true
14
+ I18n.exception_handler.call(*args)
15
+ end
16
+ translation = I18n.translate(key, **html_safe_options, exception_handler: exception_handler)
17
+ if exception
18
+ translation
19
+ else
20
+ html_safe_translation(translation)
21
+ end
12
22
  else
13
23
  I18n.translate(key, **options)
14
24
  end
15
25
  end
16
26
 
17
- private
18
- def html_safe_translation_key?(key)
19
- /(?:_|\b)html\z/.match?(key)
20
- end
27
+ def html_safe_translation_key?(key)
28
+ /(?:_|\b)html\z/.match?(key)
29
+ end
21
30
 
31
+ private
22
32
  def html_escape_translation_options(options)
23
33
  options.each do |name, value|
24
34
  unless i18n_option?(name) || (name == :count && value.is_a?(Numeric))
@@ -164,7 +164,7 @@ module ActiveSupport
164
164
  # upcase_first('w') # => "W"
165
165
  # upcase_first('') # => ""
166
166
  def upcase_first(string)
167
- string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
167
+ string.length > 0 ? string[0].upcase.concat(string[1..-1]) : +""
168
168
  end
169
169
 
170
170
  # Converts the first character in the string to lowercase.
@@ -173,7 +173,7 @@ module ActiveSupport
173
173
  # downcase_first('I') # => "i"
174
174
  # downcase_first('') # => ""
175
175
  def downcase_first(string)
176
- string.length > 0 ? string[0].downcase.concat(string[1..-1]) : ""
176
+ string.length > 0 ? string[0].downcase.concat(string[1..-1]) : +""
177
177
  end
178
178
 
179
179
  # Capitalizes all the words and replaces some characters in the string to
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "active_support/core_ext/module/attribute_accessors"
4
4
  require "active_support/core_ext/class/attribute"
5
+ require "active_support/core_ext/enumerable"
5
6
  require "active_support/subscriber"
6
7
  require "active_support/deprecation/proxy_wrappers"
7
8
 
@@ -86,6 +87,12 @@ module ActiveSupport
86
87
  mattr_accessor :colorize_logging, default: true
87
88
  class_attribute :log_levels, instance_accessor: false, default: {} # :nodoc:
88
89
 
90
+ LEVEL_CHECKS = {
91
+ debug: -> (logger) { !logger.debug? },
92
+ info: -> (logger) { !logger.info? },
93
+ error: -> (logger) { !logger.error? },
94
+ }
95
+
89
96
  class << self
90
97
  def logger
91
98
  @logger ||= if defined?(Rails) && Rails.respond_to?(:logger)
@@ -122,7 +129,7 @@ module ActiveSupport
122
129
  end
123
130
 
124
131
  def subscribe_log_level(method, level)
125
- self.log_levels = log_levels.merge(method => ::Logger.const_get(level.upcase))
132
+ self.log_levels = log_levels.merge(method => LEVEL_CHECKS.fetch(level))
126
133
  set_event_levels
127
134
  end
128
135
  end
@@ -137,7 +144,7 @@ module ActiveSupport
137
144
  end
138
145
 
139
146
  def silenced?(event)
140
- logger.nil? || logger.level > @event_levels.fetch(event, Float::INFINITY)
147
+ logger.nil? || @event_levels[event]&.call(logger)
141
148
  end
142
149
 
143
150
  def call(event)
@@ -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
 
@@ -18,26 +18,30 @@ module ActiveSupport
18
18
  end
19
19
 
20
20
  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
21
+ private
22
+ def iterate_guarding_exceptions(collection)
23
+ exceptions = nil
30
24
 
31
- if exceptions
32
- if exceptions.size == 1
33
- raise exceptions.first
34
- else
35
- raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
25
+ collection.each do |s|
26
+ yield s
27
+ rescue Exception => e
28
+ exceptions ||= []
29
+ exceptions << e
36
30
  end
37
- end
38
31
 
39
- listeners
40
- end
32
+ if exceptions
33
+ exceptions = exceptions.flat_map do |exception|
34
+ exception.is_a?(InstrumentationSubscriberError) ? exception.exceptions : [exception]
35
+ end
36
+ if exceptions.size == 1
37
+ raise exceptions.first
38
+ else
39
+ raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
40
+ end
41
+ end
42
+
43
+ collection
44
+ end
41
45
  end
42
46
 
43
47
  # This is a default queue implementation that ships with Notifications.
@@ -225,6 +229,8 @@ module ActiveSupport
225
229
  # handle.finish
226
230
  # end
227
231
  class Handle
232
+ include FanoutIteration
233
+
228
234
  def initialize(notifier, name, id, payload) # :nodoc:
229
235
  @name = name
230
236
  @id = id
@@ -239,7 +245,7 @@ module ActiveSupport
239
245
  ensure_state! :initialized
240
246
  @state = :started
241
247
 
242
- @groups.each do |group|
248
+ iterate_guarding_exceptions(@groups) do |group|
243
249
  group.start(@name, @id, @payload)
244
250
  end
245
251
  end
@@ -252,7 +258,7 @@ module ActiveSupport
252
258
  ensure_state! :started
253
259
  @state = :finished
254
260
 
255
- @groups.each do |group|
261
+ iterate_guarding_exceptions(@groups) do |group|
256
262
  group.finish(name, id, payload)
257
263
  end
258
264
  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)
@@ -119,7 +119,15 @@ module ActiveSupport
119
119
  @allocation_count_finish = 0
120
120
  end
121
121
 
122
- def record
122
+ def time
123
+ @time / 1000.0 if @time
124
+ end
125
+
126
+ def end
127
+ @end / 1000.0 if @end
128
+ end
129
+
130
+ def record # :nodoc:
123
131
  start!
124
132
  begin
125
133
  yield payload if block_given?
@@ -195,7 +203,7 @@ module ActiveSupport
195
203
  #
196
204
  # @event.duration # => 1000.138
197
205
  def duration
198
- self.end - time
206
+ @end - @time
199
207
  end
200
208
 
201
209
  private
@@ -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