activesupport 6.0.6.1 → 7.1.3.2

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 (245) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +865 -438
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -6
  5. data/lib/active_support/actionable_error.rb +4 -2
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +30 -10
  8. data/lib/active_support/benchmarkable.rb +4 -3
  9. data/lib/active_support/broadcast_logger.rb +250 -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 +208 -63
  15. data/lib/active_support/cache/memory_store.rb +120 -38
  16. data/lib/active_support/cache/null_store.rb +16 -2
  17. data/lib/active_support/cache/redis_cache_store.rb +201 -208
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +73 -66
  20. data/lib/active_support/cache.rb +539 -261
  21. data/lib/active_support/callbacks.rb +273 -142
  22. data/lib/active_support/code_generator.rb +65 -0
  23. data/lib/active_support/concern.rb +53 -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 +19 -6
  28. data/lib/active_support/configuration_file.rb +51 -0
  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/benchmark.rb +2 -2
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  35. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  36. data/lib/active_support/core_ext/class/subclasses.rb +19 -29
  37. data/lib/active_support/core_ext/date/blank.rb +1 -1
  38. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  39. data/lib/active_support/core_ext/date/conversions.rb +18 -16
  40. data/lib/active_support/core_ext/date_and_time/calculations.rb +27 -4
  41. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  42. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  43. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  44. data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
  45. data/lib/active_support/core_ext/digest/uuid.rb +30 -13
  46. data/lib/active_support/core_ext/enumerable.rb +146 -72
  47. data/lib/active_support/core_ext/erb/util.rb +196 -0
  48. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  49. data/lib/active_support/core_ext/hash/conversions.rb +3 -4
  50. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  51. data/lib/active_support/core_ext/hash/deep_transform_values.rb +4 -4
  52. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  53. data/lib/active_support/core_ext/hash/keys.rb +5 -5
  54. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  55. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  56. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  57. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  58. data/lib/active_support/core_ext/load_error.rb +1 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +31 -29
  61. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +51 -20
  62. data/lib/active_support/core_ext/module/concerning.rb +14 -8
  63. data/lib/active_support/core_ext/module/delegation.rb +75 -42
  64. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  65. data/lib/active_support/core_ext/module/introspection.rb +1 -26
  66. data/lib/active_support/core_ext/name_error.rb +23 -2
  67. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  68. data/lib/active_support/core_ext/numeric/conversions.rb +82 -73
  69. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  70. data/lib/active_support/core_ext/object/blank.rb +2 -2
  71. data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
  72. data/lib/active_support/core_ext/object/duplicable.rb +15 -4
  73. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  74. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  75. data/lib/active_support/core_ext/object/json.rb +52 -28
  76. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  77. data/lib/active_support/core_ext/object/try.rb +20 -20
  78. data/lib/active_support/core_ext/object/with.rb +44 -0
  79. data/lib/active_support/core_ext/object/with_options.rb +25 -6
  80. data/lib/active_support/core_ext/object.rb +1 -0
  81. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  82. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  83. data/lib/active_support/core_ext/pathname.rb +4 -0
  84. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  85. data/lib/active_support/core_ext/range/conversions.rb +34 -13
  86. data/lib/active_support/core_ext/range/each.rb +1 -1
  87. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  88. data/lib/active_support/core_ext/range.rb +1 -2
  89. data/lib/active_support/core_ext/regexp.rb +8 -1
  90. data/lib/active_support/core_ext/securerandom.rb +25 -13
  91. data/lib/active_support/core_ext/string/access.rb +5 -24
  92. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  93. data/lib/active_support/core_ext/string/filters.rb +21 -15
  94. data/lib/active_support/core_ext/string/indent.rb +1 -1
  95. data/lib/active_support/core_ext/string/inflections.rb +51 -10
  96. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  97. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  98. data/lib/active_support/core_ext/string/output_safety.rb +85 -194
  99. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  100. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  101. data/lib/active_support/core_ext/symbol.rb +3 -0
  102. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  103. data/lib/active_support/core_ext/time/calculations.rb +46 -8
  104. data/lib/active_support/core_ext/time/conversions.rb +16 -13
  105. data/lib/active_support/core_ext/time/zones.rb +12 -28
  106. data/lib/active_support/core_ext.rb +2 -1
  107. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  108. data/lib/active_support/current_attributes.rb +54 -22
  109. data/lib/active_support/deep_mergeable.rb +53 -0
  110. data/lib/active_support/dependencies/autoload.rb +17 -12
  111. data/lib/active_support/dependencies/interlock.rb +10 -18
  112. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  113. data/lib/active_support/dependencies.rb +58 -769
  114. data/lib/active_support/deprecation/behaviors.rb +77 -38
  115. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  116. data/lib/active_support/deprecation/deprecators.rb +104 -0
  117. data/lib/active_support/deprecation/disallowed.rb +54 -0
  118. data/lib/active_support/deprecation/instance_delegator.rb +31 -5
  119. data/lib/active_support/deprecation/method_wrappers.rb +12 -28
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +40 -25
  121. data/lib/active_support/deprecation/reporting.rb +76 -16
  122. data/lib/active_support/deprecation.rb +36 -4
  123. data/lib/active_support/deprecator.rb +7 -0
  124. data/lib/active_support/descendants_tracker.rb +150 -68
  125. data/lib/active_support/digest.rb +5 -3
  126. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  127. data/lib/active_support/duration/iso8601_serializer.rb +24 -12
  128. data/lib/active_support/duration.rb +136 -56
  129. data/lib/active_support/encrypted_configuration.rb +72 -9
  130. data/lib/active_support/encrypted_file.rb +46 -13
  131. data/lib/active_support/environment_inquirer.rb +40 -0
  132. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  133. data/lib/active_support/error_reporter.rb +203 -0
  134. data/lib/active_support/evented_file_update_checker.rb +86 -137
  135. data/lib/active_support/execution_context/test_helper.rb +13 -0
  136. data/lib/active_support/execution_context.rb +53 -0
  137. data/lib/active_support/execution_wrapper.rb +31 -12
  138. data/lib/active_support/executor/test_helper.rb +7 -0
  139. data/lib/active_support/file_update_checker.rb +4 -2
  140. data/lib/active_support/fork_tracker.rb +79 -0
  141. data/lib/active_support/gem_version.rb +5 -5
  142. data/lib/active_support/gzip.rb +2 -0
  143. data/lib/active_support/hash_with_indifferent_access.rb +86 -42
  144. data/lib/active_support/html_safe_translation.rb +53 -0
  145. data/lib/active_support/i18n.rb +2 -1
  146. data/lib/active_support/i18n_railtie.rb +29 -27
  147. data/lib/active_support/inflector/inflections.rb +26 -9
  148. data/lib/active_support/inflector/methods.rb +54 -64
  149. data/lib/active_support/inflector/transliterate.rb +7 -5
  150. data/lib/active_support/isolated_execution_state.rb +76 -0
  151. data/lib/active_support/json/decoding.rb +6 -5
  152. data/lib/active_support/json/encoding.rb +31 -45
  153. data/lib/active_support/key_generator.rb +32 -7
  154. data/lib/active_support/lazy_load_hooks.rb +33 -7
  155. data/lib/active_support/locale/en.yml +10 -4
  156. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  157. data/lib/active_support/log_subscriber.rb +101 -32
  158. data/lib/active_support/logger.rb +9 -60
  159. data/lib/active_support/logger_silence.rb +2 -26
  160. data/lib/active_support/logger_thread_safe_level.rb +24 -25
  161. data/lib/active_support/message_encryptor.rb +205 -58
  162. data/lib/active_support/message_encryptors.rb +141 -0
  163. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  164. data/lib/active_support/message_pack/extensions.rb +292 -0
  165. data/lib/active_support/message_pack/serializer.rb +63 -0
  166. data/lib/active_support/message_pack.rb +50 -0
  167. data/lib/active_support/message_verifier.rb +237 -86
  168. data/lib/active_support/message_verifiers.rb +135 -0
  169. data/lib/active_support/messages/codec.rb +65 -0
  170. data/lib/active_support/messages/metadata.rb +112 -46
  171. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  172. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  173. data/lib/active_support/messages/rotator.rb +35 -32
  174. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  175. data/lib/active_support/multibyte/chars.rb +15 -52
  176. data/lib/active_support/multibyte/unicode.rb +8 -122
  177. data/lib/active_support/multibyte.rb +1 -1
  178. data/lib/active_support/notifications/fanout.rb +310 -105
  179. data/lib/active_support/notifications/instrumenter.rb +113 -48
  180. data/lib/active_support/notifications.rb +56 -29
  181. data/lib/active_support/number_helper/number_converter.rb +15 -8
  182. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  183. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  184. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  185. data/lib/active_support/number_helper/number_to_human_size_converter.rb +5 -5
  186. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  187. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  188. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  189. data/lib/active_support/number_helper.rb +379 -304
  190. data/lib/active_support/option_merger.rb +11 -18
  191. data/lib/active_support/ordered_hash.rb +4 -4
  192. data/lib/active_support/ordered_options.rb +23 -3
  193. data/lib/active_support/parameter_filter.rb +104 -75
  194. data/lib/active_support/proxy_object.rb +2 -0
  195. data/lib/active_support/rails.rb +1 -4
  196. data/lib/active_support/railtie.rb +90 -6
  197. data/lib/active_support/reloader.rb +12 -4
  198. data/lib/active_support/rescuable.rb +18 -16
  199. data/lib/active_support/ruby_features.rb +7 -0
  200. data/lib/active_support/secure_compare_rotator.rb +58 -0
  201. data/lib/active_support/security_utils.rb +19 -12
  202. data/lib/active_support/string_inquirer.rb +5 -3
  203. data/lib/active_support/subscriber.rb +23 -47
  204. data/lib/active_support/syntax_error_proxy.rb +70 -0
  205. data/lib/active_support/tagged_logging.rb +84 -23
  206. data/lib/active_support/test_case.rb +166 -27
  207. data/lib/active_support/testing/assertions.rb +73 -20
  208. data/lib/active_support/testing/autorun.rb +0 -2
  209. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  210. data/lib/active_support/testing/deprecation.rb +53 -2
  211. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  212. data/lib/active_support/testing/isolation.rb +30 -29
  213. data/lib/active_support/testing/method_call_assertions.rb +24 -11
  214. data/lib/active_support/testing/parallelization/server.rb +82 -0
  215. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  216. data/lib/active_support/testing/parallelization.rb +16 -95
  217. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  218. data/lib/active_support/testing/stream.rb +4 -6
  219. data/lib/active_support/testing/strict_warnings.rb +39 -0
  220. data/lib/active_support/testing/tagged_logging.rb +1 -1
  221. data/lib/active_support/testing/time_helpers.rb +89 -19
  222. data/lib/active_support/time_with_zone.rb +105 -70
  223. data/lib/active_support/values/time_zone.rb +59 -26
  224. data/lib/active_support/version.rb +1 -1
  225. data/lib/active_support/xml_mini/jdom.rb +4 -11
  226. data/lib/active_support/xml_mini/libxml.rb +5 -5
  227. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  228. data/lib/active_support/xml_mini/nokogiri.rb +5 -5
  229. data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
  230. data/lib/active_support/xml_mini/rexml.rb +9 -2
  231. data/lib/active_support/xml_mini.rb +7 -6
  232. data/lib/active_support.rb +40 -1
  233. metadata +127 -40
  234. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  235. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  236. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  237. data/lib/active_support/core_ext/marshal.rb +0 -24
  238. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  239. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  240. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  241. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -23
  242. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  243. data/lib/active_support/core_ext/uri.rb +0 -25
  244. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
  245. data/lib/active_support/per_thread_registry.rb +0 -60
