activesupport 4.0.12 → 7.0.2.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (295) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +249 -501
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -5
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +48 -0
  8. data/lib/active_support/backtrace_cleaner.rb +41 -13
  9. data/lib/active_support/benchmarkable.rb +7 -15
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +96 -74
  12. data/lib/active_support/cache/mem_cache_store.rb +211 -103
  13. data/lib/active_support/cache/memory_store.rb +90 -58
  14. data/lib/active_support/cache/null_store.rb +19 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +468 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +86 -83
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  18. data/lib/active_support/cache.rb +580 -241
  19. data/lib/active_support/callbacks.rb +812 -425
  20. data/lib/active_support/code_generator.rb +65 -0
  21. data/lib/active_support/concern.rb +103 -14
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +33 -0
  23. data/lib/active_support/concurrency/share_lock.rb +226 -0
  24. data/lib/active_support/configurable.rb +21 -19
  25. data/lib/active_support/configuration_file.rb +51 -0
  26. data/lib/active_support/core_ext/array/access.rb +47 -1
  27. data/lib/active_support/core_ext/array/conversions.rb +35 -44
  28. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +26 -16
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  34. data/lib/active_support/core_ext/array.rb +10 -7
  35. data/lib/active_support/core_ext/benchmark.rb +5 -3
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  37. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  38. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  39. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  40. data/lib/active_support/core_ext/class/subclasses.rb +25 -26
  41. data/lib/active_support/core_ext/class.rb +4 -4
  42. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  43. data/lib/active_support/core_ext/date/blank.rb +14 -0
  44. data/lib/active_support/core_ext/date/calculations.rb +31 -18
  45. data/lib/active_support/core_ext/date/conversions.rb +43 -32
  46. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  47. data/lib/active_support/core_ext/date/zones.rb +5 -34
  48. data/lib/active_support/core_ext/date.rb +7 -4
  49. data/lib/active_support/core_ext/date_and_time/calculations.rb +198 -66
  50. data/lib/active_support/core_ext/date_and_time/compatibility.rb +31 -0
  51. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +79 -38
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +31 -26
  57. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  58. data/lib/active_support/core_ext/date_time.rb +8 -4
  59. data/lib/active_support/core_ext/digest/uuid.rb +79 -0
  60. data/lib/active_support/core_ext/digest.rb +3 -0
  61. data/lib/active_support/core_ext/enumerable.rb +249 -17
  62. data/lib/active_support/core_ext/file/atomic.rb +41 -32
  63. data/lib/active_support/core_ext/file.rb +3 -1
  64. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +14 -5
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  69. data/lib/active_support/core_ext/hash/keys.rb +39 -56
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  71. data/lib/active_support/core_ext/hash/slice.rb +8 -23
  72. data/lib/active_support/core_ext/hash.rb +10 -8
  73. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  74. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  75. data/lib/active_support/core_ext/integer/time.rb +11 -33
  76. data/lib/active_support/core_ext/integer.rb +5 -3
  77. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  78. data/lib/active_support/core_ext/kernel/reporting.rb +9 -78
  79. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  80. data/lib/active_support/core_ext/kernel.rb +5 -4
  81. data/lib/active_support/core_ext/load_error.rb +5 -21
  82. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  83. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  84. data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
  85. data/lib/active_support/core_ext/module/attribute_accessors.rb +186 -44
  86. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +157 -0
  87. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  88. data/lib/active_support/core_ext/module/delegation.rb +172 -45
  89. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  90. data/lib/active_support/core_ext/module/introspection.rb +23 -38
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/module.rb +13 -10
  94. data/lib/active_support/core_ext/name_error.rb +45 -4
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +135 -127
  97. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  99. data/lib/active_support/core_ext/numeric.rb +6 -3
  100. data/lib/active_support/core_ext/object/acts_like.rb +41 -6
  101. data/lib/active_support/core_ext/object/blank.rb +70 -20
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  104. data/lib/active_support/core_ext/object/duplicable.rb +17 -47
  105. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +244 -0
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  110. data/lib/active_support/core_ext/object/try.rb +106 -26
  111. data/lib/active_support/core_ext/object/with_options.rb +64 -5
  112. data/lib/active_support/core_ext/object.rb +14 -12
  113. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  114. data/lib/active_support/core_ext/pathname.rb +3 -0
  115. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  116. data/lib/active_support/core_ext/range/conversions.rb +37 -15
  117. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  118. data/lib/active_support/core_ext/range/each.rb +18 -17
  119. data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
  120. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  121. data/lib/active_support/core_ext/range.rb +7 -4
  122. data/lib/active_support/core_ext/regexp.rb +10 -1
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string/access.rb +42 -51
  125. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  126. data/lib/active_support/core_ext/string/conversions.rb +18 -13
  127. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  128. data/lib/active_support/core_ext/string/filters.rb +97 -7
  129. data/lib/active_support/core_ext/string/indent.rb +6 -4
  130. data/lib/active_support/core_ext/string/inflections.rb +106 -25
  131. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  132. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  133. data/lib/active_support/core_ext/string/output_safety.rb +227 -54
  134. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  135. data/lib/active_support/core_ext/string/strip.rb +6 -5
  136. data/lib/active_support/core_ext/string/zones.rb +4 -1
  137. data/lib/active_support/core_ext/string.rb +15 -13
  138. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/symbol.rb +3 -0
  140. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  141. data/lib/active_support/core_ext/time/calculations.rb +178 -116
  142. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  143. data/lib/active_support/core_ext/time/conversions.rb +37 -25
  144. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  145. data/lib/active_support/core_ext/time/zones.rb +44 -42
  146. data/lib/active_support/core_ext/time.rb +8 -5
  147. data/lib/active_support/core_ext/uri.rb +4 -25
  148. data/lib/active_support/core_ext.rb +4 -2
  149. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  150. data/lib/active_support/current_attributes.rb +226 -0
  151. data/lib/active_support/dependencies/autoload.rb +3 -1
  152. data/lib/active_support/dependencies/interlock.rb +49 -0
  153. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  154. data/lib/active_support/dependencies.rb +71 -696
  155. data/lib/active_support/deprecation/behaviors.rb +65 -16
  156. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  157. data/lib/active_support/deprecation/disallowed.rb +56 -0
  158. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  159. data/lib/active_support/deprecation/method_wrappers.rb +62 -21
  160. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -31
  161. data/lib/active_support/deprecation/reporting.rb +81 -18
  162. data/lib/active_support/deprecation.rb +19 -11
  163. data/lib/active_support/descendants_tracker.rb +192 -34
  164. data/lib/active_support/digest.rb +22 -0
  165. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  166. data/lib/active_support/duration/iso8601_serializer.rb +67 -0
  167. data/lib/active_support/duration.rb +437 -39
  168. data/lib/active_support/encrypted_configuration.rb +56 -0
  169. data/lib/active_support/encrypted_file.rb +117 -0
  170. data/lib/active_support/environment_inquirer.rb +20 -0
  171. data/lib/active_support/error_reporter.rb +117 -0
  172. data/lib/active_support/evented_file_update_checker.rb +170 -0
  173. data/lib/active_support/execution_context/test_helper.rb +13 -0
  174. data/lib/active_support/execution_context.rb +53 -0
  175. data/lib/active_support/execution_wrapper.rb +151 -0
  176. data/lib/active_support/executor/test_helper.rb +7 -0
  177. data/lib/active_support/executor.rb +8 -0
  178. data/lib/active_support/file_update_checker.rb +62 -37
  179. data/lib/active_support/fork_tracker.rb +71 -0
  180. data/lib/active_support/gem_version.rb +17 -0
  181. data/lib/active_support/gzip.rb +7 -5
  182. data/lib/active_support/hash_with_indifferent_access.rb +207 -54
  183. data/lib/active_support/html_safe_translation.rb +43 -0
  184. data/lib/active_support/i18n.rb +10 -6
  185. data/lib/active_support/i18n_railtie.rb +48 -19
  186. data/lib/active_support/inflections.rb +19 -12
  187. data/lib/active_support/inflector/inflections.rb +97 -37
  188. data/lib/active_support/inflector/methods.rb +192 -157
  189. data/lib/active_support/inflector/transliterate.rb +83 -33
  190. data/lib/active_support/inflector.rb +7 -5
  191. data/lib/active_support/isolated_execution_state.rb +64 -0
  192. data/lib/active_support/json/decoding.rb +37 -42
  193. data/lib/active_support/json/encoding.rb +93 -293
  194. data/lib/active_support/json.rb +4 -2
  195. data/lib/active_support/key_generator.rb +30 -47
  196. data/lib/active_support/lazy_load_hooks.rb +54 -21
  197. data/lib/active_support/locale/en.rb +33 -0
  198. data/lib/active_support/locale/en.yml +10 -4
  199. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  200. data/lib/active_support/log_subscriber.rb +61 -18
  201. data/lib/active_support/logger.rb +40 -4
  202. data/lib/active_support/logger_silence.rb +17 -20
  203. data/lib/active_support/logger_thread_safe_level.rb +69 -0
  204. data/lib/active_support/message_encryptor.rb +178 -55
  205. data/lib/active_support/message_verifier.rb +195 -26
  206. data/lib/active_support/messages/metadata.rb +80 -0
  207. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  208. data/lib/active_support/messages/rotator.rb +57 -0
  209. data/lib/active_support/multibyte/chars.rb +45 -92
  210. data/lib/active_support/multibyte/unicode.rb +44 -377
  211. data/lib/active_support/multibyte.rb +5 -3
  212. data/lib/active_support/notifications/fanout.rb +177 -44
  213. data/lib/active_support/notifications/instrumenter.rb +117 -17
  214. data/lib/active_support/notifications.rb +106 -39
  215. data/lib/active_support/number_helper/number_converter.rb +181 -0
  216. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  217. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  218. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  219. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  220. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  221. data/lib/active_support/number_helper/number_to_phone_converter.rb +59 -0
  222. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  223. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  224. data/lib/active_support/number_helper.rb +152 -394
  225. data/lib/active_support/option_merger.rb +18 -5
  226. data/lib/active_support/ordered_hash.rb +8 -6
  227. data/lib/active_support/ordered_options.rb +43 -7
  228. data/lib/active_support/parameter_filter.rb +138 -0
  229. data/lib/active_support/per_thread_registry.rb +24 -11
  230. data/lib/active_support/proxy_object.rb +2 -0
  231. data/lib/active_support/rails.rb +10 -11
  232. data/lib/active_support/railtie.rb +118 -12
  233. data/lib/active_support/reloader.rb +130 -0
  234. data/lib/active_support/rescuable.rb +112 -57
  235. data/lib/active_support/ruby_features.rb +7 -0
  236. data/lib/active_support/secure_compare_rotator.rb +51 -0
  237. data/lib/active_support/security_utils.rb +38 -0
  238. data/lib/active_support/string_inquirer.rb +11 -4
  239. data/lib/active_support/subscriber.rb +109 -39
  240. data/lib/active_support/tagged_logging.rb +54 -17
  241. data/lib/active_support/test_case.rb +121 -37
  242. data/lib/active_support/testing/assertions.rb +177 -39
  243. data/lib/active_support/testing/autorun.rb +5 -3
  244. data/lib/active_support/testing/constant_lookup.rb +3 -6
  245. data/lib/active_support/testing/declarative.rb +10 -22
  246. data/lib/active_support/testing/deprecation.rb +65 -11
  247. data/lib/active_support/testing/file_fixtures.rb +38 -0
  248. data/lib/active_support/testing/isolation.rb +56 -87
  249. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  250. data/lib/active_support/testing/parallelization/server.rb +82 -0
  251. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  252. data/lib/active_support/testing/parallelization.rb +55 -0
  253. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  254. data/lib/active_support/testing/setup_and_teardown.rb +30 -10
  255. data/lib/active_support/testing/stream.rb +41 -0
  256. data/lib/active_support/testing/tagged_logging.rb +6 -4
  257. data/lib/active_support/testing/time_helpers.rb +246 -0
  258. data/lib/active_support/time.rb +13 -13
  259. data/lib/active_support/time_with_zone.rb +315 -90
  260. data/lib/active_support/values/time_zone.rb +306 -135
  261. data/lib/active_support/version.rb +6 -7
  262. data/lib/active_support/xml_mini/jdom.rb +117 -115
  263. data/lib/active_support/xml_mini/libxml.rb +22 -21
  264. data/lib/active_support/xml_mini/libxmlsax.rb +17 -19
  265. data/lib/active_support/xml_mini/nokogiri.rb +19 -19
  266. data/lib/active_support/xml_mini/nokogirisax.rb +16 -17
  267. data/lib/active_support/xml_mini/rexml.rb +25 -17
  268. data/lib/active_support/xml_mini.rb +67 -56
  269. data/lib/active_support.rb +58 -3
  270. metadata +125 -66
  271. data/lib/active_support/basic_object.rb +0 -11
  272. data/lib/active_support/buffered_logger.rb +0 -21
  273. data/lib/active_support/concurrency/latch.rb +0 -27
  274. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  275. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  276. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  277. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  278. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  279. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  280. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  281. data/lib/active_support/core_ext/logger.rb +0 -67
  282. data/lib/active_support/core_ext/marshal.rb +0 -21
  283. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  284. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  285. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  286. data/lib/active_support/core_ext/proc.rb +0 -17
  287. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  288. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  289. data/lib/active_support/core_ext/struct.rb +0 -6
  290. data/lib/active_support/core_ext/thread.rb +0 -79
  291. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  292. data/lib/active_support/file_watcher.rb +0 -36
  293. data/lib/active_support/json/variable.rb +0 -18
  294. data/lib/active_support/testing/pending.rb +0 -14
  295. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,8 +1,22 @@
