activesupport 7.0.4 → 7.1.5.1

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1076 -230
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -6
  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 +37 -10
  14. data/lib/active_support/cache/mem_cache_store.rb +100 -76
  15. data/lib/active_support/cache/memory_store.rb +78 -24
  16. data/lib/active_support/cache/null_store.rb +6 -0
  17. data/lib/active_support/cache/redis_cache_store.rb +153 -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/calculations.rb +15 -0
  31. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  32. data/lib/active_support/core_ext/date.rb +0 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  34. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  35. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  36. data/lib/active_support/core_ext/date_time.rb +0 -1
  37. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  38. data/lib/active_support/core_ext/enumerable.rb +8 -75
  39. data/lib/active_support/core_ext/erb/util.rb +196 -0
  40. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  41. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  42. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  43. data/lib/active_support/core_ext/hash/keys.rb +3 -3
  44. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  45. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  46. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  47. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  48. data/lib/active_support/core_ext/module/delegation.rb +81 -37
  49. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  50. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  51. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  52. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  53. data/lib/active_support/core_ext/numeric.rb +0 -1
  54. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  55. data/lib/active_support/core_ext/object/duplicable.rb +25 -16
  56. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  57. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  58. data/lib/active_support/core_ext/object/json.rb +16 -6
  59. data/lib/active_support/core_ext/object/to_query.rb +0 -2
  60. data/lib/active_support/core_ext/object/with.rb +44 -0
  61. data/lib/active_support/core_ext/object/with_options.rb +9 -9
  62. data/lib/active_support/core_ext/object.rb +1 -0
  63. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  64. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  65. data/lib/active_support/core_ext/pathname.rb +1 -0
  66. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  67. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  68. data/lib/active_support/core_ext/range.rb +1 -2
  69. data/lib/active_support/core_ext/securerandom.rb +24 -12
  70. data/lib/active_support/core_ext/string/filters.rb +20 -14
  71. data/lib/active_support/core_ext/string/indent.rb +1 -1
  72. data/lib/active_support/core_ext/string/inflections.rb +16 -9
  73. data/lib/active_support/core_ext/string/output_safety.rb +42 -174
  74. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  75. data/lib/active_support/core_ext/time/calculations.rb +22 -2
  76. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  77. data/lib/active_support/core_ext/time/zones.rb +7 -8
  78. data/lib/active_support/core_ext/time.rb +0 -1
  79. data/lib/active_support/current_attributes.rb +15 -6
  80. data/lib/active_support/deep_mergeable.rb +53 -0
  81. data/lib/active_support/dependencies/autoload.rb +17 -12
  82. data/lib/active_support/deprecation/behaviors.rb +65 -42
  83. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  84. data/lib/active_support/deprecation/deprecators.rb +104 -0
  85. data/lib/active_support/deprecation/disallowed.rb +6 -8
  86. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  87. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  88. data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
  89. data/lib/active_support/deprecation/reporting.rb +43 -26
  90. data/lib/active_support/deprecation.rb +32 -5
  91. data/lib/active_support/deprecator.rb +7 -0
  92. data/lib/active_support/descendants_tracker.rb +104 -132
  93. data/lib/active_support/duration/iso8601_serializer.rb +0 -2
  94. data/lib/active_support/duration.rb +2 -1
  95. data/lib/active_support/encrypted_configuration.rb +63 -11
  96. data/lib/active_support/encrypted_file.rb +16 -12
  97. data/lib/active_support/environment_inquirer.rb +22 -2
  98. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  99. data/lib/active_support/error_reporter.rb +121 -35
  100. data/lib/active_support/evented_file_update_checker.rb +17 -2
  101. data/lib/active_support/execution_wrapper.rb +4 -4
  102. data/lib/active_support/file_update_checker.rb +4 -2
  103. data/lib/active_support/fork_tracker.rb +10 -2
  104. data/lib/active_support/gem_version.rb +4 -4
  105. data/lib/active_support/gzip.rb +2 -0
  106. data/lib/active_support/hash_with_indifferent_access.rb +35 -17
  107. data/lib/active_support/html_safe_translation.rb +16 -6
  108. data/lib/active_support/i18n.rb +1 -1
  109. data/lib/active_support/i18n_railtie.rb +20 -13
  110. data/lib/active_support/inflector/inflections.rb +2 -0
  111. data/lib/active_support/inflector/methods.rb +28 -18
  112. data/lib/active_support/inflector/transliterate.rb +3 -1
  113. data/lib/active_support/isolated_execution_state.rb +26 -22
  114. data/lib/active_support/json/decoding.rb +2 -1
  115. data/lib/active_support/json/encoding.rb +25 -43
  116. data/lib/active_support/key_generator.rb +9 -1
  117. data/lib/active_support/lazy_load_hooks.rb +7 -5
  118. data/lib/active_support/locale/en.yml +2 -0
  119. data/lib/active_support/log_subscriber.rb +85 -33
  120. data/lib/active_support/logger.rb +9 -60
  121. data/lib/active_support/logger_thread_safe_level.rb +10 -24
  122. data/lib/active_support/message_encryptor.rb +197 -53
  123. data/lib/active_support/message_encryptors.rb +141 -0
  124. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  125. data/lib/active_support/message_pack/extensions.rb +292 -0
  126. data/lib/active_support/message_pack/serializer.rb +63 -0
  127. data/lib/active_support/message_pack.rb +50 -0
  128. data/lib/active_support/message_verifier.rb +212 -93
  129. data/lib/active_support/message_verifiers.rb +135 -0
  130. data/lib/active_support/messages/codec.rb +65 -0
  131. data/lib/active_support/messages/metadata.rb +111 -45
  132. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  133. data/lib/active_support/messages/rotator.rb +34 -32
  134. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  135. data/lib/active_support/multibyte/chars.rb +2 -0
  136. data/lib/active_support/multibyte/unicode.rb +9 -37
  137. data/lib/active_support/notifications/fanout.rb +245 -81
  138. data/lib/active_support/notifications/instrumenter.rb +87 -22
  139. data/lib/active_support/notifications.rb +3 -3
  140. data/lib/active_support/number_helper/number_converter.rb +14 -5
  141. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
  142. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
  143. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  144. data/lib/active_support/number_helper.rb +379 -317
  145. data/lib/active_support/ordered_hash.rb +3 -3
  146. data/lib/active_support/ordered_options.rb +14 -0
  147. data/lib/active_support/parameter_filter.rb +103 -84
  148. data/lib/active_support/proxy_object.rb +2 -0
  149. data/lib/active_support/railtie.rb +33 -21
  150. data/lib/active_support/reloader.rb +12 -4
  151. data/lib/active_support/rescuable.rb +2 -0
  152. data/lib/active_support/secure_compare_rotator.rb +16 -9
  153. data/lib/active_support/string_inquirer.rb +3 -1
  154. data/lib/active_support/subscriber.rb +9 -27
  155. data/lib/active_support/syntax_error_proxy.rb +60 -0
  156. data/lib/active_support/tagged_logging.rb +64 -24
  157. data/lib/active_support/test_case.rb +153 -6
  158. data/lib/active_support/testing/assertions.rb +26 -10
  159. data/lib/active_support/testing/autorun.rb +0 -2
  160. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  161. data/lib/active_support/testing/deprecation.rb +25 -25
  162. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  163. data/lib/active_support/testing/isolation.rb +29 -28
  164. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  165. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  166. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  167. data/lib/active_support/testing/stream.rb +1 -1
  168. data/lib/active_support/testing/strict_warnings.rb +39 -0
  169. data/lib/active_support/testing/time_helpers.rb +37 -15
  170. data/lib/active_support/time_with_zone.rb +8 -37
  171. data/lib/active_support/values/time_zone.rb +18 -7
  172. data/lib/active_support/version.rb +1 -1
  173. data/lib/active_support/xml_mini/jdom.rb +3 -10
  174. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  175. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  176. data/lib/active_support/xml_mini/rexml.rb +1 -1
  177. data/lib/active_support/xml_mini.rb +2 -2
  178. data/lib/active_support.rb +14 -3
  179. metadata +148 -19
  180. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  181. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -26
  182. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -22
  183. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  184. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -26
  185. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -7
  186. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  187. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -22
  188. data/lib/active_support/core_ext/uri.rb +0 -5
  189. 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, :time, :end, :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,14 +113,21 @@ 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 record