@@ -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,28 @@ 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
+
82
+ def new_event(name, payload = {}) # :nodoc:
83
+ Event.new(name, nil, nil, @id, payload)
84
+ end
85
+
34
86
  # Send a start notification with +name+ and +payload+.
35
87
  def start(name, payload)
36
88
  @notifier.start name, @id, payload
@@ -52,28 +104,34 @@ module ActiveSupport
52
104
  end
53
105
 
54
106
  class Event
55
- attr_reader :name, :time, :end, :transaction_id, :payload, :children
56
-
57
- def self.clock_gettime_supported? # :nodoc:
58
- defined?(Process::CLOCK_THREAD_CPUTIME_ID) &&
59
- !Gem.win_platform? &&
60
- !RUBY_PLATFORM.match?(/solaris/i)
61
- end
62
- private_class_method :clock_gettime_supported?
107
+ attr_reader :name, :time, :end, :transaction_id
108
+ attr_accessor :payload
63
109
 
64
110
  def initialize(name, start, ending, transaction_id, payload)
65
111
  @name = name
66
112
  @payload = payload.dup
67
- @time = start
113
+ @time = start ? start.to_f * 1_000.0 : start
68
114
  @transaction_id = transaction_id
69
- @end = ending
70
- @children = []
71
- @cpu_time_start = 0
72
- @cpu_time_finish = 0
115
+ @end = ending ? ending.to_f * 1_000.0 : ending
116
+ @cpu_time_start = 0.0
117
+ @cpu_time_finish = 0.0
73
118
  @allocation_count_start = 0