1
- require 'mutex_m'
2
- require 'thread_safe'
1
+ # frozen_string_literal: true
2
+
3
+ require "mutex_m"
4
+ require "concurrent/map"
5
+ require "set"
6
+ require "active_support/core_ext/object/try"
3
7
 
4
8
  module ActiveSupport
5
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
+
6
20
  # This is a default queue implementation that ships with Notifications.
7
21
  # It just pushes events to all registered log subscribers.
8
22
  #
@@ -11,44 +25,92 @@ module ActiveSupport
11
25
  include Mutex_m
12
26
 
13
27
  def initialize
14
- @subscribers = []
15
- @listeners_for = ThreadSafe::Cache.new
28
+ @string_subscribers = Hash.new { |h, k| h[k] = [] }
29
+ @other_subscribers = []
30
+ @listeners_for = Concurrent::Map.new
16
31
  super
17
32
  end
18
33
 
19
- def subscribe(pattern = nil, block = Proc.new)
20
- subscriber = Subscribers.new pattern, block
34
+ def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
35
+ subscriber = Subscribers.new(pattern, callable || block, monotonic)
21
36
  synchronize do
22
- @subscribers << subscriber
23
- @listeners_for.clear
37
+ case pattern
38
+ when String
39
+ @string_subscribers[pattern] << subscriber
40
+ @listeners_for.delete(pattern)
41
+ when NilClass, Regexp
42
+ @other_subscribers << subscriber
43
+ @listeners_for.clear
44
+ else
45
+ raise ArgumentError, "pattern must be specified as a String, Regexp or empty"
46
+ end
24
47
  end
