activesupport 6.0.4.4 → 7.0.4.1

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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +257 -532
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +2 -2
  6. data/lib/active_support/backtrace_cleaner.rb +5 -5
  7. data/lib/active_support/benchmarkable.rb +3 -3
  8. data/lib/active_support/cache/file_store.rb +16 -10
  9. data/lib/active_support/cache/mem_cache_store.rb +163 -42
  10. data/lib/active_support/cache/memory_store.rb +57 -29
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +79 -98
  13. data/lib/active_support/cache/strategy/local_cache.rb +49 -57
  14. data/lib/active_support/cache.rb +378 -179
  15. data/lib/active_support/callbacks.rb +230 -122
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +49 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  19. data/lib/active_support/concurrency/share_lock.rb +2 -2
  20. data/lib/active_support/configurable.rb +9 -6
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +1 -5
  23. data/lib/active_support/core_ext/array/conversions.rb +13 -12
  24. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  25. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  26. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  27. data/lib/active_support/core_ext/array.rb +1 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  30. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  31. data/lib/active_support/core_ext/class/subclasses.rb +9 -22
  32. data/lib/active_support/core_ext/date/blank.rb +1 -1
  33. data/lib/active_support/core_ext/date/calculations.rb +9 -9
  34. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  35. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  36. data/lib/active_support/core_ext/date.rb +1 -0
  37. data/lib/active_support/core_ext/date_and_time/calculations.rb +17 -4
  38. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  39. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  41. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  42. data/lib/active_support/core_ext/date_time.rb +1 -0
  43. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  44. data/lib/active_support/core_ext/enumerable.rb +164 -23
  45. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  46. data/lib/active_support/core_ext/hash/conversions.rb +2 -3
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  49. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  50. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  51. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  52. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  53. data/lib/active_support/core_ext/load_error.rb +1 -1
  54. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  57. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  58. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  59. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  60. data/lib/active_support/core_ext/name_error.rb +23 -2
  61. data/lib/active_support/core_ext/numeric/conversions.rb +80 -73
  62. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  63. data/lib/active_support/core_ext/numeric.rb +1 -0
  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 +1 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  68. data/lib/active_support/core_ext/object/json.rb +42 -26
  69. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  70. data/lib/active_support/core_ext/object/try.rb +20 -20
  71. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  72. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  73. data/lib/active_support/core_ext/pathname.rb +3 -0
  74. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  75. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  76. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  77. data/lib/active_support/core_ext/range/each.rb +1 -1
  78. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  79. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  80. data/lib/active_support/core_ext/range.rb +1 -1
  81. data/lib/active_support/core_ext/regexp.rb +8 -1
  82. data/lib/active_support/core_ext/securerandom.rb +1 -1
  83. data/lib/active_support/core_ext/string/access.rb +5 -24
  84. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  85. data/lib/active_support/core_ext/string/filters.rb +1 -1
  86. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  87. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  88. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  89. data/lib/active_support/core_ext/string/output_safety.rb +92 -41
  90. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  92. data/lib/active_support/core_ext/symbol.rb +3 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +25 -7
  94. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  95. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  96. data/lib/active_support/core_ext/time/zones.rb +7 -22
  97. data/lib/active_support/core_ext/time.rb +1 -0
  98. data/lib/active_support/core_ext/uri.rb +3 -23
  99. data/lib/active_support/core_ext.rb +2 -1
  100. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  101. data/lib/active_support/current_attributes.rb +39 -16
  102. data/lib/active_support/dependencies/interlock.rb +10 -18
  103. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  104. data/lib/active_support/dependencies.rb +58 -769
  105. data/lib/active_support/deprecation/behaviors.rb +23 -7
  106. data/lib/active_support/deprecation/disallowed.rb +56 -0
  107. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  108. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
  110. data/lib/active_support/deprecation/reporting.rb +50 -7
  111. data/lib/active_support/deprecation.rb +7 -2
  112. data/lib/active_support/descendants_tracker.rb +174 -64
  113. data/lib/active_support/digest.rb +5 -3
  114. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  115. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  116. data/lib/active_support/duration.rb +134 -55
  117. data/lib/active_support/encrypted_configuration.rb +13 -2
  118. data/lib/active_support/encrypted_file.rb +32 -3
  119. data/lib/active_support/environment_inquirer.rb +20 -0
  120. data/lib/active_support/error_reporter.rb +117 -0
  121. data/lib/active_support/evented_file_update_checker.rb +72 -138
  122. data/lib/active_support/execution_context/test_helper.rb +13 -0
  123. data/lib/active_support/execution_context.rb +53 -0
  124. data/lib/active_support/execution_wrapper.rb +43 -21
  125. data/lib/active_support/executor/test_helper.rb +7 -0
  126. data/lib/active_support/fork_tracker.rb +71 -0
  127. data/lib/active_support/gem_version.rb +3 -3
  128. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  129. data/lib/active_support/html_safe_translation.rb +43 -0
  130. data/lib/active_support/i18n.rb +1 -0
  131. data/lib/active_support/i18n_railtie.rb +14 -19
  132. data/lib/active_support/inflector/inflections.rb +24 -9
  133. data/lib/active_support/inflector/methods.rb +29 -49
  134. data/lib/active_support/inflector/transliterate.rb +5 -5
  135. data/lib/active_support/isolated_execution_state.rb +72 -0
  136. data/lib/active_support/json/decoding.rb +4 -4
  137. data/lib/active_support/json/encoding.rb +8 -4
  138. data/lib/active_support/key_generator.rb +23 -6
  139. data/lib/active_support/lazy_load_hooks.rb +28 -4
  140. data/lib/active_support/locale/en.yml +8 -4
  141. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  142. data/lib/active_support/log_subscriber.rb +23 -5
  143. data/lib/active_support/logger.rb +1 -1
  144. data/lib/active_support/logger_silence.rb +2 -26
  145. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  146. data/lib/active_support/message_encryptor.rb +16 -13
  147. data/lib/active_support/message_verifier.rb +50 -18
  148. data/lib/active_support/messages/metadata.rb +2 -2
  149. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  150. data/lib/active_support/messages/rotator.rb +6 -5
  151. data/lib/active_support/multibyte/chars.rb +13 -52
  152. data/lib/active_support/multibyte/unicode.rb +1 -87
  153. data/lib/active_support/multibyte.rb +1 -1
  154. data/lib/active_support/notifications/fanout.rb +110 -69
  155. data/lib/active_support/notifications/instrumenter.rb +37 -29
  156. data/lib/active_support/notifications.rb +55 -28
  157. data/lib/active_support/number_helper/number_converter.rb +2 -4
  158. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  159. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  160. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  161. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  162. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  163. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  164. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  165. data/lib/active_support/number_helper.rb +29 -16
  166. data/lib/active_support/option_merger.rb +11 -18
  167. data/lib/active_support/ordered_hash.rb +1 -1
  168. data/lib/active_support/ordered_options.rb +9 -3
  169. data/lib/active_support/parameter_filter.rb +21 -11
  170. data/lib/active_support/per_thread_registry.rb +6 -1
  171. data/lib/active_support/rails.rb +1 -4
  172. data/lib/active_support/railtie.rb +77 -5
  173. data/lib/active_support/reloader.rb +1 -1
  174. data/lib/active_support/rescuable.rb +16 -16
  175. data/lib/active_support/ruby_features.rb +7 -0
  176. data/lib/active_support/secure_compare_rotator.rb +51 -0
  177. data/lib/active_support/security_utils.rb +19 -12
  178. data/lib/active_support/string_inquirer.rb +2 -2
  179. data/lib/active_support/subscriber.rb +19 -25
  180. data/lib/active_support/tagged_logging.rb +31 -6
  181. data/lib/active_support/test_case.rb +13 -21
  182. data/lib/active_support/testing/assertions.rb +50 -13
  183. data/lib/active_support/testing/deprecation.rb +52 -1
  184. data/lib/active_support/testing/isolation.rb +2 -2
  185. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  186. data/lib/active_support/testing/parallelization/server.rb +82 -0
  187. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  188. data/lib/active_support/testing/parallelization.rb +16 -95
  189. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  190. data/lib/active_support/testing/stream.rb +3 -5
  191. data/lib/active_support/testing/tagged_logging.rb +1 -1
  192. data/lib/active_support/testing/time_helpers.rb +53 -5
  193. data/lib/active_support/time_with_zone.rb +126 -62
  194. data/lib/active_support/values/time_zone.rb +54 -23
  195. data/lib/active_support/version.rb +1 -1
  196. data/lib/active_support/xml_mini/jdom.rb +1 -1
  197. data/lib/active_support/xml_mini/libxml.rb +5 -5
  198. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  199. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  200. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  201. data/lib/active_support/xml_mini/rexml.rb +9 -2
  202. data/lib/active_support/xml_mini.rb +5 -4
  203. data/lib/active_support.rb +29 -1
  204. metadata +46 -45
  205. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  206. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  207. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  208. data/lib/active_support/core_ext/marshal.rb +0 -24
  209. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  210. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  211. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  212. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -31,6 +31,10 @@ module ActiveSupport