74
119
  @allocation_count_finish = 0
75
120
  end
76
121
 
122
+ def record
123
+ start!
124
+ begin
125
+ yield payload if block_given?
126
+ rescue Exception => e
127
+ payload[:exception] = [e.class.name, e.message]
128
+ payload[:exception_object] = e
129
+ raise e
130
+ ensure
131
+ finish!
132
+ end
133
+ end
134
+
77
135
  # Record information at the time this event starts
78
136
  def start!
79
137
  @time = now
@@ -88,29 +146,42 @@ module ActiveSupport
88
146
  @allocation_count_finish = now_allocations
89
147
  end
90
148
 
91
- def end=(ending)
92
- ActiveSupport::Deprecation.deprecation_warning(:end=, :finish!)
93
- @end = ending
94
- end
95
-
96
- # Returns the CPU time (in milliseconds) passed since the call to
97
- # +start!+ and the call to +finish!+
149
+ # Returns the CPU time (in milliseconds) passed between the call to
150
+ # #start! and the call to #finish!.
98
151
  def cpu_time
99
- (@cpu_time_finish - @cpu_time_start) * 1000
152
+ @cpu_time_finish - @cpu_time_start
100
153
  end
101
154
 
102
- # Returns the idle time time (in milliseconds) passed since the call to
103
- # +start!+ and the call to +finish!+
155
+ # Returns the idle time time (in milliseconds) passed between the call to
156
+ # #start! and the call to #finish!.
104
157
  def idle_time
