activesupport 7.2.2.2 → 8.1.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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +368 -152
  3. data/README.rdoc +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +73 -2
  5. data/lib/active_support/benchmark.rb +21 -0
  6. data/lib/active_support/benchmarkable.rb +3 -2
  7. data/lib/active_support/broadcast_logger.rb +61 -74
  8. data/lib/active_support/cache/file_store.rb +14 -4
  9. data/lib/active_support/cache/mem_cache_store.rb +27 -29
  10. data/lib/active_support/cache/memory_store.rb +11 -5
  11. data/lib/active_support/cache/null_store.rb +2 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +43 -34
  13. data/lib/active_support/cache/strategy/local_cache.rb +72 -27
  14. data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
  15. data/lib/active_support/cache.rb +88 -20
  16. data/lib/active_support/callbacks.rb +28 -13
  17. data/lib/active_support/class_attribute.rb +33 -0
  18. data/lib/active_support/code_generator.rb +9 -0
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
  20. data/lib/active_support/concurrency/share_lock.rb +0 -1
  21. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  22. data/lib/active_support/configurable.rb +34 -0
  23. data/lib/active_support/configuration_file.rb +15 -6
  24. data/lib/active_support/continuous_integration.rb +145 -0
  25. data/lib/active_support/core_ext/array/conversions.rb +3 -3
  26. data/lib/active_support/core_ext/array.rb +7 -7
  27. data/lib/active_support/core_ext/benchmark.rb +4 -14
  28. data/lib/active_support/core_ext/big_decimal.rb +1 -1
  29. data/lib/active_support/core_ext/class/attribute.rb +26 -20
  30. data/lib/active_support/core_ext/class.rb +2 -2
  31. data/lib/active_support/core_ext/date/conversions.rb +2 -0
  32. data/lib/active_support/core_ext/date.rb +5 -5
  33. data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -35
  34. data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
  35. data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
  36. data/lib/active_support/core_ext/date_time.rb +5 -5
  37. data/lib/active_support/core_ext/digest.rb +1 -1
  38. data/lib/active_support/core_ext/enumerable.rb +25 -8
  39. data/lib/active_support/core_ext/erb/util.rb +5 -5
  40. data/lib/active_support/core_ext/file.rb +1 -1
  41. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
  42. data/lib/active_support/core_ext/hash/except.rb +0 -12
  43. data/lib/active_support/core_ext/hash.rb +8 -8
  44. data/lib/active_support/core_ext/integer.rb +3 -3
  45. data/lib/active_support/core_ext/kernel.rb +3 -3
  46. data/lib/active_support/core_ext/module/attr_internal.rb +3 -4
  47. data/lib/active_support/core_ext/module/introspection.rb +3 -0
  48. data/lib/active_support/core_ext/module.rb +11 -11
  49. data/lib/active_support/core_ext/numeric.rb +3 -3
  50. data/lib/active_support/core_ext/object/json.rb +24 -11
  51. data/lib/active_support/core_ext/object/to_query.rb +7 -1
  52. data/lib/active_support/core_ext/object/try.rb +2 -2
  53. data/lib/active_support/core_ext/object.rb +13 -13
  54. data/lib/active_support/core_ext/pathname.rb +2 -2
  55. data/lib/active_support/core_ext/range/overlap.rb +3 -3
  56. data/lib/active_support/core_ext/range/sole.rb +17 -0
  57. data/lib/active_support/core_ext/range.rb +4 -4
  58. data/lib/active_support/core_ext/securerandom.rb +24 -8
  59. data/lib/active_support/core_ext/string/filters.rb +3 -3
  60. data/lib/active_support/core_ext/string/multibyte.rb +12 -3
  61. data/lib/active_support/core_ext/string/output_safety.rb +19 -12
  62. data/lib/active_support/core_ext/string.rb +13 -13
  63. data/lib/active_support/core_ext/symbol.rb +1 -1
  64. data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
  65. data/lib/active_support/core_ext/time/calculations.rb +7 -2
  66. data/lib/active_support/core_ext/time/compatibility.rb +2 -19
  67. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  68. data/lib/active_support/core_ext/time.rb +5 -5
  69. data/lib/active_support/core_ext.rb +1 -1
  70. data/lib/active_support/current_attributes/test_helper.rb +2 -2
  71. data/lib/active_support/current_attributes.rb +27 -17
  72. data/lib/active_support/delegation.rb +25 -44
  73. data/lib/active_support/dependencies/interlock.rb +11 -5
  74. data/lib/active_support/dependencies.rb +6 -2
  75. data/lib/active_support/deprecation/reporting.rb +4 -21
  76. data/lib/active_support/deprecation.rb +1 -1
  77. data/lib/active_support/duration.rb +14 -10
  78. data/lib/active_support/editor.rb +70 -0
  79. data/lib/active_support/encrypted_configuration.rb +20 -2
  80. data/lib/active_support/error_reporter.rb +81 -4
  81. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  82. data/lib/active_support/event_reporter.rb +592 -0
  83. data/lib/active_support/evented_file_update_checker.rb +5 -2
  84. data/lib/active_support/execution_context.rb +64 -7
  85. data/lib/active_support/execution_wrapper.rb +1 -1
  86. data/lib/active_support/file_update_checker.rb +8 -6
  87. data/lib/active_support/gem_version.rb +3 -3
  88. data/lib/active_support/gzip.rb +1 -0
  89. data/lib/active_support/hash_with_indifferent_access.rb +61 -38
  90. data/lib/active_support/i18n_railtie.rb +19 -11
  91. data/lib/active_support/inflector/inflections.rb +32 -15
  92. data/lib/active_support/inflector/methods.rb +2 -2
  93. data/lib/active_support/inflector/transliterate.rb +6 -8
  94. data/lib/active_support/isolated_execution_state.rb +17 -17
  95. data/lib/active_support/json/decoding.rb +6 -4
  96. data/lib/active_support/json/encoding.rb +157 -21
  97. data/lib/active_support/lazy_load_hooks.rb +1 -1
  98. data/lib/active_support/log_subscriber.rb +2 -6
  99. data/lib/active_support/logger_thread_safe_level.rb +6 -3
  100. data/lib/active_support/message_encryptors.rb +54 -2
  101. data/lib/active_support/message_pack/extensions.rb +6 -1
  102. data/lib/active_support/message_verifier.rb +9 -0
  103. data/lib/active_support/message_verifiers.rb +57 -3
  104. data/lib/active_support/messages/rotation_coordinator.rb +9 -0
  105. data/lib/active_support/messages/rotator.rb +10 -0
  106. data/lib/active_support/multibyte/chars.rb +12 -2
  107. data/lib/active_support/multibyte.rb +4 -0
  108. data/lib/active_support/notifications/fanout.rb +64 -43
  109. data/lib/active_support/notifications/instrumenter.rb +1 -1
  110. data/lib/active_support/number_helper.rb +22 -0
  111. data/lib/active_support/railtie.rb +32 -9
  112. data/lib/active_support/structured_event_subscriber.rb +99 -0
  113. data/lib/active_support/subscriber.rb +0 -5
  114. data/lib/active_support/syntax_error_proxy.rb +7 -0
  115. data/lib/active_support/tagged_logging.rb +5 -0
  116. data/lib/active_support/test_case.rb +67 -6
  117. data/lib/active_support/testing/assertions.rb +115 -27
  118. data/lib/active_support/testing/autorun.rb +5 -0
  119. data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
  120. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  121. data/lib/active_support/testing/isolation.rb +0 -2
  122. data/lib/active_support/testing/notification_assertions.rb +92 -0
  123. data/lib/active_support/testing/parallelization/server.rb +15 -2
  124. data/lib/active_support/testing/parallelization/worker.rb +8 -4
  125. data/lib/active_support/testing/parallelization.rb +25 -1
  126. data/lib/active_support/testing/tests_without_assertions.rb +1 -1
  127. data/lib/active_support/testing/time_helpers.rb +9 -4
  128. data/lib/active_support/time_with_zone.rb +36 -23
  129. data/lib/active_support/values/time_zone.rb +19 -10
  130. data/lib/active_support/xml_mini.rb +3 -2
  131. data/lib/active_support.rb +21 -9
  132. metadata +34 -12
  133. data/lib/active_support/core_ext/range/each.rb +0 -24
  134. data/lib/active_support/proxy_object.rb +0 -20
  135. data/lib/active_support/testing/strict_warnings.rb +0 -43
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "concurrent/map"
4
- require "set"
5
4
  require "active_support/core_ext/object/try"
