activesupport 7.0.10 → 7.1.0.beta1

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 (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +703 -361
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +2 -0
  7. data/lib/active_support/backtrace_cleaner.rb +25 -5
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/builder.rb +1 -1
  10. data/lib/active_support/cache/coder.rb +153 -0
  11. data/lib/active_support/cache/entry.rb +128 -0
  12. data/lib/active_support/cache/file_store.rb +37 -10
  13. data/lib/active_support/cache/mem_cache_store.rb +84 -68
  14. data/lib/active_support/cache/memory_store.rb +76 -26
  15. data/lib/active_support/cache/null_store.rb +6 -0
  16. data/lib/active_support/cache/redis_cache_store.rb +126 -131
  17. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +20 -8
  19. data/lib/active_support/cache.rb +304 -246
  20. data/lib/active_support/callbacks.rb +38 -18
  21. data/lib/active_support/concern.rb +4 -2
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  23. data/lib/active_support/concurrency/null_lock.rb +13 -0
  24. data/lib/active_support/configurable.rb +10 -0
  25. data/lib/active_support/core_ext/array/conversions.rb +2 -1
  26. data/lib/active_support/core_ext/array.rb +0 -1
  27. data/lib/active_support/core_ext/class/attribute.rb +1 -0
  28. data/lib/active_support/core_ext/class/subclasses.rb +13 -10
  29. data/lib/active_support/core_ext/date/conversions.rb +1 -0
  30. data/lib/active_support/core_ext/date.rb +0 -1
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  32. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  33. data/lib/active_support/core_ext/date_time.rb +0 -1
  34. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  35. data/lib/active_support/core_ext/enumerable.rb +3 -75
  36. data/lib/active_support/core_ext/erb/util.rb +196 -0
  37. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  38. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  39. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  40. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  41. data/lib/active_support/core_ext/module/delegation.rb +40 -11
  42. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  43. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  44. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  45. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  46. data/lib/active_support/core_ext/numeric.rb +0 -1
  47. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  48. data/lib/active_support/core_ext/object/duplicable.rb +15 -24
  49. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  50. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  51. data/lib/active_support/core_ext/object/json.rb +14 -8
  52. data/lib/active_support/core_ext/object/with.rb +44 -0
  53. data/lib/active_support/core_ext/object/with_options.rb +4 -4
  54. data/lib/active_support/core_ext/object.rb +1 -0
  55. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  56. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  57. data/lib/active_support/core_ext/pathname.rb +1 -0
  58. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  59. data/lib/active_support/core_ext/range/overlap.rb +12 -0
  60. data/lib/active_support/core_ext/range.rb +1 -2
  61. data/lib/active_support/core_ext/securerandom.rb +24 -12
  62. data/lib/active_support/core_ext/string/filters.rb +20 -14
  63. data/lib/active_support/core_ext/string/indent.rb +1 -1
  64. data/lib/active_support/core_ext/string/inflections.rb +16 -5
  65. data/lib/active_support/core_ext/string/output_safety.rb +38 -174
  66. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  67. data/lib/active_support/core_ext/time/calculations.rb +18 -2
  68. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  69. data/lib/active_support/core_ext/time/zones.rb +4 -4
  70. data/lib/active_support/core_ext/time.rb +0 -1
  71. data/lib/active_support/current_attributes.rb +15 -6
  72. data/lib/active_support/dependencies/autoload.rb +17 -12
  73. data/lib/active_support/deprecation/behaviors.rb +55 -34
  74. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  75. data/lib/active_support/deprecation/deprecators.rb +104 -0
  76. data/lib/active_support/deprecation/disallowed.rb +3 -5
  77. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  78. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  79. data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
  80. data/lib/active_support/deprecation/reporting.rb +40 -29
  81. data/lib/active_support/deprecation.rb +32 -5
  82. data/lib/active_support/deprecator.rb +7 -0
  83. data/lib/active_support/descendants_tracker.rb +104 -132
  84. data/lib/active_support/duration/iso8601_serializer.rb +0 -2
  85. data/lib/active_support/duration.rb +2 -1
  86. data/lib/active_support/encrypted_configuration.rb +30 -9
  87. data/lib/active_support/encrypted_file.rb +8 -3
  88. data/lib/active_support/environment_inquirer.rb +22 -2
  89. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  90. data/lib/active_support/error_reporter.rb +121 -35
  91. data/lib/active_support/execution_wrapper.rb +4 -4
  92. data/lib/active_support/file_update_checker.rb +4 -2
  93. data/lib/active_support/fork_tracker.rb +10 -2
  94. data/lib/active_support/gem_version.rb +4 -4
  95. data/lib/active_support/gzip.rb +2 -0
  96. data/lib/active_support/hash_with_indifferent_access.rb +35 -17
  97. data/lib/active_support/i18n.rb +1 -1
  98. data/lib/active_support/i18n_railtie.rb +20 -13
  99. data/lib/active_support/inflector/inflections.rb +2 -0
  100. data/lib/active_support/inflector/methods.rb +23 -11
  101. data/lib/active_support/inflector/transliterate.rb +3 -1
  102. data/lib/active_support/isolated_execution_state.rb +26 -22
  103. data/lib/active_support/json/decoding.rb +2 -1
  104. data/lib/active_support/json/encoding.rb +25 -43
  105. data/lib/active_support/key_generator.rb +9 -1
  106. data/lib/active_support/lazy_load_hooks.rb +6 -4
  107. data/lib/active_support/locale/en.yml +2 -0
  108. data/lib/active_support/log_subscriber.rb +78 -33
  109. data/lib/active_support/logger.rb +1 -1
  110. data/lib/active_support/logger_thread_safe_level.rb +9 -22
  111. data/lib/active_support/message_encryptor.rb +197 -53
  112. data/lib/active_support/message_encryptors.rb +140 -0
  113. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  114. data/lib/active_support/message_pack/extensions.rb +292 -0
  115. data/lib/active_support/message_pack/serializer.rb +63 -0
  116. data/lib/active_support/message_pack.rb +50 -0
  117. data/lib/active_support/message_verifier.rb +212 -93
  118. data/lib/active_support/message_verifiers.rb +134 -0
  119. data/lib/active_support/messages/codec.rb +65 -0
  120. data/lib/active_support/messages/metadata.rb +111 -45
  121. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  122. data/lib/active_support/messages/rotator.rb +34 -32
  123. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  124. data/lib/active_support/multibyte/chars.rb +2 -0
  125. data/lib/active_support/multibyte/unicode.rb +9 -37
  126. data/lib/active_support/notifications/fanout.rb +239 -81
  127. data/lib/active_support/notifications/instrumenter.rb +79 -30
  128. data/lib/active_support/notifications.rb +1 -1
  129. data/lib/active_support/number_helper/number_converter.rb +5 -14
  130. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  131. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  132. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  133. data/lib/active_support/number_helper.rb +318 -379
  134. data/lib/active_support/ordered_hash.rb +3 -3
  135. data/lib/active_support/ordered_options.rb +14 -0
  136. data/lib/active_support/parameter_filter.rb +84 -69
  137. data/lib/active_support/proxy_object.rb +2 -0
  138. data/lib/active_support/railtie.rb +33 -21
  139. data/lib/active_support/reloader.rb +12 -4
  140. data/lib/active_support/rescuable.rb +2 -0
  141. data/lib/active_support/secure_compare_rotator.rb +16 -9
  142. data/lib/active_support/string_inquirer.rb +3 -1
  143. data/lib/active_support/subscriber.rb +9 -27
  144. data/lib/active_support/syntax_error_proxy.rb +49 -0
  145. data/lib/active_support/tagged_logging.rb +60 -24
  146. data/lib/active_support/test_case.rb +153 -6
  147. data/lib/active_support/testing/assertions.rb +26 -10
  148. data/lib/active_support/testing/autorun.rb +0 -2
  149. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  150. data/lib/active_support/testing/deprecation.rb +25 -25
  151. data/lib/active_support/testing/error_reporter_assertions.rb +108 -0
  152. data/lib/active_support/testing/isolation.rb +1 -1
  153. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  154. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  155. data/lib/active_support/testing/stream.rb +1 -1
  156. data/lib/active_support/testing/strict_warnings.rb +38 -0
  157. data/lib/active_support/testing/time_helpers.rb +32 -14
  158. data/lib/active_support/time_with_zone.rb +4 -14
  159. data/lib/active_support/values/time_zone.rb +9 -7
  160. data/lib/active_support/version.rb +1 -1
  161. data/lib/active_support/xml_mini/jdom.rb +3 -10
  162. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  163. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  164. data/lib/active_support/xml_mini/rexml.rb +1 -1
  165. data/lib/active_support/xml_mini.rb +2 -2
  166. data/lib/active_support.rb +13 -3
  167. metadata +48 -58
  168. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  169. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
  170. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
  171. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  172. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
  173. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
  174. data/lib/active_support/core_ext/range/overlaps.rb +0 -36
  175. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
  176. data/lib/active_support/core_ext/uri.rb +0 -5
  177. data/lib/active_support/per_thread_registry.rb +0 -65
@@ -17,6 +17,29 @@ module ActiveSupport
17
17
  end
18
18
  end
19
19
 
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
30
+
31
+ if exceptions
32
+ if exceptions.size == 1
33
+ raise exceptions.first
34
+ else
35
+ raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
36
+ end
37
+ end
38
+
39
+ listeners
40
+ end
41
+ end
42
+
20
43
  # This is a default queue implementation that ships with Notifications.
21
44
  # It just pushes events to all registered log subscribers.
22
45
  #
@@ -25,22 +48,29 @@ module ActiveSupport
25
48
  include Mutex_m
26
49
 
27
50
  def initialize
28
- @string_subscribers = Hash.new { |h, k| h[k] = [] }
51
+ @string_subscribers = Concurrent::Map.new { |h, k| h.compute_if_absent(k) { [] } }
29
52
  @other_subscribers = []
30
- @listeners_for = Concurrent::Map.new
53
+ @all_listeners_for = Concurrent::Map.new
54
+ @groups_for = Concurrent::Map.new
55
+ @silenceable_groups_for = Concurrent::Map.new
31
56
  super
32
57
  end
33
58
 
59
+ def inspect # :nodoc:
60
+ total_patterns = @string_subscribers.size + @other_subscribers.size
61
+ "#<#{self.class} (#{total_patterns} patterns)>"
62
+ end
63
+
34
64
  def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
35
65
  subscriber = Subscribers.new(pattern, callable || block, monotonic)
36
66
  synchronize do
37
67
  case pattern
38
68
  when String
39
69
  @string_subscribers[pattern] << subscriber
40
- @listeners_for.delete(pattern)
70
+ clear_cache(pattern)
41
71
  when NilClass, Regexp
42
72
  @other_subscribers << subscriber
43
- @listeners_for.clear
73
+ clear_cache
44
74
  else
45
75
  raise ArgumentError, "pattern must be specified as a String, Regexp or empty"
46
76
  end
@@ -53,69 +83,230 @@ module ActiveSupport
53
83
  case subscriber_or_name
54
84
  when String
55
85
  @string_subscribers[subscriber_or_name].clear
56
- @listeners_for.delete(subscriber_or_name)
86
+ clear_cache(subscriber_or_name)
57
87
  @other_subscribers.each { |sub| sub.unsubscribe!(subscriber_or_name) }
58
88
  else
59
89
  pattern = subscriber_or_name.try(:pattern)
60
90
  if String === pattern
61
91
  @string_subscribers[pattern].delete(subscriber_or_name)
62
- @listeners_for.delete(pattern)
92
+ clear_cache(pattern)
63
93
  else
64
94
  @other_subscribers.delete(subscriber_or_name)
65
- @listeners_for.clear
95
+ clear_cache
66
96
  end
67
97
  end
68
98
  end
69
99
  end
70
100
 
71
- def start(name, id, payload)
72
- iterate_guarding_exceptions(listeners_for(name)) { |s| s.start(name, id, payload) }
101
+ def clear_cache(key = nil) # :nodoc:
102
+ if key
103
+ @all_listeners_for.delete(key)
104
+ @groups_for.delete(key)
105
+ @silenceable_groups_for.delete(key)
106
+ else
107
+ @all_listeners_for.clear
108
+ @groups_for.clear
109
+ @silenceable_groups_for.clear
110
+ end
73
111
  end
74
112
 
75
- def finish(name, id, payload, listeners = listeners_for(name))
76
- iterate_guarding_exceptions(listeners) { |s| s.finish(name, id, payload) }
113
+ class BaseGroup # :nodoc:
114
+ include FanoutIteration
115
+
116
+ def initialize(listeners, name, id, payload)
117
+ @listeners = listeners
118
+ end
119
+
120
+ def each(&block)
121
+ iterate_guarding_exceptions(@listeners, &block)
122
+ end
77
123
  end
78
124
 
79
- def publish(name, *args)
80
- iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
125
+ class BaseTimeGroup < BaseGroup # :nodoc:
126
+ def start(name, id, payload)
127
+ @start_time = now
128
+ end
129
+
130
+ def finish(name, id, payload)
131
+ stop_time = now
132
+ each do |listener|
133
+ listener.call(name, @start_time, stop_time, id, payload)
134
+ end
135
+ end
81
136
  end
82
137
 
83
- def publish_event(event)
84
- iterate_guarding_exceptions(listeners_for(event.name)) { |s| s.publish_event(event) }
138
+ class MonotonicTimedGroup < BaseTimeGroup # :nodoc:
139
+ private
140
+ def now
141
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
142
+ end
85
143
  end
86
144
 
87
- def iterate_guarding_exceptions(listeners)
88
- exceptions = nil
145
+ class TimedGroup < BaseTimeGroup # :nodoc:
146
+ private
147
+ def now
148
+ Time.now
149
+ end
150
+ end
89
151
 
90
- listeners.each do |s|
91
- yield s
92
- rescue Exception => e
93
- exceptions ||= []
94
- exceptions << e
152
+ class EventedGroup < BaseGroup # :nodoc:
153
+ def start(name, id, payload)
154
+ each do |s|
155
+ s.start(name, id, payload)
156
+ end
95
157
  end
96
158
 
97
- if exceptions
98
- if exceptions.size == 1
99
- raise exceptions.first
100
- else
101
- raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
159
+ def finish(name, id, payload)
160
+ each do |s|
161
+ s.finish(name, id, payload)
102
162
  end
103
163
  end
164
+ end
104
165
 
105
- listeners
166
+ class EventObjectGroup < BaseGroup # :nodoc:
167
+ def start(name, id, payload)
168
+ @event = build_event(name, id, payload)
169
+ @event.start!
170
+ end
171
+
172
+ def finish(name, id, payload)
173
+ @event.payload = payload
174
+ @event.finish!
175
+
176
+ each do |s|
177
+ s.call(@event)
178
+ end
179
+ end
180
+
181
+ private
182
+ def build_event(name, id, payload)
183
+ ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
184
+ end
106
185
  end
107
186
 
108
- def listeners_for(name)
187
+ def groups_for(name) # :nodoc:
188
+ groups = @groups_for.compute_if_absent(name) do
189
+ all_listeners_for(name).reject(&:silenceable).group_by(&:group_class).transform_values do |s|
190
+ s.map(&:delegate)
191
+ end
192
+ end
193
+
194
+ silenceable_groups = @silenceable_groups_for.compute_if_absent(name) do
195
+ all_listeners_for(name).select(&:silenceable).group_by(&:group_class).transform_values do |s|
196
+ s.map(&:delegate)
197
+ end
198
+ end
199
+
200
+ unless silenceable_groups.empty?
201
+ groups = groups.dup
202
+ silenceable_groups.each do |group_class, subscriptions|
203
+ active_subscriptions = subscriptions.reject { |s| s.silenced?(name) }
204
+ unless active_subscriptions.empty?
205
+ groups[group_class] = (groups[group_class] || []) + active_subscriptions
206
+ end
207
+ end
208
+ end
209
+
210
+ groups
211
+ end
212
+
213
+ # A +Handle+ is used to record the start and finish time of event.
214
+ #
215
+ # Both #start and #finish must each be called exactly once.
216
+ #
217
+ # Where possible, it's best to use the block form: ActiveSupport::Notifications.instrument.
218
+ # +Handle+ is a low-level API intended for cases where the block form can't be used.
219
+ #
220
+ # handle = ActiveSupport::Notifications.instrumenter.build_handle("my.event", {})
221
+ # begin
222
+ # handle.start
223
+ # # work to be instrumented
224
+ # ensure
225
+ # handle.finish
226
+ # end
227
+ class Handle
228
+ def initialize(notifier, name, id, payload) # :nodoc:
229
+ @name = name
230
+ @id = id
231
+ @payload = payload
232
+ @groups = notifier.groups_for(name).map do |group_klass, grouped_listeners|
233
+ group_klass.new(grouped_listeners, name, id, payload)
234
+ end
235
+ @state = :initialized
236
+ end
237
+
238
+ def start
239
+ ensure_state! :initialized
240
+ @state = :started
241
+
242
+ @groups.each do |group|
243
+ group.start(@name, @id, @payload)
244
+ end
245
+ end
246
+
247
+ def finish
248
+ finish_with_values(@name, @id, @payload)
249
+ end
250
+
251
+ def finish_with_values(name, id, payload) # :nodoc:
252
+ ensure_state! :started
253
+ @state = :finished
254
+
255
+ @groups.each do |group|
256
+ group.finish(name, id, payload)
257
+ end
258
+ end
259
+
260
+ private
261
+ def ensure_state!(expected)
262
+ if @state != expected
263
+ raise ArgumentError, "expected state to be #{expected.inspect} but was #{@state.inspect}"
264
+ end
265
+ end
266
+ end
267
+
268
+ include FanoutIteration
269
+
270
+ def build_handle(name, id, payload)
271
+ Handle.new(self, name, id, payload)
272
+ end
273
+
274
+ def start(name, id, payload)
275
+ handle_stack = (IsolatedExecutionState[:_fanout_handle_stack] ||= [])
276
+ handle = build_handle(name, id, payload)
277
+ handle_stack << handle
278
+ handle.start
279
+ end
280
+
281
+ def finish(name, id, payload, listeners = nil)
282
+ handle_stack = IsolatedExecutionState[:_fanout_handle_stack]
283
+ handle = handle_stack.pop
284
+ handle.finish_with_values(name, id, payload)
285
+ end
286
+
287
+ def publish(name, *args)
288
+ iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
289
+ end
290
+
291
+ def publish_event(event)
292
+ iterate_guarding_exceptions(listeners_for(event.name)) { |s| s.publish_event(event) }
293
+ end
294
+
295
+ def all_listeners_for(name)
109
296
  # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
110
- @listeners_for[name] || synchronize do
297
+ @all_listeners_for[name] || synchronize do
111
298
  # use synchronisation when accessing @subscribers
112
- @listeners_for[name] ||=
299
+ @all_listeners_for[name] ||=
113
300
  @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
114
301
  end
115
302
  end
116
303
 
304
+ def listeners_for(name)
305
+ all_listeners_for(name).reject { |s| s.silenced?(name) }
306
+ end
307
+
117
308
  def listening?(name)
118
- listeners_for(name).any?
309
+ all_listeners_for(name).any? { |s| !s.silenced?(name) }
119
310
  end
120
311
 
121
312
  # This is a sync queue, so there is no waiting.
@@ -180,15 +371,20 @@ module ActiveSupport
180
371
  end
181
372
 
182
373
  class Evented # :nodoc:
183
- attr_reader :pattern
374
+ attr_reader :pattern, :delegate, :silenceable
184
375
 
185
376
  def initialize(pattern, delegate)
186
377
  @pattern = Matcher.wrap(pattern)
187
378
  @delegate = delegate
379
+ @silenceable = delegate.respond_to?(:silenced?)
188
380
  @can_publish = delegate.respond_to?(:publish)
189
381
  @can_publish_event = delegate.respond_to?(:publish_event)
190
382
  end
191
383
 
384
+ def group_class
385
+ EventedGroup
386
+ end
387
+
192
388
  def publish(name, *args)
193
389
  if @can_publish
194
390
  @delegate.publish name, *args
@@ -203,12 +399,8 @@ module ActiveSupport
203
399
  end
204
400
  end
205
401
 
206
- def start(name, id, payload)
207
- @delegate.start name, id, payload
208
- end
209
-
210
- def finish(name, id, payload)
211
- @delegate.finish name, id, payload
402
+ def silenced?(name)
403
+ @silenceable && @delegate.silenced?(name)
212
404
  end
213
405
 
214
406
  def subscribed_to?(name)
@@ -221,63 +413,29 @@ module ActiveSupport
221
413
  end
222
414
 
223
415
  class Timed < Evented # :nodoc:
224
- def publish(name, *args)
225
- @delegate.call name, *args
226
- end
227
-
228
- def start(name, id, payload)
229
- timestack = IsolatedExecutionState[:_timestack] ||= []
230
- timestack.push Time.now
231
- end
232
-
233
- def finish(name, id, payload)
234
- timestack = IsolatedExecutionState[:_timestack]
235
- started = timestack.pop
236
- @delegate.call(name, started, Time.now, id, payload)
416
+ def group_class
417
+ TimedGroup
237
418
  end
238
- end
239
419
 
240
- class MonotonicTimed < Evented # :nodoc:
241
420
  def publish(name, *args)
242
421
  @delegate.call name, *args
243
422
  end
423
+ end
244
424
 
245
- def start(name, id, payload)
246
- timestack = IsolatedExecutionState[:_timestack_monotonic] ||= []
247
- timestack.push Process.clock_gettime(Process::CLOCK_MONOTONIC)
248
- end
249
-
250
- def finish(name, id, payload)
251
- timestack = IsolatedExecutionState[:_timestack_monotonic]
252
- started = timestack.pop
253
- @delegate.call(name, started, Process.clock_gettime(Process::CLOCK_MONOTONIC), id, payload)
425
+ class MonotonicTimed < Timed # :nodoc:
426
+ def group_class
427
+ MonotonicTimedGroup
254
428
  end
255
429
  end
256
430
 
257
431
  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
263
- end
264
-
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
432
+ def group_class
433
+ EventObjectGroup
271
434
  end
272
435
 
273
436
  def publish_event(event)
274
437
  @delegate.call event
275
438
  end
276
-
277
- private
278
- def build_event(name, id, payload)
279
- ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
280
- end
281
439
  end
282
440
  end
283
441
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/module/delegation"
3
4
  require "securerandom"
4
5
 
5
6
  module ActiveSupport
@@ -9,17 +10,50 @@ module ActiveSupport
9
10
  attr_reader :id
10
11
 
11
12
  def initialize(notifier)
13
+ unless notifier.respond_to?(:build_handle)
14
+ notifier = LegacyHandle::Wrapper.new(notifier)
15
+ end
16
+
12
17
  @id = unique_id
13
18
  @notifier = notifier
14
19
  end
15
20
 
21
+ class LegacyHandle # :nodoc:
22
+ class Wrapper # :nodoc:
23
+ def initialize(notifier)
24
+ @notifier = notifier
25
+ end
26
+
27
+ def build_handle(name, id, payload)
28
+ LegacyHandle.new(@notifier, name, id, payload)
29
+ end
30
+
31
+ delegate :start, :finish, to: :@notifier
32
+ end
33
+
34
+ def initialize(notifier, name, id, payload)
35
+ @notifier = notifier
36
+ @name = name
37
+ @id = id
38
+ @payload = payload
39
+ end
40
+
41
+ def start
42
+ @listener_state = @notifier.start @name, @id, @payload
43
+ end
44
+
45
+ def finish
46
+ @notifier.finish(@name, @id, @payload, @listener_state)
47
+ end
48
+ end
49
+
16
50
  # Given a block, instrument it by measuring the time taken to execute
17
51
  # and publish it. Without a block, simply send a message via the
18
52
  # notifier. Notice that events get sent even if an error occurs in the
19
53
  # passed-in block.
20
54
  def instrument(name, payload = {})
21
- # some of the listeners might have state
22
- listeners_state = start name, payload
55
+ handle = build_handle(name, payload)
56
+ handle.start
23
57
  begin
24
58
  yield payload if block_given?
25
59
  rescue Exception => e
@@ -27,10 +61,24 @@ module ActiveSupport
27
61
  payload[:exception_object] = e
28
62
  raise e
29
63
  ensure
30
- finish_with_state listeners_state, name, payload
64
+ handle.finish
31
65
  end
32
66
  end
33
67
 
68
+ # Returns a "handle" for an event with the given +name+ and +payload+
69
+ #
70
+ # +#start+ and +#finish+ must each be called exactly once on the returned object.
71
+ #
72
+ # Where possible, it's best to use +#instrument+, which will record the
73
+ # start and finish of the event and correctly handle any exceptions.
74
+ # +build_handle+ is a low-level API intended for cases where using
75
+ # +#instrument+ isn't possible.
76
+ #
77
+ # See ActiveSupport::Notifications::Fanout::Handle
78
+ def build_handle(name, payload)
79
+ @notifier.build_handle(name, @id, payload)
80
+ end
81
+
34
82
  def new_event(name, payload = {}) # :nodoc:
35
83
  Event.new(name, nil, nil, @id, payload)
36
84
  end
@@ -56,7 +104,7 @@ module ActiveSupport
56
104
  end
57
105
 
58
106
  class Event
59
- attr_reader :name, :transaction_id, :children
107
+ attr_reader :name, :time, :end, :transaction_id
60
108
  attr_accessor :payload
61
109
 
62
110
  def initialize(name, start, ending, transaction_id, payload)
@@ -65,22 +113,13 @@ module ActiveSupport
65
113
  @time = start ? start.to_f * 1_000.0 : start
66
114
  @transaction_id = transaction_id
67
115
  @end = ending ? ending.to_f * 1_000.0 : ending
68
- @children = []
69
116
  @cpu_time_start = 0.0
70
117
  @cpu_time_finish = 0.0
71
118
  @allocation_count_start = 0
72
119
  @allocation_count_finish = 0
73
120
  end
74
121
 
75
- def time
76
- @time / 1000.0 if @time
77
- end
78
-
79
- def end
80
- @end / 1000.0 if @end
81
- end
82
-
83
- def record # :nodoc:
122
+ def record
84
123
  start!
85
124
  begin
86
125
  yield payload if block_given?
@@ -107,24 +146,42 @@ module ActiveSupport
107
146
  @allocation_count_finish = now_allocations
108
147
  end
109
148
 
110
- # Returns the CPU time (in milliseconds) passed between the call to
111
- # #start! and the call to #finish!.
149
+ # Returns the CPU time (in milliseconds) passed since the call to
150
+ # +start!+ and the call to +finish!+
112
151
  def cpu_time
113
152
  @cpu_time_finish - @cpu_time_start
114
153
  end
115
154
 
116
- # Returns the idle time time (in milliseconds) passed between the call to
117
- # #start! and the call to #finish!.
155
+ # Returns the idle time time (in milliseconds) passed since the call to
156
+ # +start!+ and the call to +finish!+
118
157
  def idle_time
119
- duration - cpu_time
158
+ diff = duration - cpu_time
159
+ diff > 0.0 ? diff : 0.0
120
160
  end
121
161
 
122
- # Returns the number of allocations made between the call to #start! and
123
- # the call to #finish!.
162
+ # Returns the number of allocations made since the call to +start!+ and
163
+ # the call to +finish!+
124
164
  def allocations
125
165
  @allocation_count_finish - @allocation_count_start
126
166
  end
127
167
 
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)
183
+ end
184
+
128
185
  # Returns the difference in milliseconds between when the execution of the
