activesupport 7.0.9 → 7.1.6

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 +1007 -326
  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 +30 -5
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/broadcast_logger.rb +251 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +36 -9
  14. data/lib/active_support/cache/mem_cache_store.rb +100 -76
  15. data/lib/active_support/cache/memory_store.rb +75 -23
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +151 -141
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +29 -14
  20. data/lib/active_support/cache.rb +333 -253
  21. data/lib/active_support/callbacks.rb +44 -21
  22. data/lib/active_support/code_generator.rb +15 -10
  23. data/lib/active_support/concern.rb +4 -2
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/configurable.rb +10 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +2 -1
  28. data/lib/active_support/core_ext/array.rb +0 -1
  29. data/lib/active_support/core_ext/class/subclasses.rb +13 -10
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date.rb +0 -1
  32. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  33. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  34. data/lib/active_support/core_ext/date_time.rb +0 -1
  35. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  36. data/lib/active_support/core_ext/enumerable.rb +3 -75
  37. data/lib/active_support/core_ext/erb/util.rb +196 -0
  38. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  39. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  40. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  41. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  42. data/lib/active_support/core_ext/module/delegation.rb +81 -37
  43. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  44. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  45. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  46. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  47. data/lib/active_support/core_ext/numeric.rb +0 -1
  48. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  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 +10 -2
  52. data/lib/active_support/core_ext/object/with.rb +44 -0
  53. data/lib/active_support/core_ext/object/with_options.rb +3 -3
  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 +40 -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/inflections.rb +16 -5
  64. data/lib/active_support/core_ext/string/output_safety.rb +38 -174
  65. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  66. data/lib/active_support/core_ext/time/calculations.rb +18 -2
  67. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  68. data/lib/active_support/core_ext/time/zones.rb +4 -4
  69. data/lib/active_support/core_ext/time.rb +0 -1
  70. data/lib/active_support/current_attributes.rb +15 -6
  71. data/lib/active_support/deep_mergeable.rb +53 -0
  72. data/lib/active_support/dependencies/autoload.rb +17 -12
  73. data/lib/active_support/deprecation/behaviors.rb +65 -42
  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 +35 -21
  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 +3 -3
  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/html_safe_translation.rb +16 -6
  98. data/lib/active_support/i18n.rb +1 -1
  99. data/lib/active_support/i18n_railtie.rb +20 -13
  100. data/lib/active_support/inflector/inflections.rb +2 -0
  101. data/lib/active_support/inflector/methods.rb +22 -10
  102. data/lib/active_support/inflector/transliterate.rb +3 -1
  103. data/lib/active_support/isolated_execution_state.rb +26 -22
  104. data/lib/active_support/json/decoding.rb +2 -1
  105. data/lib/active_support/json/encoding.rb +25 -43
  106. data/lib/active_support/key_generator.rb +9 -1
  107. data/lib/active_support/lazy_load_hooks.rb +6 -4
  108. data/lib/active_support/locale/en.yml +2 -0
  109. data/lib/active_support/log_subscriber.rb +85 -33
  110. data/lib/active_support/logger.rb +9 -60
  111. data/lib/active_support/logger_thread_safe_level.rb +9 -24
  112. data/lib/active_support/message_encryptor.rb +197 -53
  113. data/lib/active_support/message_encryptors.rb +141 -0
  114. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  115. data/lib/active_support/message_pack/extensions.rb +292 -0
  116. data/lib/active_support/message_pack/serializer.rb +63 -0
  117. data/lib/active_support/message_pack.rb +50 -0
  118. data/lib/active_support/message_verifier.rb +212 -93
  119. data/lib/active_support/message_verifiers.rb +135 -0
  120. data/lib/active_support/messages/codec.rb +65 -0
  121. data/lib/active_support/messages/metadata.rb +111 -45
  122. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  123. data/lib/active_support/messages/rotator.rb +34 -32
  124. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  125. data/lib/active_support/multibyte/chars.rb +2 -0
  126. data/lib/active_support/multibyte/unicode.rb +9 -37
  127. data/lib/active_support/notifications/fanout.rb +245 -81
  128. data/lib/active_support/notifications/instrumenter.rb +71 -14
  129. data/lib/active_support/notifications.rb +1 -1
  130. data/lib/active_support/number_helper/number_converter.rb +2 -2
  131. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  132. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  133. data/lib/active_support/ordered_hash.rb +3 -3
  134. data/lib/active_support/ordered_options.rb +14 -0
  135. data/lib/active_support/parameter_filter.rb +84 -69
  136. data/lib/active_support/proxy_object.rb +2 -0
  137. data/lib/active_support/railtie.rb +33 -21
  138. data/lib/active_support/reloader.rb +12 -4
  139. data/lib/active_support/rescuable.rb +2 -0
  140. data/lib/active_support/secure_compare_rotator.rb +16 -9
  141. data/lib/active_support/string_inquirer.rb +3 -1
  142. data/lib/active_support/subscriber.rb +9 -27
  143. data/lib/active_support/syntax_error_proxy.rb +60 -0
  144. data/lib/active_support/tagged_logging.rb +64 -24
  145. data/lib/active_support/test_case.rb +153 -6
  146. data/lib/active_support/testing/assertions.rb +25 -9
  147. data/lib/active_support/testing/autorun.rb +0 -2
  148. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  149. data/lib/active_support/testing/deprecation.rb +25 -25
  150. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  151. data/lib/active_support/testing/isolation.rb +1 -1
  152. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  153. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  154. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  155. data/lib/active_support/testing/stream.rb +1 -1
  156. data/lib/active_support/testing/strict_warnings.rb +39 -0
  157. data/lib/active_support/testing/time_helpers.rb +37 -15
  158. data/lib/active_support/time_with_zone.rb +4 -14
  159. data/lib/active_support/values/time_zone.rb +18 -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 +14 -3
  167. metadata +45 -14
  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,33 @@ module ActiveSupport
