activesupport 5.2.4.3 → 7.0.3
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.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +244 -459
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +31 -5
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/cache/file_store.rb +47 -41
- data/lib/active_support/cache/mem_cache_store.rb +151 -40
- data/lib/active_support/cache/memory_store.rb +68 -34
- data/lib/active_support/cache/null_store.rb +16 -3
- data/lib/active_support/cache/redis_cache_store.rb +103 -101
- data/lib/active_support/cache/strategy/local_cache.rb +56 -64
- data/lib/active_support/cache.rb +333 -116
- data/lib/active_support/callbacks.rb +244 -128
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +72 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -3
- data/lib/active_support/configurable.rb +15 -16
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +15 -7
- data/lib/active_support/core_ext/array/conversions.rb +18 -17
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/array.rb +2 -1
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +9 -22
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +15 -14
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date.rb +1 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -51
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +13 -14
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +39 -13
- data/lib/active_support/core_ext/enumerable.rb +241 -76
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +3 -4
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +2 -31
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +32 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +35 -28
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +70 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +23 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +132 -129
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric.rb +1 -1
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +3 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +14 -110
- data/lib/active_support/core_ext/object/json.rb +44 -27
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +24 -14
- data/lib/active_support/core_ext/object/with_options.rb +21 -2
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +23 -27
- data/lib/active_support/core_ext/range/conversions.rb +32 -30
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +1 -2
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +3 -2
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +46 -7
- data/lib/active_support/core_ext/string/inquiry.rb +2 -1
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +129 -20
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +59 -10
- data/lib/active_support/core_ext/time/conversions.rb +15 -12
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +7 -22
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -22
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +47 -16
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +60 -715
- data/lib/active_support/deprecation/behaviors.rb +21 -5
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +18 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +31 -8
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +7 -2
- data/lib/active_support/descendants_tracker.rb +190 -34
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +5 -7
- data/lib/active_support/duration/iso8601_serializer.rb +27 -15
- data/lib/active_support/duration.rb +149 -67
- data/lib/active_support/encrypted_configuration.rb +12 -5
- data/lib/active_support/encrypted_file.rb +23 -5
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +85 -122
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +44 -21
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +5 -5
- data/lib/active_support/hash_with_indifferent_access.rb +73 -43
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +2 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +25 -14
- data/lib/active_support/inflector/methods.rb +38 -71
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/isolated_execution_state.rb +72 -0
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +14 -6
- data/lib/active_support/key_generator.rb +23 -38
- data/lib/active_support/lazy_load_hooks.rb +19 -5
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +51 -11
- data/lib/active_support/logger.rb +6 -22
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +45 -10
- data/lib/active_support/message_encryptor.rb +20 -19
- data/lib/active_support/message_verifier.rb +53 -21
- data/lib/active_support/messages/metadata.rb +13 -4
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +17 -76
- data/lib/active_support/multibyte/unicode.rb +7 -331
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +163 -37
- data/lib/active_support/notifications/instrumenter.rb +90 -11
- data/lib/active_support/notifications.rb +88 -30
- data/lib/active_support/number_helper/number_converter.rb +6 -9
- data/lib/active_support/number_helper/number_to_currency_converter.rb +12 -12
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -4
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -32
- data/lib/active_support/number_helper.rb +36 -12
- data/lib/active_support/option_merger.rb +15 -4
- data/lib/active_support/ordered_hash.rb +2 -2
- data/lib/active_support/ordered_options.rb +14 -4
- data/lib/active_support/parameter_filter.rb +138 -0
- data/lib/active_support/per_thread_registry.rb +6 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +77 -5
- data/lib/active_support/reloader.rb +5 -6
- data/lib/active_support/rescuable.rb +8 -8
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +2 -3
- data/lib/active_support/subscriber.rb +79 -46
- data/lib/active_support/tagged_logging.rb +58 -9
- data/lib/active_support/test_case.rb +79 -0
- data/lib/active_support/testing/assertions.rb +62 -11
- data/lib/active_support/testing/deprecation.rb +52 -2
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +4 -4
- data/lib/active_support/testing/method_call_assertions.rb +32 -5
- data/lib/active_support/testing/parallelization/server.rb +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +55 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +4 -7
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +60 -14
- data/lib/active_support/time_with_zone.rb +139 -64
- data/lib/active_support/values/time_zone.rb +66 -30
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -4
- data/lib/active_support/xml_mini/libxml.rb +7 -7
- data/lib/active_support/xml_mini/libxmlsax.rb +5 -5
- data/lib/active_support/xml_mini/nokogiri.rb +6 -6
- data/lib/active_support/xml_mini/nokogirisax.rb +4 -4
- data/lib/active_support/xml_mini/rexml.rb +11 -4
- data/lib/active_support/xml_mini.rb +7 -14
- data/lib/active_support.rb +30 -1
- metadata +64 -35
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/marshal.rb +0 -24
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -3
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -2,9 +2,21 @@
|
|
2
2
|
|
3
3
|
require "mutex_m"
|
4
4
|
require "concurrent/map"
|
5
|
+
require "set"
|
6
|
+
require "active_support/core_ext/object/try"
|
5
7
|
|
6
8
|
module ActiveSupport
|
7
9
|
module Notifications
|
10
|
+
class InstrumentationSubscriberError < RuntimeError
|
11
|
+
attr_reader :exceptions
|
12
|
+
|
13
|
+
def initialize(exceptions)
|
14
|
+
@exceptions = exceptions
|
15
|
+
exception_class_names = exceptions.map { |e| e.class.name }
|
16
|
+
super "Exception(s) occurred within instrumentation subscribers: #{exception_class_names.join(', ')}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
8
20
|
# This is a default queue implementation that ships with Notifications.
|
9
21
|
# It just pushes events to all registered log subscribers.
|
10
22
|
#
|
@@ -13,16 +25,25 @@ module ActiveSupport
|
|
13
25
|
include Mutex_m
|
14
26
|
|
15
27
|
def initialize
|
16
|
-
@
|
28
|
+
@string_subscribers = Hash.new { |h, k| h[k] = [] }
|
29
|
+
@other_subscribers = []
|
17
30
|
@listeners_for = Concurrent::Map.new
|
18
31
|
super
|
19
32
|
end
|
20
33
|
|
21
|
-
def subscribe(pattern = nil, callable = nil, &block)
|
22
|
-
subscriber = Subscribers.new(pattern, callable || block)
|
34
|
+
def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
|
35
|
+
subscriber = Subscribers.new(pattern, callable || block, monotonic)
|
23
36
|
synchronize do
|
24
|
-
|
25
|
-
|
37
|
+
case pattern
|
38
|
+
when String
|
39
|
+
@string_subscribers[pattern] << subscriber
|
40
|
+
@listeners_for.delete(pattern)
|
41
|
+
when NilClass, Regexp
|
42
|
+
@other_subscribers << subscriber
|
43
|
+
@listeners_for.clear
|
44
|
+
else
|
45
|
+
raise ArgumentError, "pattern must be specified as a String, Regexp or empty"
|
46
|
+
end
|
26
47
|
end
|
27
48
|
subscriber
|
28
49
|
end
|
@@ -31,32 +52,65 @@ module ActiveSupport
|
|
31
52
|
synchronize do
|
32
53
|
case subscriber_or_name
|
33
54
|
when String
|
34
|
-
@
|
55
|
+
@string_subscribers[subscriber_or_name].clear
|
56
|
+
@listeners_for.delete(subscriber_or_name)
|
57
|
+
@other_subscribers.each { |sub| sub.unsubscribe!(subscriber_or_name) }
|
35
58
|
else
|
36
|
-
|
59
|
+
pattern = subscriber_or_name.try(:pattern)
|
60
|
+
if String === pattern
|
61
|
+
@string_subscribers[pattern].delete(subscriber_or_name)
|
62
|
+
@listeners_for.delete(pattern)
|
63
|
+
else
|
64
|
+
@other_subscribers.delete(subscriber_or_name)
|
65
|
+
@listeners_for.clear
|
66
|
+
end
|
37
67
|
end
|
38
|
-
|
39
|
-
@listeners_for.clear
|
40
68
|
end
|
41
69
|
end
|
42
70
|
|
43
71
|
def start(name, id, payload)
|
44
|
-
listeners_for(name)
|
72
|
+
iterate_guarding_exceptions(listeners_for(name)) { |s| s.start(name, id, payload) }
|
45
73
|
end
|
46
74
|
|
47
75
|
def finish(name, id, payload, listeners = listeners_for(name))
|
48
|
-
listeners
|
76
|
+
iterate_guarding_exceptions(listeners) { |s| s.finish(name, id, payload) }
|
49
77
|
end
|
50
78
|
|
51
79
|
def publish(name, *args)
|
52
|
-
listeners_for(name)
|
80
|
+
iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
|
81
|
+
end
|
82
|
+
|
83
|
+
def publish_event(event)
|
84
|
+
iterate_guarding_exceptions(listeners_for(event.name)) { |s| s.publish_event(event) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def iterate_guarding_exceptions(listeners)
|
88
|
+
exceptions = nil
|
89
|
+
|
90
|
+
listeners.each do |s|
|
91
|
+
yield s
|
92
|
+
rescue Exception => e
|
93
|
+
exceptions ||= []
|
94
|
+
exceptions << e
|
95
|
+
end
|
96
|
+
|
97
|
+
if exceptions
|
98
|
+
if exceptions.size == 1
|
99
|
+
raise exceptions.first
|
100
|
+
else
|
101
|
+
raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
listeners
|
53
106
|
end
|
54
107
|
|
55
108
|
def listeners_for(name)
|
56
109
|
# this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
|
57
110
|
@listeners_for[name] || synchronize do
|
58
111
|
# use synchronisation when accessing @subscribers
|
59
|
-
@listeners_for[name] ||=
|
112
|
+
@listeners_for[name] ||=
|
113
|
+
@string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
|
60
114
|
end
|
61
115
|
end
|
62
116
|
|
@@ -69,25 +123,70 @@ module ActiveSupport
|
|
69
123
|
end
|
70
124
|
|
71
125
|
module Subscribers # :nodoc:
|
72
|
-
def self.new(pattern, listener)
|
126
|
+
def self.new(pattern, listener, monotonic)
|
127
|
+
subscriber_class = monotonic ? MonotonicTimed : Timed
|
128
|
+
|
73
129
|
if listener.respond_to?(:start) && listener.respond_to?(:finish)
|
74
|
-
|
130
|
+
subscriber_class = Evented
|
75
131
|
else
|
76
|
-
|
132
|
+
# Doing this to detect a single argument block or callable
|
133
|
+
# like `proc { |x| }` vs `proc { |*x| }`, `proc { |**x| }`,
|
134
|
+
# or `proc { |x, **y| }`
|
135
|
+
procish = listener.respond_to?(:parameters) ? listener : listener.method(:call)
|
136
|
+
|
137
|
+
if procish.arity == 1 && procish.parameters.length == 1
|
138
|
+
subscriber_class = EventObject
|
139
|
+
end
|
77
140
|
end
|
78
141
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
142
|
+
subscriber_class.new(pattern, listener)
|
143
|
+
end
|
144
|
+
|
145
|
+
class Matcher # :nodoc:
|
146
|
+
attr_reader :pattern, :exclusions
|
147
|
+
|
148
|
+
def self.wrap(pattern)
|
149
|
+
if String === pattern
|
150
|
+
pattern
|
151
|
+
elsif pattern.nil?
|
152
|
+
AllMessages.new
|
153
|
+
else
|
154
|
+
new(pattern)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def initialize(pattern)
|
159
|
+
@pattern = pattern
|
160
|
+
@exclusions = Set.new
|
161
|
+
end
|
162
|
+
|
163
|
+
def unsubscribe!(name)
|
164
|
+
exclusions << -name if pattern === name
|
165
|
+
end
|
166
|
+
|
167
|
+
def ===(name)
|
168
|
+
pattern === name && !exclusions.include?(name)
|
169
|
+
end
|
170
|
+
|
171
|
+
class AllMessages
|
172
|
+
def ===(name)
|
173
|
+
true
|
174
|
+
end
|
175
|
+
|
176
|
+
def unsubscribe!(*)
|
177
|
+
false
|
178
|
+
end
|
83
179
|
end
|
84
180
|
end
|
85
181
|
|
86
|
-
class Evented
|
182
|
+
class Evented # :nodoc:
|
183
|
+
attr_reader :pattern
|
184
|
+
|
87
185
|
def initialize(pattern, delegate)
|
88
|
-
@pattern = pattern
|
186
|
+
@pattern = Matcher.wrap(pattern)
|
89
187
|
@delegate = delegate
|
90
188
|
@can_publish = delegate.respond_to?(:publish)
|
189
|
+
@can_publish_event = delegate.respond_to?(:publish_event)
|
91
190
|
end
|
92
191
|
|
93
192
|
def publish(name, *args)
|
@@ -96,6 +195,14 @@ module ActiveSupport
|
|
96
195
|
end
|
97
196
|
end
|
98
197
|
|
198
|
+
def publish_event(event)
|
199
|
+
if @can_publish_event
|
200
|
+
@delegate.publish_event event
|
201
|
+
else
|
202
|
+
publish(event.name, event.time, event.end, event.transaction_id, event.payload)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
99
206
|
def start(name, id, payload)
|
100
207
|
@delegate.start name, id, payload
|
101
208
|
end
|
@@ -105,11 +212,11 @@ module ActiveSupport
|
|
105
212
|
end
|
106
213
|
|
107
214
|
def subscribed_to?(name)
|
108
|
-
|
215
|
+
pattern === name
|
109
216
|
end
|
110
217
|
|
111
|
-
def
|
112
|
-
|
218
|
+
def unsubscribe!(name)
|
219
|
+
pattern.unsubscribe!(name)
|
113
220
|
end
|
114
221
|
end
|
115
222
|
|
@@ -119,39 +226,58 @@ module ActiveSupport
|
|
119
226
|
end
|
120
227
|
|
121
228
|
def start(name, id, payload)
|
122
|
-
timestack =
|
229
|
+
timestack = IsolatedExecutionState[:_timestack] ||= []
|
123
230
|
timestack.push Time.now
|
124
231
|
end
|
125
232
|
|
126
233
|
def finish(name, id, payload)
|
127
|
-
timestack =
|
234
|
+
timestack = IsolatedExecutionState[:_timestack]
|
128
235
|
started = timestack.pop
|
129
236
|
@delegate.call(name, started, Time.now, id, payload)
|
130
237
|
end
|
131
238
|
end
|
132
239
|
|
133
|
-
class
|
134
|
-
def
|
135
|
-
@delegate
|
240
|
+
class MonotonicTimed < Evented # :nodoc:
|
241
|
+
def publish(name, *args)
|
242
|
+
@delegate.call name, *args
|
136
243
|
end
|
137
244
|
|
138
245
|
def start(name, id, payload)
|
139
|
-
|
246
|
+
timestack = IsolatedExecutionState[:_timestack_monotonic] ||= []
|
247
|
+
timestack.push Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
140
248
|
end
|
141
249
|
|
142
250
|
def finish(name, id, payload)
|
143
|
-
|
251
|
+
timestack = IsolatedExecutionState[:_timestack_monotonic]
|
252
|
+
started = timestack.pop
|
253
|
+
@delegate.call(name, started, Process.clock_gettime(Process::CLOCK_MONOTONIC), id, payload)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class EventObject < Evented
|
258
|
+
def start(name, id, payload)
|
259
|
+
stack = IsolatedExecutionState[:_event_stack] ||= []
|
260
|
+
event = build_event name, id, payload
|
261
|
+
event.start!
|
262
|
+
stack.push event
|
144
263
|
end
|
145
264
|
|
146
|
-
def
|
147
|
-
|
265
|
+
def finish(name, id, payload)
|
266
|
+
stack = IsolatedExecutionState[:_event_stack]
|
267
|
+
event = stack.pop
|
268
|
+
event.payload = payload
|
269
|
+
event.finish!
|
270
|
+
@delegate.call event
|
148
271
|
end
|
149
272
|
|
150
|
-
def
|
151
|
-
|
273
|
+
def publish_event(event)
|
274
|
+
@delegate.call event
|
152
275
|
end
|
153
276
|
|
154
|
-
|
277
|
+
private
|
278
|
+
def build_event(name, id, payload)
|
279
|
+
ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
|
280
|
+
end
|
155
281
|
end
|
156
282
|
end
|
157
283
|
end
|
@@ -13,14 +13,15 @@ module ActiveSupport
|
|
13
13
|
@notifier = notifier
|
14
14
|
end
|
15
15
|
|
16
|
-
#
|
17
|
-
# and publish it.
|
18
|
-
#
|
16
|
+
# Given a block, instrument it by measuring the time taken to execute
|
17
|
+
# and publish it. Without a block, simply send a message via the
|
18
|
+
# notifier. Notice that events get sent even if an error occurs in the
|
19
|
+
# passed-in block.
|
19
20
|
def instrument(name, payload = {})
|
20
21
|
# some of the listeners might have state
|
21
22
|
listeners_state = start name, payload
|
22
23
|
begin
|
23
|
-
yield payload
|
24
|
+
yield payload if block_given?
|
24
25
|
rescue Exception => e
|
25
26
|
payload[:exception] = [e.class.name, e.message]
|
26
27
|
payload[:exception_object] = e
|
@@ -30,6 +31,10 @@ module ActiveSupport
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
34
|
+
def new_event(name, payload = {}) # :nodoc:
|
35
|
+
Event.new(name, nil, nil, @id, payload)
|
36
|
+
end
|
37
|
+
|
33
38
|
# Send a start notification with +name+ and +payload+.
|
34
39
|
def start(name, payload)
|
35
40
|
@notifier.start name, @id, payload
|
@@ -45,24 +50,71 @@ module ActiveSupport
|
|
45
50
|
end
|
46
51
|
|
47
52
|
private
|
48
|
-
|
49
53
|
def unique_id
|
50
54
|
SecureRandom.hex(10)
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
54
58
|
class Event
|
55
|
-
attr_reader :name, :time, :
|
56
|
-
attr_accessor :
|
59
|
+
attr_reader :name, :time, :end, :transaction_id, :children
|
60
|
+
attr_accessor :payload
|
57
61
|
|
58
62
|
def initialize(name, start, ending, transaction_id, payload)
|
59
63
|
@name = name
|
60
64
|
@payload = payload.dup
|
61
|
-
@time = start
|
65
|
+
@time = start ? start.to_f * 1_000.0 : start
|
62
66
|
@transaction_id = transaction_id
|
63
|
-
@end = ending
|
67
|
+
@end = ending ? ending.to_f * 1_000.0 : ending
|
64
68
|
@children = []
|
65
|
-
@
|
69
|
+
@cpu_time_start = 0.0
|
70
|
+
@cpu_time_finish = 0.0
|
71
|
+
@allocation_count_start = 0
|
72
|
+
@allocation_count_finish = 0
|
73
|
+
end
|
74
|
+
|
75
|
+
def record
|
76
|
+
start!
|
77
|
+
begin
|
78
|
+
yield payload if block_given?
|
79
|
+
rescue Exception => e
|
80
|
+
payload[:exception] = [e.class.name, e.message]
|
81
|
+
payload[:exception_object] = e
|
82
|
+
raise e
|
83
|
+
ensure
|
84
|
+
finish!
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Record information at the time this event starts
|
89
|
+
def start!
|
90
|
+
@time = now
|
91
|
+
@cpu_time_start = now_cpu
|
92
|
+
@allocation_count_start = now_allocations
|
93
|
+
end
|
94
|
+
|
95
|
+
# Record information at the time this event finishes
|
96
|
+
def finish!
|
97
|
+
@cpu_time_finish = now_cpu
|
98
|
+
@end = now
|
99
|
+
@allocation_count_finish = now_allocations
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns the CPU time (in milliseconds) passed since the call to
|
103
|
+
# +start!+ and the call to +finish!+
|
104
|
+
def cpu_time
|
105
|
+
@cpu_time_finish - @cpu_time_start
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns the idle time time (in milliseconds) passed since the call to
|
109
|
+
# +start!+ and the call to +finish!+
|
110
|
+
def idle_time
|
111
|
+
duration - cpu_time
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns the number of allocations made since the call to +start!+ and
|
115
|
+
# the call to +finish!+
|
116
|
+
def allocations
|
117
|
+
@allocation_count_finish - @allocation_count_start
|
66
118
|
end
|
67
119
|
|
68
120
|
# Returns the difference in milliseconds between when the execution of the
|
@@ -78,7 +130,7 @@ module ActiveSupport
|
|
78
130
|
#
|
79
131
|
# @event.duration # => 1000.138
|
80
132
|
def duration
|
81
|
-
|
133
|
+
self.end - time
|
82
134
|
end
|
83
135
|
|
84
136
|
def <<(event)
|
@@ -88,6 +140,33 @@ module ActiveSupport
|
|
88
140
|
def parent_of?(event)
|
89
141
|
@children.include? event
|
90
142
|
end
|
143
|
+
|
144
|
+
private
|
145
|
+
def now
|
146
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
147
|
+
end
|
148
|
+
|
149
|
+
begin
|
150
|
+
Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
|
151
|
+
|
152
|
+
def now_cpu
|
153
|
+
Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
|
154
|
+
end
|
155
|
+
rescue
|
156
|
+
def now_cpu # rubocop:disable Lint/DuplicateMethods
|
157
|
+
0.0
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
if GC.stat.key?(:total_allocated_objects)
|
162
|
+
def now_allocations
|
163
|
+
GC.stat(:total_allocated_objects)
|
164
|
+
end
|
165
|
+
else # Likely on JRuby, TruffleRuby
|
166
|
+
def now_allocations
|
167
|
+
0
|
168
|
+
end
|
169
|
+
end
|
91
170
|
end
|
92
171
|
end
|
93
172
|
end
|
@@ -2,10 +2,9 @@
|
|
2
2
|
|
3
3
|
require "active_support/notifications/instrumenter"
|
4
4
|
require "active_support/notifications/fanout"
|
5
|
-
require "active_support/per_thread_registry"
|
6
5
|
|
7
6
|
module ActiveSupport
|
8
|
-
# = Notifications
|
7
|
+
# = \Notifications
|
9
8
|
#
|
10
9
|
# <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
|
11
10
|
# Ruby.
|
@@ -34,10 +33,23 @@ module ActiveSupport
|
|
34
33
|
# name # => String, name of the event (such as 'render' from above)
|
35
34
|
# start # => Time, when the instrumented block started execution
|
36
35
|
# finish # => Time, when the instrumented block ended execution
|
37
|
-
# id # => String, unique ID for
|
36
|
+
# id # => String, unique ID for the instrumenter that fired the event
|
38
37
|
# payload # => Hash, the payload
|
39
38
|
# end
|
40
39
|
#
|
40
|
+
# Here, the +start+ and +finish+ values represent wall-clock time. If you are
|
41
|
+
# concerned about accuracy, you can register a monotonic subscriber.
|
42
|
+
#
|
43
|
+
# ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
|
44
|
+
# 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
|
47
|
+
# id # => String, unique ID for the instrumenter that fired the event
|
48
|
+
# payload # => Hash, the payload
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# The +start+ and +finish+ values above represent monotonic time.
|
52
|
+
#
|
41
53
|
# For instance, let's store all "render" events in an array:
|
42
54
|
#
|
43
55
|
# events = []
|
@@ -59,7 +71,7 @@ module ActiveSupport
|
|
59
71
|
# event.payload # => { extra: :information }
|
60
72
|
#
|
61
73
|
# The block in the <tt>subscribe</tt> call gets the name of the event, start
|
62
|
-
# timestamp, end timestamp, a string with a unique identifier for that event
|
74
|
+
# timestamp, end timestamp, a string with a unique identifier for that event's instrumenter
|
63
75
|
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
|
64
76
|
# that order.
|
65
77
|
#
|
@@ -67,9 +79,12 @@ module ActiveSupport
|
|
67
79
|
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
|
68
80
|
# the name of the exception class, and the exception message.
|
69
81
|
# The <tt>:exception_object</tt> key of the payload will have the exception
|
70
|
-
# itself as the value
|
82
|
+
# itself as the value:
|
71
83
|
#
|
72
|
-
#
|
84
|
+
# event.payload[:exception] # => ["ArgumentError", "Invalid value"]
|
85
|
+
# event.payload[:exception_object] # => #<ArgumentError: Invalid value>
|
86
|
+
#
|
87
|
+
# As the earlier example depicts, the class ActiveSupport::Notifications::Event
|
73
88
|
# is able to take the arguments as they come and provide an object-oriented
|
74
89
|
# interface to that data.
|
75
90
|
#
|
@@ -132,6 +147,16 @@ module ActiveSupport
|
|
132
147
|
# during the execution of the block. The callback is unsubscribed automatically
|
133
148
|
# after that.
|
134
149
|
#
|
150
|
+
# To record +started+ and +finished+ values with monotonic time,
|
151
|
+
# specify the optional <tt>:monotonic</tt> option to the
|
152
|
+
# <tt>subscribed</tt> method. The <tt>:monotonic</tt> option is set
|
153
|
+
# to +false+ by default.
|
154
|
+
#
|
155
|
+
# callback = lambda {|name, started, finished, unique_id, payload| ... }
|
156
|
+
# ActiveSupport::Notifications.subscribed(callback, "sql.active_record", monotonic: true) do
|
157
|
+
# ...
|
158
|
+
# end
|
159
|
+
#
|
135
160
|
# === Manual Unsubscription
|
136
161
|
#
|
137
162
|
# The +subscribe+ method returns a subscriber object:
|
@@ -150,6 +175,15 @@ module ActiveSupport
|
|
150
175
|
#
|
151
176
|
# ActiveSupport::Notifications.unsubscribe("render")
|
152
177
|
#
|
178
|
+
# Subscribers using a regexp or other pattern-matching object will remain subscribed
|
179
|
+
# to all events that match their original pattern, unless those events match a string
|
180
|
+
# passed to +unsubscribe+:
|
181
|
+
#
|
182
|
+
# subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
|
183
|
+
# ActiveSupport::Notifications.unsubscribe('render_template.action_view')
|
184
|
+
# subscriber.matches?('render_template.action_view') # => false
|
185
|
+
# subscriber.matches?('render_partial.action_view') # => true
|
186
|
+
#
|
153
187
|
# == Default Queue
|
154
188
|
#
|
155
189
|
# Notifications ships with a queue implementation that consumes and publishes events
|
@@ -163,6 +197,10 @@ module ActiveSupport
|
|
163
197
|
notifier.publish(name, *args)
|
164
198
|
end
|
165
199
|
|
200
|
+
def publish_event(event) # :nodoc:
|
201
|
+
notifier.publish_event(event)
|
202
|
+
end
|
203
|
+
|
166
204
|
def instrument(name, payload = {})
|
167
205
|
if notifier.listening?(name)
|
168
206
|
instrumenter.instrument(name, payload) { yield payload if block_given? }
|
@@ -171,12 +209,47 @@ module ActiveSupport
|
|
171
209
|
end
|
172
210
|
end
|
173
211
|
|
174
|
-
|
175
|
-
|
212
|
+
# Subscribe to a given event name with the passed +block+.
|
213
|
+
#
|
214
|
+
# You can subscribe to events by passing a String to match exact event
|
215
|
+
# names, or by passing a Regexp to match all events that match a pattern.
|
216
|
+
#
|
217
|
+
# ActiveSupport::Notifications.subscribe(/render/) do |*args|
|
218
|
+
# @event = ActiveSupport::Notifications::Event.new(*args)
|
219
|
+
# end
|
220
|
+
#
|
221
|
+
# The +block+ will receive five parameters with information about the event:
|
222
|
+
#
|
223
|
+
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
|
224
|
+
# name # => String, name of the event (such as 'render' from above)
|
225
|
+
# start # => Time, when the instrumented block started execution
|
226
|
+
# finish # => Time, when the instrumented block ended execution
|
227
|
+
# id # => String, unique ID for the instrumenter that fired the event
|
228
|
+
# payload # => Hash, the payload
|
229
|
+
# end
|
230
|
+
#
|
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
|
+
# Raises an error if invalid event name type is passed:
|
239
|
+
#
|
240
|
+
# ActiveSupport::Notifications.subscribe(:render) {|*args| ...}
|
241
|
+
# #=> ArgumentError (pattern must be specified as a String, Regexp or empty)
|
242
|
+
#
|
243
|
+
def subscribe(pattern = nil, callback = nil, &block)
|
244
|
+
notifier.subscribe(pattern, callback, monotonic: false, &block)
|
176
245
|
end
|
177
246
|
|
178
|
-
def
|
179
|
-
|
247
|
+
def monotonic_subscribe(pattern = nil, callback = nil, &block)
|
248
|
+
notifier.subscribe(pattern, callback, monotonic: true, &block)
|
249
|
+
end
|
250
|
+
|
251
|
+
def subscribed(callback, pattern = nil, monotonic: false, &block)
|
252
|
+
subscriber = notifier.subscribe(pattern, callback, monotonic: monotonic)
|
180
253
|
yield
|
181
254
|
ensure
|
182
255
|
unsubscribe(subscriber)
|
@@ -187,28 +260,13 @@ module ActiveSupport
|
|
187
260
|
end
|
188
261
|
|
189
262
|
def instrumenter
|
190
|
-
|
263
|
+
registry[notifier] ||= Instrumenter.new(notifier)
|
191
264
|
end
|
192
|
-
end
|
193
265
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
# InstrumentationRegistry.instrumenter_for(notifier)
|
199
|
-
#
|
200
|
-
# The instrumenters for multiple notifiers are held in a single instance of
|
201
|
-
# this class.
|
202
|
-
class InstrumentationRegistry # :nodoc:
|
203
|
-
extend ActiveSupport::PerThreadRegistry
|
204
|
-
|
205
|
-
def initialize
|
206
|
-
@registry = {}
|
207
|
-
end
|
208
|
-
|
209
|
-
def instrumenter_for(notifier)
|
210
|
-
@registry[notifier] ||= Instrumenter.new(notifier)
|
211
|
-
end
|
266
|
+
private
|
267
|
+
def registry
|
268
|
+
ActiveSupport::IsolatedExecutionState[:active_support_notifications_registry] ||= {}
|
269
|
+
end
|
212
270
|
end
|
213
271
|
|
214
272
|
self.notifier = Fanout.new
|
@@ -30,7 +30,7 @@ module ActiveSupport
|
|
30
30
|
# If set to true, precision will mean the number of significant digits instead
|
31
31
|
# of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
|
32
32
|
significant: false,
|
33
|
-
# If set, the zeros after the decimal separator will always be stripped (
|
33
|
+
# If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2)
|
34
34
|
strip_insignificant_zeros: false
|
35
35
|
},
|
36
36
|
|
@@ -136,7 +136,6 @@ module ActiveSupport
|
|
136
136
|
end
|
137
137
|
|
138
138
|
private
|
139
|
-
|
140
139
|
def options
|
141
140
|
@options ||= format_options.merge(opts)
|
142
141
|
end
|
@@ -162,12 +161,12 @@ module ActiveSupport
|
|
162
161
|
options
|
163
162
|
end
|
164
163
|
|
165
|
-
def translate_number_value_with_default(key, i18n_options
|
166
|
-
I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
|
164
|
+
def translate_number_value_with_default(key, **i18n_options)
|
165
|
+
I18n.translate(key, **{ default: default_value(key), scope: :number }.merge!(i18n_options))
|
167
166
|
end
|
168
167
|
|
169
|
-
def translate_in_locale(key, i18n_options
|
170
|
-
translate_number_value_with_default(key, { locale: options[:locale] }.merge(i18n_options))
|
168
|
+
def translate_in_locale(key, **i18n_options)
|
169
|
+
translate_number_value_with_default(key, **{ locale: options[:locale] }.merge(i18n_options))
|
171
170
|
end
|
172
171
|
|
173
172
|
def default_value(key)
|
@@ -175,9 +174,7 @@ module ActiveSupport
|
|
175
174
|
end
|
176
175
|
|
177
176
|
def valid_float?
|
178
|
-
Float(number)
|
179
|
-
rescue ArgumentError, TypeError
|
180
|
-
false
|
177
|
+
Float(number, exception: false)
|
181
178
|
end
|
182
179
|
end
|
183
180
|
end
|