105
- duration - cpu_time
158
+ diff = duration - cpu_time
159
+ diff > 0.0 ? diff : 0.0
106
160
  end
107
161
 
108
- # Returns the number of allocations made since the call to +start!+ and
109
- # the call to +finish!+
162
+ # Returns the number of allocations made between the call to #start! and
163
+ # the call to #finish!.
110
164
  def allocations
111
165
  @allocation_count_finish - @allocation_count_start
112
166
  end
113
167
 
168
+ def children # :nodoc:
169
+ ActiveSupport.deprecator.warn <<~EOM
170
+ ActiveSupport::Notifications::Event#children is deprecated and will
171
+ be removed in Rails 7.2.
172
+ EOM
173
+ []
174
+ end
175
+
176
+ def parent_of?(event) # :nodoc:
177
+ ActiveSupport.deprecator.warn <<~EOM
178
+ ActiveSupport::Notifications::Event#parent_of? is deprecated and will
179
+ be removed in Rails 7.2.
180
+ EOM
181
+ start = (time - event.time) * 1000
182
+ start <= 0 && (start + duration >= event.duration)
183
+ end
184
+
114
185
  # Returns the difference in milliseconds between when the execution of the
115
186
  # event started and when it ended.
116
187
  #
@@ -124,39 +195,33 @@ module ActiveSupport
124
195
  #
