activesupport 6.1.0 → 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 (225) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1075 -325
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/lib/active_support/actionable_error.rb +4 -2
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +32 -7
  8. data/lib/active_support/benchmarkable.rb +3 -2
  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 +53 -20
  14. data/lib/active_support/cache/mem_cache_store.rb +201 -62
  15. data/lib/active_support/cache/memory_store.rb +86 -24
  16. data/lib/active_support/cache/null_store.rb +16 -2
  17. data/lib/active_support/cache/redis_cache_store.rb +186 -193
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +63 -71
  20. data/lib/active_support/cache.rb +487 -249
  21. data/lib/active_support/callbacks.rb +227 -105
  22. data/lib/active_support/code_generator.rb +70 -0
  23. data/lib/active_support/concern.rb +9 -7
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/concurrency/share_lock.rb +2 -2
  27. data/lib/active_support/configurable.rb +18 -5
  28. data/lib/active_support/configuration_file.rb +7 -2
  29. data/lib/active_support/core_ext/array/access.rb +1 -5
  30. data/lib/active_support/core_ext/array/conversions.rb +15 -13
  31. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  33. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  34. data/lib/active_support/core_ext/class/subclasses.rb +37 -26
  35. data/lib/active_support/core_ext/date/blank.rb +1 -1
  36. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  37. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  38. data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
  39. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  41. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  42. data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
  43. data/lib/active_support/core_ext/digest/uuid.rb +30 -13
  44. data/lib/active_support/core_ext/enumerable.rb +85 -83
  45. data/lib/active_support/core_ext/erb/util.rb +196 -0
  46. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  47. data/lib/active_support/core_ext/hash/conversions.rb +1 -2
  48. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  49. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  50. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  51. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  52. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  53. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  54. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +8 -0
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +49 -22
  57. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  58. data/lib/active_support/core_ext/module/delegation.rb +81 -43
  59. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  60. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  61. data/lib/active_support/core_ext/name_error.rb +2 -8
  62. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  63. data/lib/active_support/core_ext/numeric/conversions.rb +82 -77
  64. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  65. data/lib/active_support/core_ext/object/blank.rb +2 -2
  66. data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +31 -11
  68. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  69. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  70. data/lib/active_support/core_ext/object/json.rb +49 -27
  71. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  72. data/lib/active_support/core_ext/object/try.rb +20 -20
  73. data/lib/active_support/core_ext/object/with.rb +44 -0
  74. data/lib/active_support/core_ext/object/with_options.rb +25 -6
  75. data/lib/active_support/core_ext/object.rb +1 -0
  76. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  77. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  78. data/lib/active_support/core_ext/pathname.rb +4 -0
  79. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  80. data/lib/active_support/core_ext/range/conversions.rb +34 -13
  81. data/lib/active_support/core_ext/range/each.rb +1 -1
  82. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  83. data/lib/active_support/core_ext/range.rb +1 -2
  84. data/lib/active_support/core_ext/securerandom.rb +25 -13
  85. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  86. data/lib/active_support/core_ext/string/filters.rb +21 -15
  87. data/lib/active_support/core_ext/string/indent.rb +1 -1
  88. data/lib/active_support/core_ext/string/inflections.rb +17 -10
  89. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  90. data/lib/active_support/core_ext/string/output_safety.rb +85 -165
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  92. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +30 -8
  94. data/lib/active_support/core_ext/time/conversions.rb +15 -13
  95. data/lib/active_support/core_ext/time/zones.rb +12 -28
  96. data/lib/active_support/core_ext.rb +2 -1
  97. data/lib/active_support/current_attributes.rb +47 -20
  98. data/lib/active_support/deep_mergeable.rb +53 -0
  99. data/lib/active_support/dependencies/autoload.rb +17 -12
  100. data/lib/active_support/dependencies/interlock.rb +10 -18
  101. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  102. data/lib/active_support/dependencies.rb +58 -788
  103. data/lib/active_support/deprecation/behaviors.rb +66 -40
  104. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  105. data/lib/active_support/deprecation/deprecators.rb +104 -0
  106. data/lib/active_support/deprecation/disallowed.rb +6 -8
  107. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  108. data/lib/active_support/deprecation/method_wrappers.rb +9 -26
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +38 -23
  110. data/lib/active_support/deprecation/reporting.rb +43 -26
  111. data/lib/active_support/deprecation.rb +32 -5
  112. data/lib/active_support/deprecator.rb +7 -0
  113. data/lib/active_support/descendants_tracker.rb +150 -72
  114. data/lib/active_support/digest.rb +5 -3
  115. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  116. data/lib/active_support/duration/iso8601_serializer.rb +9 -3
  117. data/lib/active_support/duration.rb +83 -52
  118. data/lib/active_support/encrypted_configuration.rb +72 -9
  119. data/lib/active_support/encrypted_file.rb +29 -13
  120. data/lib/active_support/environment_inquirer.rb +23 -3
  121. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  122. data/lib/active_support/error_reporter.rb +203 -0
  123. data/lib/active_support/evented_file_update_checker.rb +20 -7
  124. data/lib/active_support/execution_context/test_helper.rb +13 -0
  125. data/lib/active_support/execution_context.rb +53 -0
  126. data/lib/active_support/execution_wrapper.rb +44 -22
  127. data/lib/active_support/executor/test_helper.rb +7 -0
  128. data/lib/active_support/file_update_checker.rb +4 -2
  129. data/lib/active_support/fork_tracker.rb +28 -11
  130. data/lib/active_support/gem_version.rb +4 -4
  131. data/lib/active_support/gzip.rb +2 -0
  132. data/lib/active_support/hash_with_indifferent_access.rb +44 -19
  133. data/lib/active_support/html_safe_translation.rb +53 -0
  134. data/lib/active_support/i18n.rb +2 -1
  135. data/lib/active_support/i18n_railtie.rb +21 -14
  136. data/lib/active_support/inflector/inflections.rb +25 -7
  137. data/lib/active_support/inflector/methods.rb +50 -64
  138. data/lib/active_support/inflector/transliterate.rb +4 -2
  139. data/lib/active_support/isolated_execution_state.rb +76 -0
  140. data/lib/active_support/json/decoding.rb +2 -1
  141. data/lib/active_support/json/encoding.rb +27 -45
  142. data/lib/active_support/key_generator.rb +31 -6
  143. data/lib/active_support/lazy_load_hooks.rb +33 -7
  144. data/lib/active_support/locale/en.yml +4 -2
  145. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  146. data/lib/active_support/log_subscriber.rb +97 -35
  147. data/lib/active_support/logger.rb +9 -60
  148. data/lib/active_support/logger_thread_safe_level.rb +11 -34
  149. data/lib/active_support/message_encryptor.rb +206 -56
  150. data/lib/active_support/message_encryptors.rb +141 -0
  151. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  152. data/lib/active_support/message_pack/extensions.rb +292 -0
  153. data/lib/active_support/message_pack/serializer.rb +63 -0
  154. data/lib/active_support/message_pack.rb +50 -0
  155. data/lib/active_support/message_verifier.rb +235 -84
  156. data/lib/active_support/message_verifiers.rb +135 -0
  157. data/lib/active_support/messages/codec.rb +65 -0
  158. data/lib/active_support/messages/metadata.rb +112 -46
  159. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  160. data/lib/active_support/messages/rotator.rb +34 -32
  161. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  162. data/lib/active_support/multibyte/chars.rb +12 -11
  163. data/lib/active_support/multibyte/unicode.rb +9 -49
  164. data/lib/active_support/multibyte.rb +1 -1
  165. data/lib/active_support/notifications/fanout.rb +304 -114
  166. data/lib/active_support/notifications/instrumenter.rb +117 -35
  167. data/lib/active_support/notifications.rb +25 -25
  168. data/lib/active_support/number_helper/number_converter.rb +14 -7
  169. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  170. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  171. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -4
  172. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  173. data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
  174. data/lib/active_support/number_helper/rounding_helper.rb +2 -6
  175. data/lib/active_support/number_helper.rb +379 -319
  176. data/lib/active_support/option_merger.rb +10 -18
  177. data/lib/active_support/ordered_hash.rb +4 -4
  178. data/lib/active_support/ordered_options.rb +15 -1
  179. data/lib/active_support/parameter_filter.rb +105 -81
  180. data/lib/active_support/proxy_object.rb +2 -0
  181. data/lib/active_support/railtie.rb +83 -21
  182. data/lib/active_support/reloader.rb +13 -5
  183. data/lib/active_support/rescuable.rb +18 -16
  184. data/lib/active_support/ruby_features.rb +7 -0
  185. data/lib/active_support/secure_compare_rotator.rb +18 -11
  186. data/lib/active_support/security_utils.rb +1 -1
  187. data/lib/active_support/string_inquirer.rb +3 -3
  188. data/lib/active_support/subscriber.rb +11 -40
  189. data/lib/active_support/syntax_error_proxy.rb +60 -0
  190. data/lib/active_support/tagged_logging.rb +65 -25
  191. data/lib/active_support/test_case.rb +166 -27
  192. data/lib/active_support/testing/assertions.rb +61 -15
  193. data/lib/active_support/testing/autorun.rb +0 -2
  194. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  195. data/lib/active_support/testing/deprecation.rb +53 -2
  196. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  197. data/lib/active_support/testing/isolation.rb +30 -29
  198. data/lib/active_support/testing/method_call_assertions.rb +24 -11
  199. data/lib/active_support/testing/parallelization/server.rb +4 -0
  200. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  201. data/lib/active_support/testing/parallelization.rb +4 -0
  202. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  203. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  204. data/lib/active_support/testing/stream.rb +4 -6
  205. data/lib/active_support/testing/strict_warnings.rb +39 -0
  206. data/lib/active_support/testing/tagged_logging.rb +1 -1
  207. data/lib/active_support/testing/time_helpers.rb +49 -16
  208. data/lib/active_support/time_with_zone.rb +39 -28
  209. data/lib/active_support/values/time_zone.rb +50 -18
  210. data/lib/active_support/version.rb +1 -1
  211. data/lib/active_support/xml_mini/jdom.rb +4 -11
  212. data/lib/active_support/xml_mini/libxml.rb +5 -5
  213. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  214. data/lib/active_support/xml_mini/nokogiri.rb +5 -5
  215. data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
  216. data/lib/active_support/xml_mini/rexml.rb +2 -2
  217. data/lib/active_support/xml_mini.rb +7 -6
  218. data/lib/active_support.rb +28 -1
  219. metadata +150 -18
  220. data/lib/active_support/core_ext/marshal.rb +0 -26
  221. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -28
  222. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  223. data/lib/active_support/core_ext/uri.rb +0 -29
  224. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
  225. data/lib/active_support/per_thread_registry.rb +0 -60