25
48
  subscriber
26
49
  end
27
50
 
28
- def unsubscribe(subscriber)
51
+ def unsubscribe(subscriber_or_name)
29
52
  synchronize do
30
- @subscribers.reject! { |s| s.matches?(subscriber) }
31
- @listeners_for.clear
53
+ case subscriber_or_name
54
+ when String
55
+ @string_subscribers[subscriber_or_name].clear
56
+ @listeners_for.delete(subscriber_or_name)
57
+ @other_subscribers.each { |sub| sub.unsubscribe!(subscriber_or_name) }
58
+ else
59
+ pattern = subscriber_or_name.try(:pattern)
60
+ if String === pattern
61
+ @string_subscribers[pattern].delete(subscriber_or_name)
62
+ @listeners_for.delete(pattern)
63
+ else
64
+ @other_subscribers.delete(subscriber_or_name)
65
+ @listeners_for.clear
66
+ end
67
+ end
32
68
  end
33
69
  end
34
70
 
35
71
  def start(name, id, payload)
36
- listeners_for(name).each { |s| s.start(name, id, payload) }
72
+ iterate_guarding_exceptions(listeners_for(name)) { |s| s.start(name, id, payload) }
37
73
  end
38
74
 
39
- def finish(name, id, payload)
40
- listeners_for(name).each { |s| s.finish(name, id, payload) }
75
+ def finish(name, id, payload, listeners = listeners_for(name))
76
+ iterate_guarding_exceptions(listeners) { |s| s.finish(name, id, payload) }
41
77
  end