31
31
  end
32
32
  end
33
33
 
34
+ def new_event(name, payload = {}) # :nodoc:
35
+ Event.new(name, nil, nil, @id, payload)
36
+ end
37
+
34
38
  # Send a start notification with +name+ and +payload+.
35
39
  def start(name, payload)
36
40
  @notifier.start name, @id, payload
@@ -52,28 +56,35 @@ module ActiveSupport
52
56
  end
53
57
 
54
58
  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?
59
+ attr_reader :name, :time, :end, :transaction_id, :children
60
+ attr_accessor :payload
63
61
 
64
62
  def initialize(name, start, ending, transaction_id, payload)
65
63
  @name = name
66
64
  @payload = payload.dup
67
- @time = start
65
+ @time = start ? start.to_f * 1_000.0 : start
68
66
  @transaction_id = transaction_id
69
- @end = ending
67
+ @end = ending ? ending.to_f * 1_000.0 : ending
70
68
  @children = []
71
- @cpu_time_start = 0
72
- @cpu_time_finish = 0
69
+ @cpu_time_start = 0.0
70
+ @cpu_time_finish = 0.0
73
71
  @allocation_count_start = 0
74
72
  @allocation_count_finish = 0
75
73
  end
76
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
+
77
88
  # Record information at the time this event starts