17
17
  end
18
18
  end
19
19
 
20
+ module FanoutIteration # :nodoc:
21
+ private
22
+ def iterate_guarding_exceptions(collection)
23
+ exceptions = nil
24
+
25
+ collection.each do |s|
26
+ yield s
27
+ rescue Exception => e
28
+ exceptions ||= []
29
+ exceptions << e
30
+ end
31
+
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
45
+ end
46
+
20
47
  # This is a default queue implementation that ships with Notifications.
21
48
  # It just pushes events to all registered log subscribers.
22
49
  #
@@ -25,22 +52,29 @@ module ActiveSupport
25
52
  include Mutex_m
26
53
 
27
54
  def initialize
28
- @string_subscribers = Hash.new { |h, k| h[k] = [] }
55
+ @string_subscribers = Concurrent::Map.new { |h, k| h.compute_if_absent(k) { [] } }
29
56
  @other_subscribers = []
30
- @listeners_for = Concurrent::Map.new
57
+ @all_listeners_for = Concurrent::Map.new
58
+ @groups_for = Concurrent::Map.new
59
+ @silenceable_groups_for = Concurrent::Map.new
31
60
  super
32
61
  end
33
62
 
63
+ def inspect # :nodoc:
64
+ total_patterns = @string_subscribers.size + @other_subscribers.size
65
+ "#<#{self.class} (#{total_patterns} patterns)>"
66
+ end
67
+
34
68
  def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
35
69
  subscriber = Subscribers.new(pattern, callable || block, monotonic)
36
70
  synchronize do
37
71
  case pattern
38
72
  when String
39
73
  @string_subscribers[pattern] << subscriber
40
- @listeners_for.delete(pattern)
74
+ clear_cache(pattern)
41
75
  when NilClass, Regexp
42
76
  @other_subscribers << subscriber
43
- @listeners_for.clear
77
+ clear_cache
44
78
  else
45
79
  raise ArgumentError, "pattern must be specified as a String, Regexp or empty"
46
80
  end
@@ -53,69 +87,232 @@ module ActiveSupport
53
87
  case subscriber_or_name
54
88
  when String
55
89
  @string_subscribers[subscriber_or_name].clear
56
- @listeners_for.delete(subscriber_or_name)
90
+ clear_cache(subscriber_or_name)
57
91
  @other_subscribers.each { |sub| sub.unsubscribe!(subscriber_or_name) }