@@ -7,6 +7,43 @@ require "active_support/core_ext/object/try"
7
7
 
8
8
  module ActiveSupport
9
9
  module Notifications
10
+ class InstrumentationSubscriberError < RuntimeError
11
+ attr_reader :exceptions
12
+
13
+ def initialize(exceptions)
14
+ @exceptions = exceptions
15
+ exception_class_names = exceptions.map { |e| e.class.name }
16
+ super "Exception(s) occurred within instrumentation subscribers: #{exception_class_names.join(', ')}"
17
+ end
18
+ end
19
+
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
+
10
47
  # This is a default queue implementation that ships with Notifications.
11
48
  # It just pushes events to all registered log subscribers.
12
49
  #
@@ -15,21 +52,31 @@ module ActiveSupport
15
52
  include Mutex_m
16
53
 
17
54
  def initialize
18
- @string_subscribers = Hash.new { |h, k| h[k] = [] }
55
+ @string_subscribers = Concurrent::Map.new { |h, k| h.compute_if_absent(k) { [] } }
19
56
  @other_subscribers = []
20
- @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
21
60
  super
22
61
  end
23
62
 
63
+ def inspect # :nodoc:
64
+ total_patterns = @string_subscribers.size + @other_subscribers.size
65
+ "#<#{self.class} (#{total_patterns} patterns)>"
66
+ end
67
+
24
68
  def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