6
5
 
7
6
  module ActiveSupport
@@ -18,24 +17,30 @@ module ActiveSupport
18
17
 
19
18
  module FanoutIteration # :nodoc:
20
19
  private
21
- def iterate_guarding_exceptions(collection)
22
- exceptions = nil
23
-
24
- collection.each do |s|
25
- yield s
26
- rescue Exception => e
27
- exceptions ||= []
28
- exceptions << e
29
- end
20
+ def iterate_guarding_exceptions(collection, &block)
21
+ case collection.size
22
+ when 0
23
+ when 1
24
+ collection.each(&block)
25
+ else
26
+ exceptions = nil
30
27
 
31
- if exceptions
32
- exceptions = exceptions.flat_map do |exception|
33
- exception.is_a?(InstrumentationSubscriberError) ? exception.exceptions : [exception]
28
+ collection.each do |s|
29
+ yield s
30
+ rescue Exception => e
31
+ exceptions ||= []
32
+ exceptions << e
34
33
  end
35
- if exceptions.size == 1
36
- raise exceptions.first
37
- else
38
- raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
34
+
35
+ if exceptions
36
+ exceptions = exceptions.flat_map do |exception|
37
+ exception.is_a?(InstrumentationSubscriberError) ? exception.exceptions : [exception]
38
+ end
39
+ if exceptions.size == 1
40
+ raise exceptions.first
41
+ else
42
+ raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
43
+ end
39
44
  end