58
92
  else
59
93
  pattern = subscriber_or_name.try(:pattern)
60
94
  if String === pattern
61
95
  @string_subscribers[pattern].delete(subscriber_or_name)
62
- @listeners_for.delete(pattern)
96
+ clear_cache(pattern)
63
97
  else
64
98
  @other_subscribers.delete(subscriber_or_name)
65
- @listeners_for.clear
99
+ clear_cache
66
100
  end
67
101
  end
68
102
  end
69
103
  end
70
104
 
71
- def start(name, id, payload)
72
- iterate_guarding_exceptions(listeners_for(name)) { |s| s.start(name, id, payload) }
105
+ def clear_cache(key = nil) # :nodoc:
106
+ if key
107
+ @all_listeners_for.delete(key)
108
+ @groups_for.delete(key)
109
+ @silenceable_groups_for.delete(key)
110
+ else
111
+ @all_listeners_for.clear
112
+ @groups_for.clear
113
+ @silenceable_groups_for.clear
114
+ end
73
115
  end
74
116
 
75
- def finish(name, id, payload, listeners = listeners_for(name))
76
- iterate_guarding_exceptions(listeners) { |s| s.finish(name, id, payload) }
117
+ class BaseGroup # :nodoc:
118
+ include FanoutIteration
119
+
120
+ def initialize(listeners, name, id, payload)
121
+ @listeners = listeners
122
+ end
123
+
124
+ def each(&block)
125
+ iterate_guarding_exceptions(@listeners, &block)
126
+ end
77
127
  end
78
128
 
79
- def publish(name, *args)
80
- iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
129
+ class BaseTimeGroup < BaseGroup # :nodoc:
130
+ def start(name, id, payload)
131
+ @start_time = now
132
+ end
133
+
134
+ def finish(name, id, payload)
135
+ stop_time = now
136
+ each do |listener|
137
+ listener.call(name, @start_time, stop_time, id, payload)
138
+ end
139
+ end
81
140
  end
82
141
 
83
- def publish_event(event)
84
- iterate_guarding_exceptions(listeners_for(event.name)) { |s| s.publish_event(event) }
142
+ class MonotonicTimedGroup < BaseTimeGroup # :nodoc:
143
+ private
144
+ def now
145
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
146
+ end
85
147
  end
86
148
 
87
- def iterate_guarding_exceptions(listeners)
88
- exceptions = nil
149
+ class TimedGroup < BaseTimeGroup # :nodoc:
150
+ private
151
+ def now
152
+ Time.now
153
+ end
154
+ end
89
155
 
90
- listeners.each do |s|
91
- yield s
92
- rescue Exception => e
93
- exceptions ||= []
94
- exceptions << e
156
+ class EventedGroup < BaseGroup # :nodoc:
157
+ def start(name, id, payload)
158
+ each do |s|
159
+ s.start(name, id, payload)
160
+ end
95
161
  end
96
162
 
97
- if exceptions
98
- if exceptions.size == 1
99
- raise exceptions.first
100
- else
101
- raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
163
+ def finish(name, id, payload)
164
+ each do |s|
165
+ s.finish(name, id, payload)
102
166
  end
103
167
  end
168
+ end
169
+
170
+ class EventObjectGroup < BaseGroup # :nodoc:
171
+ def start(name, id, payload)
172
+ @event = build_event(name, id, payload)
173
+ @event.start!
174
+ end
104
175
 
105
- listeners
176
+ def finish(name, id, payload)
177
+ @event.payload = payload
178
+ @event.finish!
179
+
180
+ each do |s|
181
+ s.call(@event)
182
+ end
183
+ end
184
+
185
+ private
186
+ def build_event(name, id, payload)
187
+ ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
188
+ end
106
189
  end
107
190
 