78
89
  def start!
79
90
  @time = now
@@ -88,15 +99,10 @@ module ActiveSupport
88
99
  @allocation_count_finish = now_allocations
89
100
  end
90
101
 
91
- def end=(ending)
92
- ActiveSupport::Deprecation.deprecation_warning(:end=, :finish!)
93
- @end = ending
94
- end
95
-
96
102
  # Returns the CPU time (in milliseconds) passed since the call to
97
103
  # +start!+ and the call to +finish!+
98
104
  def cpu_time
99
- (@cpu_time_finish - @cpu_time_start) * 1000
105
+ @cpu_time_finish - @cpu_time_start
100
106
  end
101
107
 
102
108
  # Returns the idle time time (in milliseconds) passed since the call to
@@ -124,7 +130,7 @@ module ActiveSupport
124
130
  #
125
131
  # @event.duration # => 1000.138
126
132
  def duration
127
- 1000.0 * (self.end - time)
133
+ self.end - time
128
134
  end
129
135
 
130
136
  def <<(event)
@@ -137,26 +143,28 @@ module ActiveSupport
137
143
 
138
144
  private
139
145
  def now
140
- Concurrent.monotonic_time
146
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
141
147
  end
142
148
 
143
- if clock_gettime_supported?
149
+ begin
150
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
151
+
144
152
  def now_cpu
145
- Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID)
153
+ Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
146
154
  end
147
- else
148
- def now_cpu
149
- 0
155
+ rescue
156
+ def now_cpu # rubocop:disable Lint/DuplicateMethods
157
+ 0.0
150
158
  end
151
159
  end
152
160
 
153
- if defined?(JRUBY_VERSION)
161
+ if GC.stat.key?(:total_allocated_objects)
154
162
  def now_allocations
155
- 0
163
+ GC.stat(:total_allocated_objects)
156
164
  end
157
- else
165
+ else # Likely on JRuby, TruffleRuby
158
166
  def now_allocations
159
- GC.stat :total_allocated_objects
167
+ 0
160
168
  end
161
169
  end