25
69
  subscriber = Subscribers.new(pattern, callable || block, monotonic)
26
70
  synchronize do
27
- if String === pattern
71
+ case pattern
72
+ when String
28
73
  @string_subscribers[pattern] << subscriber
29
- @listeners_for.delete(pattern)
30
- else
74
+ clear_cache(pattern)
75
+ when NilClass, Regexp
31
76
  @other_subscribers << subscriber
32
- @listeners_for.clear
77
+ clear_cache
78
+ else
79
+ raise ArgumentError, "pattern must be specified as a String, Regexp or empty"
33
80
  end
34
81
  end
35
82
  subscriber
@@ -40,44 +87,232 @@ module ActiveSupport
40
87
  case subscriber_or_name
41
88
  when String
42
89
  @string_subscribers[subscriber_or_name].clear
43
- @listeners_for.delete(subscriber_or_name)
90
+ clear_cache(subscriber_or_name)
44
91
  @other_subscribers.each { |sub| sub.unsubscribe!(subscriber_or_name) }
45
92
  else
46
93
  pattern = subscriber_or_name.try(:pattern)
47
94
  if String === pattern
48
95
  @string_subscribers[pattern].delete(subscriber_or_name)
49
- @listeners_for.delete(pattern)
96
+ clear_cache(pattern)
50
97
  else