108
- def listeners_for(name)
191
+ def groups_for(name) # :nodoc:
192
+ groups = @groups_for.compute_if_absent(name) do
193
+ all_listeners_for(name).reject(&:silenceable).group_by(&:group_class).transform_values do |s|
194
+ s.map(&:delegate)
195
+ end
196
+ end
197
+
198
+ silenceable_groups = @silenceable_groups_for.compute_if_absent(name) do
199
+ all_listeners_for(name).select(&:silenceable).group_by(&:group_class).transform_values do |s|
200
+ s.map(&:delegate)
201
+ end
202
+ end
203
+
204
+ unless silenceable_groups.empty?
205
+ groups = groups.dup
206
+ silenceable_groups.each do |group_class, subscriptions|
207
+ active_subscriptions = subscriptions.reject { |s| s.silenced?(name) }
208
+ unless active_subscriptions.empty?
209
+ groups[group_class] = (groups[group_class] || []) + active_subscriptions
210
+ end
211
+ end
212
+ end
213
+
214
+ groups
215
+ end
216
+
217
+ # A +Handle+ is used to record the start and finish time of event.
218
+ #
219
+ # Both #start and #finish must each be called exactly once.
220
+ #
221
+ # Where possible, it's best to use the block form: ActiveSupport::Notifications.instrument.
222
+ # +Handle+ is a low-level API intended for cases where the block form can't be used.
223
+ #
224
+ # handle = ActiveSupport::Notifications.instrumenter.build_handle("my.event", {})
225
+ # begin
226
+ # handle.start
227
+ # # work to be instrumented
228
+ # ensure
229
+ # handle.finish
230
+ # end
231
+ class Handle
232
+ include FanoutIteration
233
+
234
+ def initialize(notifier, name, id, payload) # :nodoc:
235
+ @name = name
236
+ @id = id
237
+ @payload = payload
238
+ @groups = notifier.groups_for(name).map do |group_klass, grouped_listeners|
239
+ group_klass.new(grouped_listeners, name, id, payload)
240
+ end
241
+ @state = :initialized
242
+ end
243
+
244
+ def start
245
+ ensure_state! :initialized
246
+ @state = :started
247
+
248
+ iterate_guarding_exceptions(@groups) do |group|
249
+ group.start(@name, @id, @payload)
250
+ end
251
+ end
252
+
253
+ def finish
254
+ finish_with_values(@name, @id, @payload)
255
+ end
256
+
257
+ def finish_with_values(name, id, payload) # :nodoc:
258
+ ensure_state! :started
259
+ @state = :finished
260
+
261
+ iterate_guarding_exceptions(@groups) do |group|
262
+ group.finish(name, id, payload)
263
+ end
264
+ end
265
+
266
+ private
267
+ def ensure_state!(expected)
268
+ if @state != expected
269
+ raise ArgumentError, "expected state to be #{expected.inspect} but was #{@state.inspect}"
270
+ end
271
+ end
272
+ end
273
+
274
+ include FanoutIteration
275
+
276
+ def build_handle(name, id, payload)
277
+ Handle.new(self, name, id, payload)
278
+ end
279
+
280
+ def start(name, id, payload)
281
+ handle_stack = (IsolatedExecutionState[:_fanout_handle_stack] ||= [])
282
+ handle = build_handle(name, id, payload)
283
+ handle_stack << handle
284
+ handle.start
285
+ end
286
+
287
+ def finish(name, id, payload, listeners = nil)
288
+ handle_stack = IsolatedExecutionState[:_fanout_handle_stack]
289
+ handle = handle_stack.pop
290
+ handle.finish_with_values(name, id, payload)
291
+ end
292
+
293
+ def publish(name, *args)
294
+ iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
295
+ end
296
+
297
+ def publish_event(event)
298
+ iterate_guarding_exceptions(listeners_for(event.name)) { |s| s.publish_event(event) }
299
+ end
300
+
301
+ def all_listeners_for(name)
109
302
  # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
110
- @listeners_for[name] || synchronize do
303
+ @all_listeners_for[name] || synchronize do
111
304
  # use synchronisation when accessing @subscribers
112
- @listeners_for[name] ||=
305
+ @all_listeners_for[name] ||=
113
306
  @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
114
307
  end
115
308
  end
116
309
 
310
+ def listeners_for(name)
311
+ all_listeners_for(name).reject { |s| s.silenced?(name) }
312
+ end
313
+
117
314
  def listening?(name)