40
45
  end
41
46
 
@@ -54,7 +59,6 @@ module ActiveSupport
54
59
  @other_subscribers = []
55
60
  @all_listeners_for = Concurrent::Map.new
56
61
  @groups_for = Concurrent::Map.new
57
- @silenceable_groups_for = Concurrent::Map.new
58
62
  end
59
63
 
60
64
  def inspect # :nodoc:
@@ -103,11 +107,9 @@ module ActiveSupport
103
107
  if key
104
108
  @all_listeners_for.delete(key)
105
109
  @groups_for.delete(key)
106
- @silenceable_groups_for.delete(key)
107
110
  else
108
111
  @all_listeners_for.clear
109
112
  @groups_for.clear
110
- @silenceable_groups_for.clear
111
113
  end
112
114
  end
113
115
 
@@ -185,25 +187,25 @@ module ActiveSupport
185
187
  end
186
188
  end
187
189
 
188
- def groups_for(name) # :nodoc:
189
- groups = @groups_for.compute_if_absent(name) do
190
- all_listeners_for(name).reject(&:silenceable).group_by(&:group_class).transform_values do |s|
191
- s.map(&:delegate)
192
- end
193
- end
190
+ def group_listeners(listeners) # :nodoc:
191
+ listeners.group_by(&:group_class).transform_values do |s|
192
+ s.map(&:delegate).freeze
193
+ end.freeze
194
+ end
194
195
 
195
- silenceable_groups = @silenceable_groups_for.compute_if_absent(name) do
196
- all_listeners_for(name).select(&:silenceable).group_by(&:group_class).transform_values do |s|
197
- s.map(&:delegate)
198
- end
196
+ def groups_for(name) # :nodoc:
197
+ silenceable_groups, groups = @groups_for.compute_if_absent(name) do
198
+ listeners = all_listeners_for(name)
199
+ listeners.partition(&:silenceable).map { |l| group_listeners(l) }
199
200
  end
200
201
 
201
202
  unless silenceable_groups.empty?
202
- groups = groups.dup
203
203
  silenceable_groups.each do |group_class, subscriptions|
204
204
  active_subscriptions = subscriptions.reject { |s| s.silenced?(name) }
205
205
  unless active_subscriptions.empty?
206
- groups[group_class] = (groups[group_class] || []) + active_subscriptions
206
+ groups = groups.dup if groups.frozen?
207
+ base_groups = groups[group_class]
208
+ groups[group_class] = base_groups ? base_groups + active_subscriptions : active_subscriptions
207
209
  end
208
210
  end
209
211
  end
@@ -228,13 +230,11 @@ module ActiveSupport
228
230
  class Handle
229
231
  include FanoutIteration
230
232
 