162
170
  end
@@ -2,10 +2,9 @@
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
9
  # <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
11
10
  # Ruby.
@@ -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
@@ -30,7 +30,7 @@ module ActiveSupport
30
30
  # If set to true, precision will mean the number of significant digits instead
31
31
  # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
32
32
  significant: false,
33
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
33
+ # If set, the zeros after the decimal separator will always be stripped (e.g.: 1.200 will be 1.2)
34
34
  strip_insignificant_zeros: false
35
35
  },
36
36
 
@@ -174,9 +174,7 @@ module ActiveSupport
174
174
  end
175
175
 
176
176
  def valid_float?
177
- Float(number)
178
- rescue ArgumentError, TypeError
179
- false
177
+ Float(number, exception: false)
180
178
  end
181
179
  end
182
180
  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_f = valid_float?
14
+ if number_f
15
+ if number_f.negative?
16
+ number_f = number_f.abs
17
+ format = options[:negative_format] if (number_f * 10**options[:precision]) >= 0.5
18
+ end
19
+ number_s = NumberToRoundedConverter.convert(number_f, 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,7 +4,7 @@ require "active_support/number_helper/number_converter"
4
4
 
5
5
  module ActiveSupport
6
6
  module NumberHelper
7
- class NumberToHumanSizeConverter < NumberConverter #:nodoc:
7
+ class NumberToHumanSizeConverter < NumberConverter # :nodoc:
8
8
  STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb, :pb, :eb]
9
9
 
10
10
  self.namespace = :human
@@ -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
@@ -4,7 +4,7 @@ require "active_support/number_helper/number_converter"
4
4
 
5
5
  module ActiveSupport
6
6
  module NumberHelper
7
- class NumberToPhoneConverter < NumberConverter #:nodoc:
7
+ class NumberToPhoneConverter < NumberConverter # :nodoc:
8
8
  def convert
9
9
  str = country_code(opts[:country_code]).dup
10
10
  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
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/dependencies/autoload"
4
-
5
3
  module ActiveSupport
6
4
  module NumberHelper
7
5
  extend ActiveSupport::Autoload
@@ -73,6 +71,8 @@ module ActiveSupport
73
71
  # (defaults to current locale).
74
72
  # * <tt>:precision</tt> - Sets the level of precision (defaults
75
73
  # to 2).
74
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
75
+ # (defaults to :default. See BigDecimal::mode)
76
76
  # * <tt>:unit</tt> - Sets the denomination of the currency
77
77
  # (defaults to "$").
78
78
  # * <tt>:separator</tt> - Sets the separator between the units
@@ -99,8 +99,6 @@ module ActiveSupport
99
99
  # number_to_currency(1234567890.506, locale: :fr) # => "1 234 567 890,51 €"
100
100
  # number_to_currency('123a456') # => "$123a456"
101
101
  #
102
- # number_to_currency("123a456", raise: true) # => InvalidNumberError
103
- #
104
102
  # number_to_currency(-0.456789, precision: 0)
105
103
  # # => "$0"
106
104
  # number_to_currency(-1234567890.50, negative_format: '(%u%n)')
@@ -111,6 +109,8 @@ module ActiveSupport
111
109
  # # => "1234567890,50 &pound;"
112
110
  # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
113
111
  # # => "$1,234,567,890.5"
112
+ # number_to_currency(1234567890.50, precision: 0, round_mode: :up)
113
+ # # => "$1,234,567,891"
114
114
  def number_to_currency(number, options = {})
115
115
  NumberToCurrencyConverter.convert(number, options)
116
116
  end
@@ -124,6 +124,8 @@ module ActiveSupport
124
124
  # (defaults to current locale).
125
125
  # * <tt>:precision</tt> - Sets the precision of the number
126
126
  # (defaults to 3). Keeps the number's precision if +nil+.
127
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
128
+ # (defaults to :default. See BigDecimal::mode)
127
129
  # * <tt>:significant</tt> - If +true+, precision will be the number
128
130
  # of significant_digits. If +false+, the number of fractional
129
131
  # digits (defaults to +false+).
@@ -139,15 +141,16 @@ module ActiveSupport
139
141
  #
140
142
  # ==== Examples