118
- listeners_for(name).any?
315
+ all_listeners_for(name).any? { |s| !s.silenced?(name) }
119
316
  end
120
317
 
121
318
  # This is a sync queue, so there is no waiting.
@@ -180,15 +377,20 @@ module ActiveSupport
180
377
  end
181
378
 
182
379
  class Evented # :nodoc:
183
- attr_reader :pattern
380
+ attr_reader :pattern, :delegate, :silenceable
184
381
 
185
382
  def initialize(pattern, delegate)
186
383
  @pattern = Matcher.wrap(pattern)
187
384
  @delegate = delegate
385
+ @silenceable = delegate.respond_to?(:silenced?)
188
386
  @can_publish = delegate.respond_to?(:publish)
189
387
  @can_publish_event = delegate.respond_to?(:publish_event)
190
388
  end
191
389
 
390
+ def group_class
391
+ EventedGroup
392
+ end
393
+
192
394
  def publish(name, *args)
193
395
  if @can_publish
194
396
  @delegate.publish name, *args
@@ -203,12 +405,8 @@ module ActiveSupport
203
405
  end
204
406
  end
205
407
 
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
408
+ def silenced?(name)
409
+ @silenceable && @delegate.silenced?(name)
212
410
  end
213
411
 
214
412
  def subscribed_to?(name)
@@ -221,63 +419,29 @@ module ActiveSupport
221
419
  end
222
420
 
223
421
  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
422
+ def group_class
423
+ TimedGroup
231
424
  end
232
425
 
233
- def finish(name, id, payload)
234
- timestack = IsolatedExecutionState[:_timestack]
235
- started = timestack.pop
236
- @delegate.call(name, started, Time.now, id, payload)
237
- end
238
- end
239
-
240
- class MonotonicTimed < Evented # :nodoc:
241
426
  def publish(name, *args)
242
427
  @delegate.call name, *args
243
428
  end
429
+ end
244
430
 
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)
431
+ class MonotonicTimed < Timed # :nodoc:
432
+ def group_class
433
+ MonotonicTimedGroup
254
434
  end
255
435
  end
256
436
 
257
437
  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
438
+ def group_class
439
+ EventObjectGroup
271
440
  end
272
441
 
273
442
  def publish_event(event)
274
443
  @delegate.call event
275
444
  end
276
-
277
- private
278
- def build_event(name, id, payload)
279
- ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
280
- end
281
445
  end
282
446
  end
283
447
  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, :transaction_id
60
108
  attr_accessor :payload
61
109
 
62
110
  def initialize(name, start, ending, transaction_id, payload)
@@ -65,7 +113,6 @@ 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
@@ -116,7 +163,8 @@ module ActiveSupport
116
163
  # Returns the idle time time (in milliseconds) passed between the call to
117
164
  # #start! and the call to #finish!.
118
165
  def idle_time
119
- duration - cpu_time
166
+ diff = duration - cpu_time
167
+ diff > 0.0 ? diff : 0.0
120
168
  end
121
169
 
122
170
  # Returns the number of allocations made between the call to #start! and
@@ -125,6 +173,23 @@ module ActiveSupport
125
173
  @allocation_count_finish - @allocation_count_start
126
174
  end
127
175
 
176
+ def children # :nodoc:
177
+ ActiveSupport.deprecator.warn <<~EOM
178
+ ActiveSupport::Notifications::Event#children is deprecated and will
179
+ be removed in Rails 7.2.
180
+ EOM
181
+ []
182
+ end
183
+
184
+ def parent_of?(event) # :nodoc:
185
+ ActiveSupport.deprecator.warn <<~EOM
186
+ ActiveSupport::Notifications::Event#parent_of? is deprecated and will
187
+ be removed in Rails 7.2.
188
+ EOM
189
+ start = (time - event.time) * 1000
190
+ start <= 0 && (start + duration >= event.duration)
191
+ end
192
+
128
193
  # Returns the difference in milliseconds between when the execution of the
129
194
  # event started and when it ended.
130
195
  #
@@ -141,14 +206,6 @@ module ActiveSupport
141
206
  @end - @time
142
207
  end
143
208
 