129
186
  # event started and when it ended.
130
187
  #
@@ -138,15 +195,7 @@ module ActiveSupport
138
195
  #
139
196
  # @event.duration # => 1000.138
140
197
  def duration
141
- @end - @time
142
- end
143
-
144
- def <<(event)
145
- @children << event
146
- end
147
-
148
- def parent_of?(event)
149
- @children.include? event
198
+ self.end - time
150
199
  end
151
200
 
152
201
  private
@@ -6,7 +6,7 @@ require "active_support/notifications/fanout"
6
6
  module ActiveSupport
7
7
  # = \Notifications
8
8
  #
9
- # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
9
+ # +ActiveSupport::Notifications+ provides an instrumentation API for
10
10
  # Ruby.
11
11
  #
12
12
  # == Instrumenters
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bigdecimal"
4
- require "bigdecimal/util"
5
3
  require "active_support/core_ext/big_decimal/conversions"
6
- require "active_support/core_ext/object/blank"
7
4
  require "active_support/core_ext/hash/keys"
8
5
  require "active_support/i18n"
9
6
  require "active_support/core_ext/class/attribute"
@@ -124,13 +121,14 @@ module ActiveSupport
124
121
 
125
122
  def initialize(number, options)
126
123
  @number = number
127
- @opts = options.symbolize_keys
124
+ @opts = options.symbolize_keys
125
+ @options = nil
128
126
  end