125
196
  # @event.duration # => 1000.138
126
197
  def duration
127
- 1000.0 * (self.end - time)
128
- end
129
-
130
- def <<(event)
131
- @children << event
132
- end
133
-
134
- def parent_of?(event)
135
- @children.include? event
198
+ self.end - time
136
199
  end
137
200
 
138
201
  private
139
202
  def now
140
- Concurrent.monotonic_time
203
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
141
204
  end
142
205
 
143
- if clock_gettime_supported?
206
+ begin
207
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
208
+
144
209
  def now_cpu
145
- Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
210
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
146
211
  end
147
- else
148
- def now_cpu
149
- 0
212
+ rescue
213
+ def now_cpu # rubocop:disable Lint/DuplicateMethods
214
+ 0.0
150
215
  end
151
216
  end
152
217
 
153
- if defined?(JRUBY_VERSION)
218
+ if GC.stat.key?(:total_allocated_objects)
154
219
  def now_allocations
155
- 0
220
+ GC.stat(:total_allocated_objects)
156
221
  end
157
- else
222
+ else # Likely on JRuby, TruffleRuby
158
223
  def now_allocations
159
- GC.stat :total_allocated_objects
224
+ 0
160
225
  end
161
226
  end
162
227
  end
@@ -2,12 +2,11 @@
2
2
 
3
3
  require "active_support/notifications/instrumenter"
4
4
  require "active_support/notifications/fanout"
5
- require "active_support/per_thread_registry"
6
5
 
7
6
  module ActiveSupport
8
- # = Notifications
7
+ # = \Notifications
9
8
  #
10
- # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
9
+ # +ActiveSupport::Notifications+ provides an instrumentation API for
11
10
  # Ruby.
12
11
  #
13
12
  # == Instrumenters
@@ -38,6 +37,19 @@ module ActiveSupport
38
37
  # payload # => Hash, the payload
39
38
  # end
40
39
  #
40
+ # Here, the +start+ and +finish+ values represent wall-clock time. If you are
41
+ # concerned about accuracy, you can register a monotonic subscriber.
42
+ #
43
+ # ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
44
+ # name # => String, name of the event (such as 'render' from above)
45
+ # start # => Monotonic time, when the instrumented block started execution
46
+ # finish # => Monotonic time, when the instrumented block ended execution
47
+ # id # => String, unique ID for the instrumenter that fired the event
48
+ # payload # => Hash, the payload
49
+ # end
50
+ #
51
+ # The +start+ and +finish+ values above represent monotonic time.
52
+ #
41
53
  # For instance, let's store all "render" events in an array:
42
54
  #
43
55
  # events = []
@@ -72,7 +84,7 @@ module ActiveSupport
72
84
  # event.payload[:exception] # => ["ArgumentError", "Invalid value"]
73
85
  # event.payload[:exception_object] # => #<ArgumentError: Invalid value>
74
86
  #
75
- # As the earlier example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
87
+ # As the earlier example depicts, the class ActiveSupport::Notifications::Event
76
88
  # is able to take the arguments as they come and provide an object-oriented
77
89
  # interface to that data.
78
90
  #
@@ -135,6 +147,16 @@ module ActiveSupport
135
147
  # during the execution of the block. The callback is unsubscribed automatically
136
148
  # after that.