144
- def <<(event)
145
- @children << event
146
- end
147
-
148
- def parent_of?(event)
149
- @children.include? event
150
- end
151
-
152
209
  private
153
210
  def now
154
211
  Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
@@ -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
@@ -3,7 +3,6 @@
3
3
  require "bigdecimal"
4
4
  require "bigdecimal/util"
5
5
  require "active_support/core_ext/big_decimal/conversions"
6
- require "active_support/core_ext/object/blank"
7
6
  require "active_support/core_ext/hash/keys"
8
7
  require "active_support/i18n"
9
8
  require "active_support/core_ext/class/attribute"
@@ -124,7 +123,8 @@ module ActiveSupport
124
123
 
125
124
  def initialize(number, options)
126
125
  @number = number
127
- @opts = options.symbolize_keys
126
+ @opts = options.symbolize_keys
127
+ @options = nil
128
128
  end
129
129
 
130
130
  def execute
@@ -5,7 +5,7 @@ require "active_support/number_helper/number_converter"
5
5
  module ActiveSupport
6
6
  module NumberHelper
7
7
  class NumberToHumanSizeConverter < NumberConverter # :nodoc:
8
- STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb]
8
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb, :zb]
9
9
 
10
10
  self.namespace = :human
11
11
  self.validate_float = true
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/object/blank"
3
4
  require "active_support/number_helper/number_converter"
4
5
 
5
6
  module ActiveSupport
@@ -7,7 +7,7 @@ YAML.add_builtin_type("omap") do |type, val|
7
7
  end
8
8
 
9
9
  module ActiveSupport
10
- # DEPRECATED: <tt>ActiveSupport::OrderedHash</tt> implements a hash that preserves
10
+ # DEPRECATED: +ActiveSupport::OrderedHash+ implements a hash that preserves
11
11
  # insertion order.
12
12
  #
13
13
  # oh = ActiveSupport::OrderedHash.new
@@ -17,9 +17,9 @@ module ActiveSupport
17
17
  #
18
18
  # Also, maps the +omap+ feature for YAML files
19
19
  # (See https://yaml.org/type/omap.html) to support ordered items
20
- # when loading from yaml.
20
+ # when loading from YAML.
21
21
  #
22
- # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
22
+ # +ActiveSupport::OrderedHash+ is namespaced to prevent conflicts
23
23
  # with other implementations.
24
24
  class OrderedHash < ::Hash # :nodoc:
25
25
  def to_yaml_type
@@ -3,6 +3,8 @@
3
3
  require "active_support/core_ext/object/blank"
4
4
 
5
5
  module ActiveSupport
6
+ # = Ordered Options
7
+ #
6
8
  # +OrderedOptions+ inherits from +Hash+ and provides dynamic accessor methods.
7
9
  #
8
10
  # With a +Hash+, key-value pairs are typically managed like this:
@@ -40,6 +42,10 @@ module ActiveSupport
40
42
  super(key.to_sym)
41
43
  end
42
44
 
45
+ def dig(key, *identifiers)
46
+ super(key.to_sym, *identifiers)
47
+ end
48
+
43
49
  def method_missing(name, *args)
44
50
  name_string = +name.to_s
45
51
  if name_string.chomp!("=")
@@ -68,6 +74,8 @@ module ActiveSupport
68
74
  end
69
75
  end
70
76
 
77
+ # = Inheritable Options
78
+ #
71
79
  # +InheritableOptions+ provides a constructor to build an OrderedOptions
72
80
  # hash inherited from another hash.
73
81
  #
@@ -76,6 +84,12 @@ module ActiveSupport
76
84
  # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
77
85
  # h.girl # => 'Mary'
78
86
  # h.boy # => 'John'
87
+ #
88
+ # If the existing hash has string keys, call Hash#symbolize_keys on it.
89
+ #
90
+ # h = ActiveSupport::InheritableOptions.new({ 'girl' => 'Mary', 'boy' => 'John' }.symbolize_keys)
91
+ # h.girl # => 'Mary'
92
+ # h.boy # => 'John'
79
93
  class InheritableOptions < OrderedOptions
80
94
  def initialize(parent = nil)
81
95
  if parent.kind_of?(OrderedOptions)