122
+ def time
123
+ @time / 1000.0 if @time
124
+ end
125
+
126
+ def end
127
+ @end / 1000.0 if @end
128
+ end
129
+
130
+ def record # :nodoc:
76
131
  start!
77
132
  begin
78
133
  yield payload if block_given?
@@ -99,24 +154,42 @@ module ActiveSupport
99
154
  @allocation_count_finish = now_allocations
100
155
  end
101
156
 
102
- # Returns the CPU time (in milliseconds) passed since the call to
103
- # +start!+ and the call to +finish!+
157
+ # Returns the CPU time (in milliseconds) passed between the call to
158
+ # #start! and the call to #finish!.
104
159
  def cpu_time
105
160
  @cpu_time_finish - @cpu_time_start
106
161
  end
107
162
 
108
- # Returns the idle time time (in milliseconds) passed since the call to
109
- # +start!+ and the call to +finish!+
163
+ # Returns the idle time time (in milliseconds) passed between the call to
164
+ # #start! and the call to #finish!.
110
165
  def idle_time
111
- duration - cpu_time
166
+ diff = duration - cpu_time
167
+ diff > 0.0 ? diff : 0.0
112
168
  end
113
169
 
114
- # Returns the number of allocations made since the call to +start!+ and
115
- # the call to +finish!+
170
+ # Returns the number of allocations made between the call to #start! and
171
+ # the call to #finish!.
116
172
  def allocations