137
149
  #
150
+ # To record +started+ and +finished+ values with monotonic time,
151
+ # specify the optional <tt>:monotonic</tt> option to the
152
+ # <tt>subscribed</tt> method. The <tt>:monotonic</tt> option is set
153
+ # to +false+ by default.
154
+ #
155
+ # callback = lambda {|name, started, finished, unique_id, payload| ... }
156
+ # ActiveSupport::Notifications.subscribed(callback, "sql.active_record", monotonic: true) do
157
+ # ...
158
+ # end
159
+ #
138
160
  # === Manual Unsubscription
139
161
  #
140
162
  # The +subscribe+ method returns a subscriber object:
@@ -155,7 +177,7 @@ module ActiveSupport
155
177
  #
156
178
  # Subscribers using a regexp or other pattern-matching object will remain subscribed
157
179
  # to all events that match their original pattern, unless those events match a string
158
- # passed to `unsubscribe`:
180
+ # passed to +unsubscribe+:
159
181
  #
160
182
  # subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
161
183
  # ActiveSupport::Notifications.unsubscribe('render_template.action_view')
@@ -175,6 +197,10 @@ module ActiveSupport
175
197
  notifier.publish(name, *args)
176
198
  end
177
199
 
200
+ def publish_event(event) # :nodoc:
201
+ notifier.publish_event(event)
202
+ end
203
+
178
204
  def instrument(name, payload = {})
179
205
  if notifier.listening?(name)
180
206
  instrumenter.instrument(name, payload) { yield payload if block_given? }
@@ -208,12 +234,28 @@ module ActiveSupport
208
234
  # ActiveSupport::Notifications.subscribe(/render/) do |event|
209
235
  # @event = event
210
236
  # end
211
- def subscribe(*args, &block)
212
- notifier.subscribe(*args, &block)
237
+ #
238
+ # Raises an error if invalid event name type is passed:
239
+ #
240
+ # ActiveSupport::Notifications.subscribe(:render) {|*args| ...}
241
+ # #=> ArgumentError (pattern must be specified as a String, Regexp or empty)
242
+ #
243
+ def subscribe(pattern = nil, callback = nil, &block)
244
+ notifier.subscribe(pattern, callback, monotonic: false, &block)
245
+ end
246
+
247
+ # Performs the same functionality as #subscribe, but the +start+ and
248
+ # +finish+ block arguments are in monotonic time instead of wall-clock
249
+ # time. Monotonic time will not jump forward or backward (due to NTP or
250
+ # Daylights Savings). Use +monotonic_subscribe+ when accuracy of time
251
+ # duration is important. For example, computing elapsed time between
252
+ # two events.
253
+ def monotonic_subscribe(pattern = nil, callback = nil, &block)
254
+ notifier.subscribe(pattern, callback, monotonic: true, &block)
213
255
  end
214
256
 
215
- def subscribed(callback, *args, &block)
216
- subscriber = subscribe(*args, &callback)
257
+ def subscribed(callback, pattern = nil, monotonic: false, &block)
258
+ subscriber = notifier.subscribe(pattern, callback, monotonic: monotonic)
217
259
  yield
218
260
  ensure
219
261
  unsubscribe(subscriber)
@@ -224,28 +266,13 @@ module ActiveSupport
224
266
  end
225
267
 
226
268
  def instrumenter
227
- InstrumentationRegistry.instance.instrumenter_for(notifier)
228
- end
229
- end
230
-
231
- # This class is a registry which holds all of the +Instrumenter+ objects
232
- # in a particular thread local. To access the +Instrumenter+ object for a
233
- # particular +notifier+, you can call the following method:
234
- #
235
- # InstrumentationRegistry.instrumenter_for(notifier)
236
- #
237
- # The instrumenters for multiple notifiers are held in a single instance of
238
- # this class.
239
- class InstrumentationRegistry # :nodoc:
240
- extend ActiveSupport::PerThreadRegistry
241
-
242
- def initialize
243
- @registry = {}
269
+ registry[notifier] ||= Instrumenter.new(notifier)
244
270
  end