141
143
  #
142
- # number_to_percentage(100) # => "100.000%"
143
- # number_to_percentage('98') # => "98.000%"
144
- # number_to_percentage(100, precision: 0) # => "100%"
145
- # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
146
- # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
147
- # number_to_percentage(1000, locale: :fr) # => "1000,000%"
148
- # number_to_percentage(1000, precision: nil) # => "1000%"
149
- # number_to_percentage('98a') # => "98a%"
150
- # number_to_percentage(100, format: '%n %') # => "100.000 %"
144
+ # number_to_percentage(100) # => "100.000%"
145
+ # number_to_percentage('98') # => "98.000%"
146
+ # number_to_percentage(100, precision: 0) # => "100%"
147
+ # number_to_percentage(1000, delimiter: '.', separator: ',') # => "1.000,000%"
148
+ # number_to_percentage(302.24398923423, precision: 5) # => "302.24399%"
149
+ # number_to_percentage(1000, locale: :fr) # => "1000,000%"
150
+ # number_to_percentage(1000, precision: nil) # => "1000%"
151
+ # number_to_percentage('98a') # => "98a%"
152
+ # number_to_percentage(100, format: '%n %') # => "100.000 %"
153
+ # number_to_percentage(302.24398923423, precision: 5, round_mode: :down) # => "302.24398%"
151
154
  def number_to_percentage(number, options = {})
152
155
  NumberToPercentageConverter.convert(number, options)
153
156
  end
@@ -198,6 +201,8 @@ module ActiveSupport
198
201
  # (defaults to current locale).
199
202
  # * <tt>:precision</tt> - Sets the precision of the number
200
203
  # (defaults to 3). Keeps the number's precision if +nil+.
204
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
205
+ # (defaults to :default. See BigDecimal::mode)
201
206
  # * <tt>:significant</tt> - If +true+, precision will be the number
202
207
  # of significant_digits. If +false+, the number of fractional
203
208
  # digits (defaults to +false+).
@@ -219,6 +224,7 @@ module ActiveSupport
219
224
  # number_to_rounded(111.2345, precision: 1, significant: true) # => "100"
220
225
  # number_to_rounded(13, precision: 5, significant: true) # => "13.000"
221
226
  # number_to_rounded(13, precision: nil) # => "13"
227
+ # number_to_rounded(389.32314, precision: 0, round_mode: :up) # => "390"
222
228
  # number_to_rounded(111.234, locale: :fr) # => "111,234"
223
229
  #
224
230
  # number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
@@ -232,7 +238,7 @@ module ActiveSupport
232
238
  end
233
239
 
234
240
  # Formats the bytes in +number+ into a more understandable
235
- # representation (e.g., giving it 1500 yields 1.5 KB). This
241
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
236
242
  # method is useful for reporting file sizes to users. You can
237
243
  # customize the format in the +options+ hash.
238
244
  #
@@ -245,6 +251,8 @@ module ActiveSupport
245
251
  # (defaults to current locale).
246
252
  # * <tt>:precision</tt> - Sets the precision of the number
247
253
  # (defaults to 3).
254
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
255
+ # (defaults to :default. See BigDecimal::mode)
248
256
  # * <tt>:significant</tt> - If +true+, precision will be the number
249
257
  # of significant_digits. If +false+, the number of fractional
250
258
  # digits (defaults to +true+)
@@ -268,6 +276,7 @@ module ActiveSupport
268
276
  # number_to_human_size(1234567890123456789) # => "1.07 EB"
269
277
  # number_to_human_size(1234567, precision: 2) # => "1.2 MB"
270
278
  # number_to_human_size(483989, precision: 2) # => "470 KB"
279
+ # number_to_human_size(483989, precision: 2, round_mode: :up) # => "480 KB"
271
280
  # number_to_human_size(1234567, precision: 2, separator: ',') # => "1,2 MB"
272
281
  # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
273
282
  # number_to_human_size(524288000, precision: 5) # => "500 MB"
@@ -276,7 +285,7 @@ module ActiveSupport
276
285
  end
277
286
 
278
287
  # Pretty prints (formats and approximates) a number in a way it