117
173
  @allocation_count_finish - @allocation_count_start
118
174
  end
119
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
+
120
193
  # Returns the difference in milliseconds between when the execution of the
121
194
  # event started and when it ended.
122
195
  #
@@ -130,15 +203,7 @@ module ActiveSupport
130
203
  #
131
204
  # @event.duration # => 1000.138
132
205
  def duration
133
- self.end - time
134
- end
135
-
136
- def <<(event)
137
- @children << event
138
- end
139
-
140
- def parent_of?(event)
141
- @children.include? event
206
+ @end - @time
142
207
  end
143
208
 
144
209
  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
@@ -237,8 +237,8 @@ module ActiveSupport
237
237
  #
238
238
  # Raises an error if invalid event name type is passed:
239
239
  #
240
- # ActiveSupport::Notifications.subscribe(:render) {|*args| ...}
241
- # #=> ArgumentError (pattern must be specified as a String, Regexp or empty)
240
+ # ActiveSupport::Notifications.subscribe(:render) {|*args| ...}
241
+ # #=> ArgumentError (pattern must be specified as a String, Regexp or empty)
242
242
  #
243
243
  def subscribe(pattern = nil, callback = nil, &block)
244
244
  notifier.subscribe(pattern, callback, monotonic: false, &block)
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bigdecimal"
4
+ require "bigdecimal/util"
3
5
  require "active_support/core_ext/big_decimal/conversions"
4
- require "active_support/core_ext/object/blank"
5
6
  require "active_support/core_ext/hash/keys"
6
7
  require "active_support/i18n"
7
8
  require "active_support/core_ext/class/attribute"
@@ -122,13 +123,14 @@ module ActiveSupport
122
123
 
123
124
  def initialize(number, options)
124
125
  @number = number
125
- @opts = options.symbolize_keys
126
+ @opts = options.symbolize_keys
127
+ @options = nil
126
128
  end
127
129
 
128
130
  def execute
129
131
  if !number
130
132
  nil
131
- elsif validate_float? && !valid_float?
133
+ elsif validate_float? && !valid_bigdecimal
132
134
  number
133
135
  else
134
136
  convert
@@ -173,8 +175,15 @@ module ActiveSupport
173
175
  key.split(".").reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
174
176
  end
175
177
 
176
- def valid_float?
177
- Float(number, exception: false)
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
178
187
  end
179
188
  end
180
189
  end