231
- def initialize(notifier, name, id, payload) # :nodoc:
233
+ def initialize(notifier, name, id, groups, payload) # :nodoc:
232
234
  @name = name
233
235
  @id = id
234
236
  @payload = payload
235
- @groups = notifier.groups_for(name).map do |group_klass, grouped_listeners|
236
- group_klass.new(grouped_listeners, name, id, payload)
237
- end
237
+ @groups = groups
238
238
  @state = :initialized
239
239
  end
240
240
 
@@ -268,10 +268,31 @@ module ActiveSupport
268
268
  end
269
269
  end
270
270
 
271
+ module NullHandle # :nodoc:
272
+ extend self
273
+
274
+ def start
275
+ end
276
+
277
+ def finish
278
+ end
279
+
280
+ def finish_with_values(_name, _id, _payload)
281
+ end
282
+ end
283
+
271
284
  include FanoutIteration
272
285
 
273
286
  def build_handle(name, id, payload)
274
- Handle.new(self, name, id, payload)
287
+ groups = groups_for(name).map do |group_klass, grouped_listeners|
288
+ group_klass.new(grouped_listeners, name, id, payload)
289
+ end
290
+
291
+ if groups.empty?
292
+ NullHandle
293
+ else
294
+ Handle.new(self, name, id, groups, payload)
295
+ end
275
296
  end
276
297
 
277
298
  def start(name, id, payload)
@@ -287,8 +308,8 @@ module ActiveSupport
287
308
  handle.finish_with_values(name, id, payload)
288
309
  end
289
310
 
290
- def publish(name, *args)
291
- iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, *args) }
311
+ def publish(name, ...)
312
+ iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, ...) }
292
313
  end
293
314
 
294
315
  def publish_event(event)
@@ -388,9 +409,9 @@ module ActiveSupport
388
409
  EventedGroup
389
410
  end
390
411
 
391
- def publish(name, *args)
412
+ def publish(...)
392
413
  if @can_publish
393
- @delegate.publish name, *args
414
+ @delegate.publish(...)
394
415
  end
395
416
  end
396
417
 
@@ -420,8 +441,8 @@ module ActiveSupport
420
441
  TimedGroup
421
442
  end
422
443
 
423
- def publish(name, *args)
424
- @delegate.call name, *args
444
+ def publish(...)
445
+ @delegate.call(...)
425
446
  end
426
447
  end
427
448
 
@@ -164,7 +164,7 @@ module ActiveSupport
164
164
  @cpu_time_finish - @cpu_time_start
165
165
  end
166
166
 
167
- # Returns the idle time time (in milliseconds) passed between the call to
167
+ # Returns the idle time (in milliseconds) passed between the call to
168
168
  # #start! and the call to #finish!.
169
169
  def idle_time
170
170
  diff = duration - cpu_time
@@ -1,6 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
+ # = Number Helper
5
+ #
6
+ # Provides methods for formatting numbers into currencies, percentages,
7
+ # phone numbers, and more.
8
+ #
9
+ # Example usage in a class:
10
+ # class Topic
11
+ # include ActiveSupport::NumberHelper
12
+ #
13
+ # def price
14
+ # number_to_currency(@price)
15
+ # end
16
+ # end
17
+ #
18
+ # Example usage in a module:
19
+ # require "active_support/number_helper"
20
+ #
21
+ # module NumberFormatting
22
+ # def format_price(price)
23
+ # ActiveSupport::NumberHelper.number_to_currency(price)
24
+ # end
25
+ # end
4
26
  module NumberHelper
5
27
  extend ActiveSupport::Autoload
6
28
 
@@ -15,7 +15,7 @@ module ActiveSupport
15
15
 
16
16
  initializer "active_support.isolation_level" do |app|
17
17
  config.after_initialize do
18
- if level = app.config.active_support.delete(:isolation_level)
18
+ if level = app.config.active_support.isolation_level
19
19
  ActiveSupport::IsolatedExecutionState.isolation_level = level
20
20
  end
21
21
  end
@@ -38,19 +38,35 @@ module ActiveSupport
38
38
  end
39
39
  end
40
40
 