279
- # is more readable by humans (eg.: 1200000000 becomes "1.2
288
+ # is more readable by humans (e.g.: 1200000000 becomes "1.2
280
289
  # Billion"). This is useful for numbers that can get very large
281
290
  # (and too hard to read).
282
291
  #
@@ -284,7 +293,7 @@ module ActiveSupport
284
293
  # size.
285
294
  #
286
295
  # You can also define your own unit-quantifier names if you want
287
- # to use other decimal units (eg.: 1500 becomes "1.5
296
+ # to use other decimal units (e.g.: 1500 becomes "1.5
288
297
  # kilometers", 0.150 becomes "150 milliliters", etc). You may
289
298
  # define a wide range of unit quantifiers, even fractional ones
290
299
  # (centi, deci, mili, etc).
@@ -295,6 +304,8 @@ module ActiveSupport
295
304
  # (defaults to current locale).
296
305
  # * <tt>:precision</tt> - Sets the precision of the number
297
306
  # (defaults to 3).
307
+ # * <tt>:round_mode</tt> - Determine how rounding is performed
308
+ # (defaults to :default. See BigDecimal::mode)
298
309
  # * <tt>:significant</tt> - If +true+, precision will be the number
299
310
  # of significant_digits. If +false+, the number of fractional
300
311
  # digits (defaults to +true+)
@@ -332,6 +343,8 @@ module ActiveSupport
332
343
  # number_to_human(1234567890123456789) # => "1230 Quadrillion"
333
344
  # number_to_human(489939, precision: 2) # => "490 Thousand"
334
345
  # number_to_human(489939, precision: 4) # => "489.9 Thousand"
346
+ # number_to_human(489939, precision: 2
347
+ # , round_mode: :down) # => "480 Thousand"
335
348
  # number_to_human(1234567, precision: 4,
336
349
  # significant: false) # => "1.2346 Million"
337
350
  # number_to_human(1234567, precision: 1,
@@ -3,9 +3,9 @@
3
3
  require "active_support/core_ext/hash/deep_merge"
4
4
 
5
5
  module ActiveSupport
6
- class OptionMerger #:nodoc:
6
+ class OptionMerger # :nodoc:
7
7
  instance_methods.each do |method|
8
- undef_method(method) if !/^(__|instance_eval|class|object_id)/.match?(method)
8
+ undef_method(method) unless method.start_with?("__", "instance_eval", "class", "object_id")
9
9
  end
10
10
 
11
11
  def initialize(context, options)
@@ -15,8 +15,8 @@ module ActiveSupport
15
15
  private
16
16
  def method_missing(method, *arguments, &block)
17
17
  options = nil
18
- if arguments.first.is_a?(Proc)
19
- proc = arguments.pop
18
+ if arguments.size == 1 && arguments.first.is_a?(Proc)
19
+ proc = arguments.shift
20
20
  arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
21
21
  elsif arguments.last.respond_to?(:to_hash)
22
22
  options = @options.deep_merge(arguments.pop)
@@ -24,22 +24,15 @@ module ActiveSupport
24
24
  options = @options
25
25
  end
26
26
 
27
- invoke_method(method, arguments, options, &block)
28
- end
29
-
30
- if RUBY_VERSION >= "2.7"
31
- def invoke_method(method, arguments, options, &block)
32
- if options
33
- @context.__send__(method, *arguments, **options, &block)
34
- else
35
- @context.__send__(method, *arguments, &block)
36
- end
37
- end
38
- else
39
- def invoke_method(method, arguments, options, &block)
40
- arguments << options.dup if options
27
+ if options
28
+ @context.__send__(method, *arguments, **options, &block)
29
+ else
41
30
  @context.__send__(method, *arguments, &block)
42
31
  end
43
32
  end
33
+
34
+ def respond_to_missing?(*arguments)
35
+ @context.respond_to?(*arguments)
36
+ end
44
37
  end
45
38
  end
@@ -21,7 +21,7 @@ module ActiveSupport
21
21
  #
22
22
  # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
23
23
  # with other implementations.
24
- class OrderedHash < ::Hash
24
+ class OrderedHash < ::Hash # :nodoc:
25
25
  def to_yaml_type
26
26
  "!tag:yaml.org,2002:omap"
27
27
  end