245
271
 
246
- def instrumenter_for(notifier)
247
- @registry[notifier] ||= Instrumenter.new(notifier)
248
- end
272
+ private
273
+ def registry
274
+ ActiveSupport::IsolatedExecutionState[:active_support_notifications_registry] ||= {}
275
+ end
249
276
  end
250
277
 
251
278
  self.notifier = Fanout.new
@@ -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"
@@ -30,7 +31,7 @@ module ActiveSupport
30
31
  # If set to true, precision will mean the number of significant digits instead
31
32
  # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
32
33
  significant: false,
33
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
34
+ # If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2)
34
35
  strip_insignificant_zeros: false
35
36
  },
36
37
 
@@ -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,10 +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)
178
- rescue ArgumentError, TypeError
179
- 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
180
187
  end
181
188
  end
182
189
  end
@@ -8,16 +8,21 @@ module ActiveSupport
8
8
  self.namespace = :currency
9
9
 
10
10
  def convert
11
- number = self.number.to_s.strip
12
11
  format = options[:format]
13
12
 
14
- if number.sub!(/^-/, "") &&
15
- (options[:precision] != 0 || number.to_f > 0.5)
16
- format = options[:negative_format]
13
+ number_d = valid_bigdecimal
14
+ if number_d
15
+ if number_d.negative?
16
+ number_d = number_d.abs
17
+ format = options[:negative_format] if (number_d * 10**options[:precision]) >= 0.5
18
+ end
19
+ number_s = NumberToRoundedConverter.convert(number_d, options)
20
+ else
21
+ number_s = number.to_s.strip
22
+ format = options[:negative_format] if number_s.sub!(/^-/, "")
17
23
  end
18
24
 
19
- rounded_number = NumberToRoundedConverter.convert(number, options)
20
- format.gsub("%n", rounded_number).gsub("%u", options[:unit])
25
+ format.gsub("%n", number_s).gsub("%u", options[:unit])
21
26
  end
22
27
 
23
28
  private
@@ -4,7 +4,7 @@ require "active_support/number_helper/number_converter"
4
4
 
5
5
  module ActiveSupport
6
6
  module NumberHelper
7
- class NumberToDelimitedConverter < NumberConverter #:nodoc:
7
+ class NumberToDelimitedConverter < NumberConverter # :nodoc:
8
8
  self.validate_float = true
9
9
 
10
10
  DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
@@ -16,7 +16,7 @@ module ActiveSupport
16
16
  @number = RoundingHelper.new(options).round(number)
17
17
  @number = Float(number)
18
18
 
19
- # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
19
+ # For backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files.
20
20
  unless options.key?(:strip_insignificant_zeros)
21
21
  options[:strip_insignificant_zeros] = true
22
22
  end
@@ -4,8 +4,8 @@ require "active_support/number_helper/number_converter"
4
4
 
5
5
  module ActiveSupport
6
6
  module NumberHelper
7
- class NumberToHumanSizeConverter < NumberConverter #:nodoc:
8
- STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb]
7
+ class NumberToHumanSizeConverter < NumberConverter # :nodoc:
8
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb, :zb]
9
9
 
10
10
  self.namespace = :human
11
11
  self.validate_float = true
@@ -13,7 +13,7 @@ module ActiveSupport
13
13
  def convert
14
14
  @number = Float(number)
15
15
 
16
- # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
16
+ # For backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files.
17
17
  unless options.key?(:strip_insignificant_zeros)
18
18
  options[:strip_insignificant_zeros] = true
19
19
  end
@@ -43,13 +43,13 @@ module ActiveSupport
43
43
 
44
44
  def exponent
45
45
  max = STORAGE_UNITS.size - 1