42
78
 
43
79
  def publish(name, *args)
44
- listeners_for(name).each { |s| s.publish(name, *args) }
80
+ iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
81
+ end
82
+
83
+ def publish_event(event)
84
+ iterate_guarding_exceptions(listeners_for(event.name)) { |s| s.publish_event(event) }
85
+ end
86
+
87
+ def iterate_guarding_exceptions(listeners)
88
+ exceptions = nil
89
+
90
+ listeners.each do |s|
91
+ yield s
92
+ rescue Exception => e
93
+ exceptions ||= []
94
+ exceptions << e
95
+ end
96
+
97
+ if exceptions
98
+ if exceptions.size == 1
99
+ raise exceptions.first
100
+ else
101
+ raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
102
+ end
103
+ end
104
+
105
+ listeners
45
106
  end
46
107
 
47
108
  def listeners_for(name)
48
- # this is correctly done double-checked locking (ThreadSafe::Cache's lookups have volatile semantics)
109
+ # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
49
110
  @listeners_for[name] || synchronize do
50
111
  # use synchronisation when accessing @subscribers
51
- @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
112
+ @listeners_for[name] ||=
113
+ @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
52
114
  end
53
115
  end
54
116
 
@@ -61,25 +123,70 @@ module ActiveSupport
61
123
  end