41
- initializer "active_support.reset_execution_context" do |app|
42
- app.reloader.before_class_unload { ActiveSupport::ExecutionContext.clear }
43
- app.executor.to_run { ActiveSupport::ExecutionContext.clear }
44
- app.executor.to_complete { ActiveSupport::ExecutionContext.clear }
41
+ initializer "active_support.set_event_reporter_context_store" do |app|
42
+ config.after_initialize do
43
+ if klass = app.config.active_support.event_reporter_context_store
44
+ ActiveSupport::EventReporter.context_store = klass
45
+ end
46
+ end
45
47
  end
46
48
 
47
- initializer "active_support.reset_all_current_attributes_instances" do |app|
48
- app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all }
49
- app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all }
50
- app.executor.to_complete { ActiveSupport::CurrentAttributes.reset_all }
49
+ initializer "active_support.reset_execution_context" do |app|
50
+ app.reloader.before_class_unload do
51
+ ActiveSupport::CurrentAttributes.clear_all
52
+ ActiveSupport::ExecutionContext.clear
53
+ ActiveSupport.event_reporter.clear_context
54
+ end
55
+
56
+ app.executor.to_run do
57
+ ActiveSupport::ExecutionContext.push
58
+ end
59
+
60
+ app.executor.to_complete do
61
+ ActiveSupport::CurrentAttributes.clear_all
62
+ ActiveSupport::ExecutionContext.pop
63
+ ActiveSupport.event_reporter.clear_context
64
+ end
51
65
 
52
66
  ActiveSupport.on_load(:active_support_test_case) do
53
67
  if app.config.active_support.executor_around_test_case
68
+ ActiveSupport::ExecutionContext.nestable = true
69
+
54
70
  require "active_support/executor/test_helper"
55
71
  include ActiveSupport::Executor::TestHelper
56
72
  else
@@ -63,6 +79,13 @@ module ActiveSupport
63
79
  end
64
80
  end
65
81
 
82
+ initializer "active_support.set_filter_parameters" do |app|
83
+ config.after_initialize do
84
+ ActiveSupport.filter_parameters += Rails.application.config.filter_parameters
85
+ ActiveSupport.event_reporter.reload_payload_filter
86
+ end
87
+ end
88
+
66
89
  initializer "active_support.deprecation_behavior" do |app|
67
90
  if app.config.active_support.report_deprecations == false
68
91
  app.deprecators.silenced = true
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/subscriber"
4
+
5
+ module ActiveSupport
6
+ # = Active Support Structured Event \Subscriber
7
+ #
8
+ # +ActiveSupport::StructuredEventSubscriber+ consumes ActiveSupport::Notifications
9
+ # in order to emit structured events via +Rails.event+.
10
+ #
11
+ # An example would be the Action Controller structured event subscriber, responsible for
12
+ # emitting request processing events:
13
+ #
14
+ # module ActionController
15
+ # class StructuredEventSubscriber < ActiveSupport::StructuredEventSubscriber
16
+ # attach_to :action_controller
17
+ #
18
+ # def start_processing(event)
19
+ # emit_event("controller.request_started",
20
+ # controller: event.payload[:controller],
21
+ # action: event.payload[:action],
22
+ # format: event.payload[:format]
23
+ # )
24
+ # end
25
+ # end
26
+ # end
27
+ #
28
+ # After configured, whenever a <tt>"start_processing.action_controller"</tt> notification is published,
29
+ # it will properly dispatch the event (+ActiveSupport::Notifications::Event+) to the +start_processing+ method.
30
+ # The subscriber can then emit a structured event via the +emit_event+ method.
31
+ class StructuredEventSubscriber < Subscriber
32
+ class_attribute :debug_methods, instance_accessor: false, default: [] # :nodoc:
33
+
34
+ DEBUG_CHECK = proc { !ActiveSupport.event_reporter.debug_mode? }
35
+
36
+ class << self
37
+ def attach_to(...) # :nodoc:
38
+ result = super
39
+ set_silenced_events
40
+ result
41
+ end
42
+
43
+ private
44
+ def set_silenced_events
45
+ if subscriber
46
+ subscriber.silenced_events = debug_methods.to_h { |method| ["#{method}.#{namespace}", DEBUG_CHECK] }
47
+ end
48
+ end
49
+
50
+ def debug_only(method)
51
+ self.debug_methods << method
52
+ set_silenced_events
53
+ end
54
+ end
55
+
56
+ def initialize
57
+ super
58
+ @silenced_events = {}
59
+ end
60
+
61
+ def silenced?(event)
62
+ ActiveSupport.event_reporter.subscribers.none? || @silenced_events[event]&.call
63
+ end
64
+
65
+ attr_writer :silenced_events # :nodoc:
66
+
67
+ # Emit a structured event via Rails.event.notify.
68
+ #
69
+ # ==== Arguments
70
+ #
71
+ # * +name+ - The event name as a string or symbol
72
+ # * +payload+ - The event payload as a hash or object
73
+ # * +caller_depth+ - Stack depth for source location (default: 1)
74
+ # * +kwargs+ - Additional payload data merged with the payload hash
75
+ def emit_event(name, payload = nil, caller_depth: 1, **kwargs)
76
+ ActiveSupport.event_reporter.notify(name, payload, caller_depth: caller_depth + 1, **kwargs)
77
+ rescue => e
78
+ handle_event_error(name, e)
79
+ end
80
+
81
+ # Like +emit_event+, but only emits when the event reporter is in debug mode
82
+ def emit_debug_event(name, payload = nil, caller_depth: 1, **kwargs)
83
+ ActiveSupport.event_reporter.debug(name, payload, caller_depth: caller_depth + 1, **kwargs)
84
+ rescue => e
85
+ handle_event_error(name, e)
86
+ end
87
+
88
+ def call(event)
89
+ super
90
+ rescue => e
91
+ handle_event_error(event.name, e)
92
+ end
93
+
94
+ private
95
+ def handle_event_error(name, error)
96
+ ActiveSupport.error_reporter.report(error, source: name)
97
+ end
98
+ end
99
+ end
@@ -137,10 +137,5 @@ module ActiveSupport
137
137
  method = event.name[0, event.name.index(".")]