46
- exp = (Math.log(number) / Math.log(base)).to_i
46
+ exp = (Math.log(number.abs) / Math.log(base)).to_i
47
47
  exp = max if exp > max # avoid overflow for the highest unit
48
48
  exp
49
49
  end
50
50
 
51
51
  def smaller_than_base?
52
- number.to_i < base
52
+ number.to_i.abs < base
53
53
  end
54
54
 
55
55
  def base
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/object/blank"
3
4
  require "active_support/number_helper/number_converter"
4
5
 
5
6
  module ActiveSupport
6
7
  module NumberHelper
7
- class NumberToPhoneConverter < NumberConverter #:nodoc:
8
+ class NumberToPhoneConverter < NumberConverter # :nodoc:
8
9
  def convert
9
10
  str = country_code(opts[:country_code]).dup
10
11
  str << convert_to_phone_number(number.to_s.strip)
@@ -20,14 +20,18 @@ module ActiveSupport
20
20
  end
21
21
 
22
22
  formatted_string =
23
- if BigDecimal === rounded_number && rounded_number.finite?
23
+ if rounded_number.finite?
24
24
  s = rounded_number.to_s("F")
25
- s << "0" * precision
26
25
  a, b = s.split(".", 2)
27
- a << "."
28
- a << b[0, precision]
26
+ if precision != 0
27
+ b << "0" * precision
28
+ a << "."
29
+ a << b[0, precision]
30
+ end
31
+ a
29
32
  else
30
- "%00.#{precision}f" % rounded_number
33
+ # Infinity/NaN
34
+ "%f" % rounded_number
31
35
  end
32
36
  else
33
37
  formatted_string = rounded_number
@@ -10,56 +10,36 @@ module ActiveSupport
10
10
  end
11
11
 
12
12
  def round(number)
13
+ precision = absolute_precision(number)
13
14
  return number unless precision
14
- number = convert_to_decimal(number)
15
- if significant && precision > 0
16
- round_significant(number)
17
- else
18
- round_without_significant(number)
19
- end
15
+
16
+ rounded_number = convert_to_decimal(number).round(precision, options.fetch(:round_mode, :default).to_sym)
17
+ rounded_number.zero? ? rounded_number.abs : rounded_number # prevent showing negative zeros
20
18
  end
21
19
 
22
20
  def digit_count(number)
23
21
  return 1 if number.zero?
24
- (Math.log10(absolute_number(number)) + 1).floor
22
+ (Math.log10(number.abs) + 1).floor
25
23
  end
26
24
 
27
25
  private
28
- def round_without_significant(number)
29
- number = number.round(precision, BigDecimal.mode(BigDecimal::ROUND_MODE))
30
- number = number.to_i if precision == 0 && number.finite?
31
- number = number.abs if number.zero? # prevent showing negative zeros
32
- number
33
- end
34
-
35
- def round_significant(number)
36
- return 0 if number.zero?
37
- digits = digit_count(number)
38
- multiplier = 10**(digits - precision)
39
- (number / BigDecimal(multiplier.to_f.to_s)).round * multiplier
40
- end
41
-
42
26
  def convert_to_decimal(number)
43
27
  case number
44
28
  when Float, String
45
29
  BigDecimal(number.to_s)
46
30
  when Rational
47
- BigDecimal(number, digit_count(number.to_i) + precision)
31
+ BigDecimal(number, digit_count(number.to_i) + options[:precision])
48
32
  else
49
33
  number.to_d
50
34
  end
51
35
  end
52
36
 
53
- def precision
54
- options[:precision]
55
- end
56
-
57
- def significant
58
- options[:significant]
59
- end
60
-
61
- def absolute_number(number)
62
- number.respond_to?(:abs) ? number.abs : number.to_d.abs
37
+ def absolute_precision(number)
38
+ if options[:significant] && options[:precision] > 0
39
+ options[:precision] - digit_count(convert_to_decimal(number))
40
+ else
41
+ options[:precision]
42
+ end
63
43
  end
64
44
  end
65
45
  end