62
124
 
63
125
  module Subscribers # :nodoc:
64
- def self.new(pattern, listener)
65
- if listener.respond_to?(:start) and listener.respond_to?(:finish)
66
- subscriber = Evented.new pattern, listener
126
+ def self.new(pattern, listener, monotonic)
127
+ subscriber_class = monotonic ? MonotonicTimed : Timed
128
+
129
+ if listener.respond_to?(:start) && listener.respond_to?(:finish)
130
+ subscriber_class = Evented
67
131
  else
68
- subscriber = Timed.new pattern, listener
132
+ # Doing this to detect a single argument block or callable
133
+ # like `proc { |x| }` vs `proc { |*x| }`, `proc { |**x| }`,
134
+ # or `proc { |x, **y| }`
135
+ procish = listener.respond_to?(:parameters) ? listener : listener.method(:call)
136
+
137
+ if procish.arity == 1 && procish.parameters.length == 1
138
+ subscriber_class = EventObject
139
+ end
69
140
  end
70
141
 
71
- unless pattern
72
- AllMessages.new(subscriber)
73
- else
74
- subscriber
142
+ subscriber_class.new(pattern, listener)
143
+ end
144
+
145
+ class Matcher # :nodoc:
146
+ attr_reader :pattern, :exclusions
147
+
148
+ def self.wrap(pattern)
149
+ if String === pattern
150
+ pattern
151
+ elsif pattern.nil?
152
+ AllMessages.new
153
+ else
154
+ new(pattern)
155
+ end
156
+ end
157
+
158
+ def initialize(pattern)
159
+ @pattern = pattern
160
+ @exclusions = Set.new
161
+ end
162
+
163
+ def unsubscribe!(name)
164
+ exclusions << -name if pattern === name
165
+ end
166
+
167
+ def ===(name)
168
+ pattern === name && !exclusions.include?(name)
169
+ end
170
+
171
+ class AllMessages
172
+ def ===(name)
173
+ true
174
+ end
175
+
176
+ def unsubscribe!(*)
177
+ false
178
+ end
75
179
  end
76
180
  end
77
181
 
78
- class Evented #:nodoc:
182
+ class Evented # :nodoc:
183
+ attr_reader :pattern
184
+
79
185
  def initialize(pattern, delegate)
80
- @pattern = pattern
186
+ @pattern = Matcher.wrap(pattern)
81
187
  @delegate = delegate
82
188
  @can_publish = delegate.respond_to?(:publish)
189
+ @can_publish_event = delegate.respond_to?(:publish_event)
83
190
  end
84
191
 
85
192
  def publish(name, *args)
@@ -88,6 +195,14 @@ module ActiveSupport
88
195
  end
89
196
  end
90
197
 
198
+ def publish_event(event)
199
+ if @can_publish_event
200
+ @delegate.publish_event event
201
+ else
202
+ publish(event.name, event.time, event.end, event.transaction_id, event.payload)
203
+ end
204
+ end
205
+
91
206
  def start(name, id, payload)