51
98
  @other_subscribers.delete(subscriber_or_name)
52
- @listeners_for.clear
99
+ clear_cache
100
+ end
101
+ end
102
+ end
103
+ end
104
+
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
115
+ end
116
+
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
127
+ end
128
+
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
140
+ end
141
+
142
+ class MonotonicTimedGroup < BaseTimeGroup # :nodoc:
143
+ private
144
+ def now
145
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
146
+ end
147
+ end
148
+
149
+ class TimedGroup < BaseTimeGroup # :nodoc:
150
+ private
151
+ def now
152
+ Time.now
153
+ end
154
+ end
155
+
156
+ class EventedGroup < BaseGroup # :nodoc:
157
+ def start(name, id, payload)
158
+ each do |s|
159
+ s.start(name, id, payload)
160
+ end
161
+ end
162
+
163
+ def finish(name, id, payload)
164
+ each do |s|
165
+ s.finish(name, id, payload)
166
+ end
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
175
+
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
189
+ end
190
+
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
53
210
  end
54
211
  end
55
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)
56
278
  end
57
279
 
58
280
  def start(name, id, payload)
59
- listeners_for(name).each { |s| s.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
60
285
  end
61
286
 
62
- def finish(name, id, payload, listeners = listeners_for(name))
63
- listeners.each { |s| s.finish(name, id, payload) }
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)
64
291
  end
65
292
 
66
293
  def publish(name, *args)
67
- listeners_for(name).each { |s| s.publish(name, *args) }
294
+ iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
68
295
  end
69
296
 
70
- def listeners_for(name)
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)
71
302
  # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
72
- @listeners_for[name] || synchronize do
303
+ @all_listeners_for[name] || synchronize do
73
304
  # use synchronisation when accessing @subscribers
74
- @listeners_for[name] ||=
305
+ @all_listeners_for[name] ||=
75
306
  @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
76
307
  end
77
308
  end
78
309
 
310
+ def listeners_for(name)
311
+ all_listeners_for(name).reject { |s| s.silenced?(name) }
312
+ end
313
+
79
314
  def listening?(name)
80
- listeners_for(name).any?
315
+ all_listeners_for(name).any? { |s| !s.silenced?(name) }
81
316
  end
82
317
 
83
318
  # This is a sync queue, so there is no waiting.
@@ -91,33 +326,30 @@ module ActiveSupport
91
326
  if listener.respond_to?(:start) && listener.respond_to?(:finish)
92
327
  subscriber_class = Evented
93
328
  else
94
- # Doing all this to detect a block like `proc { |x| }` vs
95
- # `proc { |*x| }` or `proc { |**x| }`
96
- if listener.respond_to?(:parameters)
97
- params = listener.parameters
98
- if params.length == 1 && params.first.first == :opt
99
- subscriber_class = EventObject
100
- end
329
+ # Doing this to detect a single argument block or callable
330
+ # like `proc { |x| }` vs `proc { |*x| }`, `proc { |**x| }`,
331
+ # or `proc { |x, **y| }`
332
+ procish = listener.respond_to?(:parameters) ? listener : listener.method(:call)
333
+
334
+ if procish.arity == 1 && procish.parameters.length == 1
335
+ subscriber_class = EventObject
101
336
  end
102
337
  end
103
338
 
104
- wrap_all pattern, subscriber_class.new(pattern, listener)
105
- end
106
-
107
- def self.wrap_all(pattern, subscriber)
108
- unless pattern
109
- AllMessages.new(subscriber)
110
- else
111
- subscriber
112
- end
339
+ subscriber_class.new(pattern, listener)
113
340
  end
114
341
 
115
- class Matcher #:nodoc:
342
+ class Matcher # :nodoc:
116
343
  attr_reader :pattern, :exclusions
117
344
 
118
345
  def self.wrap(pattern)
119
- return pattern if String === pattern
120
- new(pattern)
346
+ if String === pattern
347
+ pattern
348
+ elsif pattern.nil?
349
+ AllMessages.new
350
+ else
351
+ new(pattern)
352
+ end
121
353
  end
122
354
 
123
355
  def initialize(pattern)
@@ -132,15 +364,31 @@ module ActiveSupport
132
364
  def ===(name)
133
365
  pattern === name && !exclusions.include?(name)
134
366
  end