129
127
 
130
128
  def execute
131
129
  if !number
132
130
  nil
133
- elsif validate_float? && !valid_bigdecimal
131
+ elsif validate_float? && !valid_float?
134
132
  number
135
133
  else
136
134
  convert
@@ -175,15 +173,8 @@ module ActiveSupport
175
173
  key.split(".").reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
176
174
  end
177
175
 
178
- def valid_bigdecimal
179
- case number
180
- when Float, Rational
181
- number.to_d(0)
182
- when String
183
- BigDecimal(number, exception: false)
184
- else
185
- number.to_d rescue nil
186
- end
176
+ def valid_float?
177
+ Float(number, exception: false)
187
178
  end
188
179
  end
189
180
  end
@@ -10,13 +10,13 @@ module ActiveSupport
10
10
  def convert
11
11
  format = options[:format]
12
12
 
13
- number_d = valid_bigdecimal
14
- if number_d
15
- if number_d.negative?
16
- number_d = number_d.abs
17
- format = options[:negative_format] if (number_d * 10**options[:precision]) >= 0.5
13
+ number_f = valid_float?
14
+ if number_f
15
+ if number_f.negative?
16
+ number_f = number_f.abs
17
+ format = options[:negative_format] if (number_f * 10**options[:precision]) >= 0.5
18
18
  end
19
- number_s = NumberToRoundedConverter.convert(number_d, options)
19
+ number_s = NumberToRoundedConverter.convert(number_f, options)
20
20
  else
21
21
  number_s = number.to_s.strip
22
22
  format = options[:negative_format] if number_s.sub!(/^-/, "")