92
207
  @delegate.start name, id, payload
93
208
  end
@@ -97,54 +212,72 @@ module ActiveSupport
97
212
  end
98
213
 
99
214
  def subscribed_to?(name)
100
- @pattern === name.to_s
215
+ pattern === name
101
216
  end
102
217
 
103
- def matches?(subscriber_or_name)
104
- self === subscriber_or_name ||
105
- @pattern && @pattern === subscriber_or_name
218
+ def unsubscribe!(name)
219
+ pattern.unsubscribe!(name)
106
220
  end
107
221
  end
108
222
 
109
- class Timed < Evented
223
+ class Timed < Evented # :nodoc:
110
224
  def publish(name, *args)
111
225
  @delegate.call name, *args
112
226
  end
113
227
 
114
228
  def start(name, id, payload)
115
- timestack = Thread.current[:_timestack] ||= []
229
+ timestack = IsolatedExecutionState[:_timestack] ||= []
116
230
  timestack.push Time.now
117
231
  end
118
232
 
119
233
  def finish(name, id, payload)
120
- timestack = Thread.current[:_timestack]
234
+ timestack = IsolatedExecutionState[:_timestack]
121
235
  started = timestack.pop
122
236
  @delegate.call(name, started, Time.now, id, payload)
123
237
  end
124
238
  end
125
239
 
126
- class AllMessages # :nodoc:
127
- def initialize(delegate)
128
- @delegate = delegate
240
+ class MonotonicTimed < Evented # :nodoc:
241
+ def publish(name, *args)
242
+ @delegate.call name, *args
129
243
  end
130
244
 
131
245
  def start(name, id, payload)
132
- @delegate.start name, id, payload
246
+ timestack = IsolatedExecutionState[:_timestack_monotonic] ||= []
247
+ timestack.push Process.clock_gettime(Process::CLOCK_MONOTONIC)
133
248
  end
134
249
 
135
250
  def finish(name, id, payload)
136
- @delegate.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)
137
254
  end
255
+ end
138
256
 
139
- def publish(name, *args)
140
- @delegate.publish name, *args
257
+ class EventObject < Evented
258
+ def start(name, id, payload)
259
+ stack = IsolatedExecutionState[:_event_stack] ||= []
260
+ event = build_event name, id, payload
261
+ event.start!
262
+ stack.push event
141
263
  end
142
264
 
143
- def subscribed_to?(name)
144
- true
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
145
271
  end
146
272
 
147
- alias :matches? :===
273
+ def publish_event(event)
274
+ @delegate.call event
275
+ end
276
+
277
+ private
278
+ def build_event(name, id, payload)
279
+ ActiveSupport::Notifications::Event.new name, nil, nil, id, payload
280
+ end
148
281
  end
149
282
  end
150
283
  end
@@ -1,4 +1,6 @@
1
- require 'securerandom'
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
2
4
 
3
5
  module ActiveSupport
4
6
  module Notifications
@@ -11,21 +13,28 @@ module ActiveSupport
11
13
  @notifier = notifier
12
14
  end
13
15
 
14
- # Instrument the given block by measuring the time taken to execute it
15
- # and publish it. Notice that events get sent even if an error occurs
16
- # in the passed-in block.
17
- def instrument(name, payload={})
18
- start name, payload
16
+ # Given a block, instrument it by measuring the time taken to execute
17
+ # and publish it. Without a block, simply send a message via the
18
+ # notifier. Notice that events get sent even if an error occurs in the
19
+ # passed-in block.
20
+ def instrument(name, payload = {})
21
+ # some of the listeners might have state
22
+ listeners_state = start name, payload
19
23
  begin
20
- yield payload
24
+ yield payload if block_given?
21
25
  rescue Exception => e
22
26
  payload[:exception] = [e.class.name, e.message]
27
+ payload[:exception_object] = e
23
28
  raise e
24
29
  ensure
25
- finish name, payload
30
+ finish_with_state listeners_state, name, payload
26
31
  end
27
32
  end
28
33
 
34
+ def new_event(name, payload = {}) # :nodoc:
35
+ Event.new(name, nil, nil, @id, payload)
36
+ end
37
+
29
38
  # Send a start notification with +name+ and +payload+.