138
138
  send(method, event)
139
139
  end
140
-
141
- def publish_event(event) # :nodoc:
142
- method = event.name[0, event.name.index(".")]
143
- send(method, event)
144
- end
145
140
  end
146
141
  end
@@ -18,6 +18,13 @@ module ActiveSupport
18
18
 
19
19
  def label
20
20
  end
21
+
22
+ def base_label
23
+ end
24
+
25
+ def absolute_path
26
+ path
27
+ end
21
28
  end
22
29
 
23
30
  class BacktraceLocationProxy < DelegateClass(Thread::Backtrace::Location) # :nodoc:
@@ -113,6 +113,11 @@ module ActiveSupport
113
113
  end
114
114
  end
115
115
 
116
+ # Returns an `ActiveSupport::Logger` that has already been wrapped with tagged logging concern.
117
+ def self.logger(*args, **kwargs)
118
+ new ActiveSupport::Logger.new(*args, **kwargs)
119
+ end
120
+
116
121
  def self.new(logger)
117
122
  logger = logger.clone
118
123
 
@@ -6,6 +6,7 @@ require "active_support/testing/setup_and_teardown"
6
6
  require "active_support/testing/tests_without_assertions"
7
7
  require "active_support/testing/assertions"
8
8
  require "active_support/testing/error_reporter_assertions"
9
+ require "active_support/testing/event_reporter_assertions"
9
10
  require "active_support/testing/deprecation"
10
11
  require "active_support/testing/declarative"
11
12
  require "active_support/testing/isolation"
@@ -15,13 +16,29 @@ require "active_support/testing/constant_stubbing"
15
16
  require "active_support/testing/file_fixtures"
16
17
  require "active_support/testing/parallelization"
17
18
  require "active_support/testing/parallelize_executor"
19
+ require "active_support/testing/notification_assertions"
18
20
  require "concurrent/utility/processor_counter"
19
21
 
20
22
  module ActiveSupport
21
23
  class TestCase < ::Minitest::Test
22
24
  Assertion = Minitest::Assertion
23
25
 
26
+ # Class variable to store the parallel worker ID
27
+ @@parallel_worker_id = nil
28
+
24
29
  class << self
30
+ # Returns the current parallel worker ID if tests are running in parallel,
31
+ # nil otherwise.
32
+ #
33
+ # ActiveSupport::TestCase.parallel_worker_id # => 2
34
+ def parallel_worker_id
35
+ @@parallel_worker_id
36
+ end
37
+
38
+ def parallel_worker_id=(value) # :nodoc:
39
+ @@parallel_worker_id = value
40
+ end
41
+
25
42
  # Sets the order in which test cases are run.