367
+
368
+ class AllMessages
369
+ def ===(name)
370
+ true
371
+ end
372
+
373
+ def unsubscribe!(*)
374
+ false
375
+ end
376
+ end
135
377
  end
136
378
 
137
- class Evented #:nodoc:
138
- attr_reader :pattern
379
+ class Evented # :nodoc:
380
+ attr_reader :pattern, :delegate, :silenceable
139
381
 
140
382
  def initialize(pattern, delegate)
141
383
  @pattern = Matcher.wrap(pattern)
142
384
  @delegate = delegate
385
+ @silenceable = delegate.respond_to?(:silenced?)
143
386
  @can_publish = delegate.respond_to?(:publish)
387
+ @can_publish_event = delegate.respond_to?(:publish_event)
388
+ end
389
+
390
+ def group_class
391
+ EventedGroup
144
392
  end
145
393
 
146
394
  def publish(name, *args)
@@ -149,109 +397,51 @@ module ActiveSupport
149
397
  end
150
398
  end
151
399
 
152
- def start(name, id, payload)
153
- @delegate.start name, id, payload
400
+ def publish_event(event)
401
+ if @can_publish_event
402
+ @delegate.publish_event event
403
+ else
404
+ publish(event.name, event.time, event.end, event.transaction_id, event.payload)
405
+ end
154
406
  end
155
407
 
156
- def finish(name, id, payload)
157
- @delegate.finish name, id, payload
408
+ def silenced?(name)
409
+ @silenceable && @delegate.silenced?(name)
158
410
  end
159
411
 
160
412
  def subscribed_to?(name)
161
413
  pattern === name
162
414
  end
163
415
 
164
- def matches?(name)
165
- pattern && pattern === name
166
- end
167
-
168
416
  def unsubscribe!(name)
169
417
  pattern.unsubscribe!(name)
170
418
  end
171
419
  end
172
420
 
173
421
  class Timed < Evented # :nodoc:
174
- def publish(name, *args)
175
- @delegate.call name, *args
176
- end
177
-
178
- def start(name, id, payload)
179
- timestack = Thread.current[:_timestack] ||= []
180
- timestack.push Time.now
422
+ def group_class
423
+ TimedGroup
181
424
  end
182
425
 
183
- def finish(name, id, payload)
184
- timestack = Thread.current[:_timestack]
185
- started = timestack.pop
186
- @delegate.call(name, started, Time.now, id, payload)
187
- end
188
- end
189
-
190
- class MonotonicTimed < Evented # :nodoc:
191
426
  def publish(name, *args)
192
427
  @delegate.call name, *args
193
428
  end
194
-
195
- def start(name, id, payload)
196
- timestack = Thread.current[:_timestack_monotonic] ||= []
197
- timestack.push Concurrent.monotonic_time
198
- end
199
-
200
- def finish(name, id, payload)
201
- timestack = Thread.current[:_timestack_monotonic]
202
- started = timestack.pop
203
- @delegate.call(name, started, Concurrent.monotonic_time, id, payload)
204
- end
205
429
  end
206
430
 
207
- class EventObject < Evented
208
- def start(name, id, payload)
209
- stack = Thread.current[:_event_stack] ||= []
210
- event = build_event name, id, payload
211
- event.start!
212
- stack.push event
213
- end
214
-
215
- def finish(name, id, payload)
216
- stack = Thread.current[:_event_stack]
217
- event = stack.pop
218
- event.payload = payload
219
- event.finish!
220
- @delegate.call event
431
+ class MonotonicTimed < Timed # :nodoc:
432
+ def group_class
433
+ MonotonicTimedGroup
221
434
  end
222
-
223
- private
224
- def build_event(name, id, payload)
225
- ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
226
- end
227
435
  end
228
436
 
229
- class AllMessages # :nodoc:
230
- def initialize(delegate)
231
- @delegate = delegate
232
- end
233
-
234
- def start(name, id, payload)
235
- @delegate.start name, id, payload
236
- end
237
-
238
- def finish(name, id, payload)
239
- @delegate.finish name, id, payload
240
- end
241
-
242
- def publish(name, *args)
243
- @delegate.publish name, *args
244
- end
245
-
246
- def subscribed_to?(name)
247
- true
437
+ class EventObject < Evented
438
+ def group_class
439
+ EventObjectGroup
248
440
  end
249
441
 
250
- def unsubscribe!(*)
251
- false
442
+ def publish_event(event)
443
+ @delegate.call event
252
444
  end
253
-
254
- alias :matches? :===
255
445
  end
256
446
  end
257
447
  end