30
39
  def start(name, payload)
31
40
  @notifier.start name, @id, payload
@@ -36,28 +45,92 @@ module ActiveSupport
36
45
  @notifier.finish name, @id, payload
37
46
  end
38
47
 
39
- private
40
-
41
- def unique_id
42
- SecureRandom.hex(10)
48
+ def finish_with_state(listeners_state, name, payload)
49
+ @notifier.finish name, @id, payload, listeners_state
43
50
  end
51
+
52
+ private
53
+ def unique_id
54
+ SecureRandom.hex(10)
55
+ end
44
56
  end
45
57
 
46
58
  class Event
47
- attr_reader :name, :time, :transaction_id, :payload, :children
48
- attr_accessor :end
59
+ attr_reader :name, :time, :end, :transaction_id, :children
60
+ attr_accessor :payload
49
61
 
50
62
  def initialize(name, start, ending, transaction_id, payload)
51
63
  @name = name
52
64
  @payload = payload.dup
53
- @time = start
65
+ @time = start ? start.to_f * 1_000.0 : start
54
66
  @transaction_id = transaction_id
55
- @end = ending
67
+ @end = ending ? ending.to_f * 1_000.0 : ending
56
68
  @children = []
69
+ @cpu_time_start = 0.0
70
+ @cpu_time_finish = 0.0
71
+ @allocation_count_start = 0
72
+ @allocation_count_finish = 0
73
+ end
74
+
75
+ def record
76
+ start!
77
+ begin
78
+ yield payload if block_given?
79
+ rescue Exception => e
80
+ payload[:exception] = [e.class.name, e.message]
81
+ payload[:exception_object] = e
82
+ raise e
83
+ ensure
84
+ finish!
85
+ end
86
+ end
87
+
88
+ # Record information at the time this event starts
89
+ def start!
90
+ @time = now
91
+ @cpu_time_start = now_cpu
92
+ @allocation_count_start = now_allocations
57
93
  end
58
94
 
95
+ # Record information at the time this event finishes
96
+ def finish!
97
+ @cpu_time_finish = now_cpu
98
+ @end = now
99
+ @allocation_count_finish = now_allocations
100
+ end
101
+
102
+ # Returns the CPU time (in milliseconds) passed since the call to
103
+ # +start!+ and the call to +finish!+
104
+ def cpu_time
105
+ @cpu_time_finish - @cpu_time_start
106
+ end
107
+
108
+ # Returns the idle time time (in milliseconds) passed since the call to
109
+ # +start!+ and the call to +finish!+
110
+ def idle_time
111
+ duration - cpu_time
112
+ end
113
+
114
+ # Returns the number of allocations made since the call to +start!+ and
115
+ # the call to +finish!+
116
+ def allocations
117
+ @allocation_count_finish - @allocation_count_start
118
+ end
119
+
120
+ # Returns the difference in milliseconds between when the execution of the
121
+ # event started and when it ended.
122
+ #
123
+ # ActiveSupport::Notifications.subscribe('wait') do |*args|
124
+ # @event = ActiveSupport::Notifications::Event.new(*args)
125
+ # end
126
+ #
127
+ # ActiveSupport::Notifications.instrument('wait') do
128
+ # sleep 1
129
+ # end
130
+ #
131
+ # @event.duration # => 1000.138
59
132
  def duration
60
- 1000.0 * (self.end - time)
133
+ self.end - time
61
134
  end
62
135
 
63
136
  def <<(event)
@@ -67,6 +140,33 @@ module ActiveSupport
67
140
  def parent_of?(event)
68
141
  @children.include? event
69
142
  end
143
+
144
+ private
145
+ def now
146
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
147
+ end
148
+
149
+ begin
150
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
151
+
152
+ def now_cpu
153
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
154
+ end
155
+ rescue
156
+ def now_cpu # rubocop:disable Lint/DuplicateMethods
157
+ 0.0
158
+ end
159
+ end
160
+
161
+ if GC.stat.key?(:total_allocated_objects)
162
+ def now_allocations
163
+ GC.stat(:total_allocated_objects)
164
+ end
165
+ else # Likely on JRuby, TruffleRuby
166
+ def now_allocations
167
+ 0
168
+ end
169
+ end
70
170
  end
71
171
  end
72
172
  end