26
43
  #
27
44
  # ActiveSupport::TestCase.test_order = :random # => :random
@@ -45,14 +62,20 @@ module ActiveSupport
45
62
  ActiveSupport.test_order ||= :random
46
63
  end
47
64
 
65
+ if Minitest.respond_to? :run_order # MT6 API change
66
+ def run_order # :nodoc:
67
+ test_order
68
+ end
69
+ end
70
+
48
71
  # Parallelizes the test suite.
49
72
  #
50
73
  # Takes a +workers+ argument that controls how many times the process
51
74
  # is forked. For each process a new database will be created suffixed
52
75
  # with the worker number.
53
76
  #
54
- # test-database-0
55
- # test-database-1
77
+ # test-database_0
78
+ # test-database_1
56
79
  #
57
80
  # If <tt>ENV["PARALLEL_WORKERS"]</tt> is set the workers argument will be ignored
58
81
  # and the environment variable will be used instead. This is useful for CI
@@ -78,14 +101,45 @@ module ActiveSupport
78
101
  # Because parallelization presents an overhead, it is only enabled when the
79
102
  # number of tests to run is above the +threshold+ param. The default value is
80
103
  # 50, and it's configurable via +config.active_support.test_parallelization_threshold+.
81
- def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold)
82
- workers = Concurrent.processor_count if workers == :number_of_processors
83
- workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
104
+ #
105
+ # If you want to skip Rails default creation of one database per process in favor of
106
+ # writing your own implementation, you can set +parallelize_databases+, or configure it
107
+ # via +config.active_support.parallelize_test_databases+.
108
+ #
109
+ # parallelize(workers: :number_of_processors, parallelize_databases: false)
110
+ #
111
+ # Note that your test suite may deadlock if you attempt to use only one database
112
+ # with multiple processes.
113
+ def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold, parallelize_databases: ActiveSupport.parallelize_test_databases)
114
+ case
115
+ when ENV["PARALLEL_WORKERS"]
116
+ workers = ENV["PARALLEL_WORKERS"].to_i
117
+ when workers == :number_of_processors
118
+ workers = (Concurrent.available_processor_count || Concurrent.processor_count).floor
119
+ end
120
+
121
+ if with == :processes
122
+ ActiveSupport.parallelize_test_databases = parallelize_databases
123
+ end
84
124
 
85
125
  Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
86
126
  end
87
127
 
88
- # Set up hook for parallel testing. This can be used if you have multiple
128
+ # Before fork hook for parallel testing. This can be used to run anything
129
+ # before the processes are forked.
130
+ #
131
+ # In your +test_helper.rb+ add the following:
132
+ #
133
+ # class ActiveSupport::TestCase
134
+ # parallelize_before_fork do
135
+ # # run this before fork
136
+ # end
137
+ # end
138
+ def parallelize_before_fork(&block)
139
+ ActiveSupport::Testing::Parallelization.before_fork_hook(&block)
140
+ end
141
+
142
+ # Setup hook for parallel testing. This can be used if you have multiple
89
143
  # databases or any behavior that needs to be run after the process is forked
90
144
  # but before the tests run.
91
145
  #
@@ -141,11 +195,18 @@ module ActiveSupport
141
195
 
142
196
  alias_method :method_name, :name
143
197
 
198
+ # Returns the current parallel worker ID if tests are running in parallel
199
+ def parallel_worker_id
200
+ self.class.parallel_worker_id
201
+ end
202
+
144
203
  include ActiveSupport::Testing::TaggedLogging
145
204
  prepend ActiveSupport::Testing::SetupAndTeardown
146
205
  prepend ActiveSupport::Testing::TestsWithoutAssertions
147
206
  include ActiveSupport::Testing::Assertions
148
207
  include ActiveSupport::Testing::ErrorReporterAssertions
208
+ include ActiveSupport::Testing::EventReporterAssertions
209
+ include ActiveSupport::Testing::NotificationAssertions
149
210
  include ActiveSupport::Testing::Deprecation
150
211
  include ActiveSupport::Testing::ConstantStubbing
151
212
  include ActiveSupport::Testing::TimeHelpers