activesupport 8.0.4 → 8.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +296 -172
- data/lib/active_support/backtrace_cleaner.rb +71 -0
- data/lib/active_support/cache/mem_cache_store.rb +13 -13
- data/lib/active_support/cache/redis_cache_store.rb +36 -30
- data/lib/active_support/cache/strategy/local_cache.rb +16 -7
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
- data/lib/active_support/cache.rb +69 -6
- data/lib/active_support/callbacks.rb +20 -8
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
- data/lib/active_support/concurrency/thread_monitor.rb +55 -0
- data/lib/active_support/configurable.rb +34 -0
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/array.rb +7 -7
- data/lib/active_support/core_ext/benchmark.rb +4 -12
- data/lib/active_support/core_ext/big_decimal.rb +1 -1
- data/lib/active_support/core_ext/class.rb +2 -2
- data/lib/active_support/core_ext/date.rb +5 -5
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -35
- data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
- data/lib/active_support/core_ext/date_time.rb +5 -5
- data/lib/active_support/core_ext/digest.rb +1 -1
- data/lib/active_support/core_ext/erb/util.rb +3 -3
- data/lib/active_support/core_ext/file.rb +1 -1
- data/lib/active_support/core_ext/hash.rb +8 -8
- data/lib/active_support/core_ext/integer.rb +3 -3
- data/lib/active_support/core_ext/kernel.rb +3 -3
- data/lib/active_support/core_ext/module.rb +11 -11
- data/lib/active_support/core_ext/numeric.rb +3 -3
- data/lib/active_support/core_ext/object/json.rb +8 -1
- data/lib/active_support/core_ext/object/to_query.rb +5 -0
- data/lib/active_support/core_ext/object.rb +13 -13
- data/lib/active_support/core_ext/pathname.rb +2 -2
- data/lib/active_support/core_ext/range.rb +4 -5
- data/lib/active_support/core_ext/string/multibyte.rb +10 -1
- data/lib/active_support/core_ext/string/output_safety.rb +19 -12
- data/lib/active_support/core_ext/string.rb +13 -13
- data/lib/active_support/core_ext/symbol.rb +1 -1
- data/lib/active_support/core_ext/time/calculations.rb +0 -7
- data/lib/active_support/core_ext/time/compatibility.rb +2 -27
- data/lib/active_support/core_ext/time.rb +5 -5
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/current_attributes/test_helper.rb +2 -2
- data/lib/active_support/current_attributes.rb +13 -10
- data/lib/active_support/dependencies/interlock.rb +11 -5
- data/lib/active_support/dependencies.rb +6 -1
- data/lib/active_support/deprecation/reporting.rb +4 -2
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/error_reporter.rb +50 -6
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +592 -0
- data/lib/active_support/evented_file_update_checker.rb +5 -1
- data/lib/active_support/execution_context.rb +64 -7
- data/lib/active_support/file_update_checker.rb +7 -5
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/gzip.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +27 -7
- data/lib/active_support/i18n_railtie.rb +1 -2
- data/lib/active_support/inflector/inflections.rb +31 -15
- data/lib/active_support/inflector/transliterate.rb +6 -8
- data/lib/active_support/isolated_execution_state.rb +12 -15
- data/lib/active_support/json/decoding.rb +2 -2
- data/lib/active_support/json/encoding.rb +135 -17
- data/lib/active_support/log_subscriber.rb +2 -6
- data/lib/active_support/message_encryptors.rb +52 -0
- data/lib/active_support/message_pack/extensions.rb +5 -0
- data/lib/active_support/message_verifiers.rb +52 -0
- data/lib/active_support/messages/rotation_coordinator.rb +9 -0
- data/lib/active_support/messages/rotator.rb +5 -0
- data/lib/active_support/multibyte/chars.rb +8 -1
- data/lib/active_support/multibyte.rb +4 -0
- data/lib/active_support/notifications/fanout.rb +64 -42
- data/lib/active_support/railtie.rb +32 -15
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +0 -5
- data/lib/active_support/syntax_error_proxy.rb +3 -0
- data/lib/active_support/test_case.rb +61 -6
- data/lib/active_support/testing/assertions.rb +34 -6
- data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/worker.rb +2 -0
- data/lib/active_support/testing/parallelization.rb +13 -0
- data/lib/active_support/testing/tests_without_assertions.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +7 -3
- data/lib/active_support/time_with_zone.rb +22 -22
- data/lib/active_support/values/time_zone.rb +8 -1
- data/lib/active_support/xml_mini.rb +1 -2
- data/lib/active_support.rb +20 -15
- metadata +24 -17
- data/lib/active_support/core_ext/range/each.rb +0 -24
|
@@ -7,6 +7,7 @@ require "pathname"
|
|
|
7
7
|
require "uri/generic"
|
|
8
8
|
require "msgpack/bigint"
|
|
9
9
|
require "active_support/hash_with_indifferent_access"
|
|
10
|
+
require "active_support/core_ext/string/output_safety"
|
|
10
11
|
require "active_support/time"
|
|
11
12
|
|
|
12
13
|
module ActiveSupport
|
|
@@ -102,6 +103,10 @@ module ActiveSupport
|
|
|
102
103
|
packer: method(:write_hash_with_indifferent_access),
|
|
103
104
|
unpacker: method(:read_hash_with_indifferent_access),
|
|
104
105
|
recursive: true
|
|
106
|
+
|
|
107
|
+
registry.register_type 18, ActiveSupport::SafeBuffer,
|
|
108
|
+
packer: :to_s,
|
|
109
|
+
unpacker: :new
|
|
105
110
|
end
|
|
106
111
|
|
|
107
112
|
def install_unregistered_type_error(registry)
|
|
@@ -26,6 +26,9 @@ module ActiveSupport
|
|
|
26
26
|
# as the first rotation and <tt>transitional = true</tt>. Then, after all
|
|
27
27
|
# servers have been updated, perform a second rolling deploy with
|
|
28
28
|
# <tt>transitional = false</tt>.
|
|
29
|
+
#
|
|
30
|
+
#--
|
|
31
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#transitional
|
|
29
32
|
|
|
30
33
|
##
|
|
31
34
|
# :singleton-method: new
|
|
@@ -42,6 +45,9 @@ module ActiveSupport
|
|
|
42
45
|
# end
|
|
43
46
|
#
|
|
44
47
|
# verifiers.rotate(base: "...")
|
|
48
|
+
#
|
|
49
|
+
#--
|
|
50
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#initialize
|
|
45
51
|
|
|
46
52
|
##
|
|
47
53
|
# :method: []
|
|
@@ -50,12 +56,18 @@ module ActiveSupport
|
|
|
50
56
|
# Returns a MessageVerifier configured with a secret derived from the
|
|
51
57
|
# given +salt+, and options from #rotate. MessageVerifier instances will
|
|
52
58
|
# be memoized, so the same +salt+ will return the same instance.
|
|
59
|
+
#
|
|
60
|
+
#--
|
|
61
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#[]
|
|
53
62
|
|
|
54
63
|
##
|
|
55
64
|
# :method: []=
|
|
56
65
|
# :call-seq: []=(salt, verifier)
|
|
57
66
|
#
|
|
58
67
|
# Overrides a MessageVerifier instance associated with a given +salt+.
|
|
68
|
+
#
|
|
69
|
+
#--
|
|
70
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#[]=
|
|
59
71
|
|
|
60
72
|
##
|
|
61
73
|
# :method: rotate
|
|
@@ -104,18 +116,55 @@ module ActiveSupport
|
|
|
104
116
|
#
|
|
105
117
|
# # Uses `serializer: Marshal, url_safe: false`.
|
|
106
118
|
# verifiers[:baz]
|
|
119
|
+
#
|
|
120
|
+
#--
|
|
121
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#rotate
|
|
122
|
+
|
|
123
|
+
##
|
|
124
|
+
# :method: prepend
|
|
125
|
+
# :call-seq:
|
|
126
|
+
# prepend(**options)
|
|
127
|
+
# prepend(&block)
|
|
128
|
+
#
|
|
129
|
+
# Just like #rotate, but prepends the given options or block to the list of
|
|
130
|
+
# option sets.
|
|
131
|
+
#
|
|
132
|
+
# This can be useful when you have an already-configured +MessageVerifiers+
|
|
133
|
+
# instance, but you want to override the way messages are signed.
|
|
134
|
+
#
|
|
135
|
+
# module ThirdParty
|
|
136
|
+
# VERIFIERS = ActiveSupport::MessageVerifiers.new { ... }.
|
|
137
|
+
# rotate(serializer: Marshal, url_safe: true).
|
|
138
|
+
# rotate(serializer: Marshal, url_safe: false)
|
|
139
|
+
# end
|
|
140
|
+
#
|
|
141
|
+
# ThirdParty.VERIFIERS.prepend(serializer: JSON, url_safe: true)
|
|
142
|
+
#
|
|
143
|
+
# # Uses `serializer: JSON, url_safe: true`.
|
|
144
|
+
# # Falls back to `serializer: Marshal, url_safe: true` or
|
|
145
|
+
# # `serializer: Marshal, url_safe: false`.
|
|
146
|
+
# ThirdParty.VERIFIERS[:foo]
|
|
147
|
+
#
|
|
148
|
+
#--
|
|
149
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#prepend
|
|
107
150
|
|
|
108
151
|
##
|
|
109
152
|
# :method: rotate_defaults
|
|
110
153
|
# :call-seq: rotate_defaults
|
|
111
154
|
#
|
|
112
155
|
# Invokes #rotate with the default options.
|
|
156
|
+
#
|
|
157
|
+
#--
|
|
158
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#rotate_defaults
|
|
113
159
|
|
|
114
160
|
##
|
|
115
161
|
# :method: clear_rotations
|
|
116
162
|
# :call-seq: clear_rotations
|
|
117
163
|
#
|
|
118
164
|
# Clears the list of option sets.
|
|
165
|
+
#
|
|
166
|
+
#--
|
|
167
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#clear_rotations
|
|
119
168
|
|
|
120
169
|
##
|
|
121
170
|
# :method: on_rotation
|
|
@@ -127,6 +176,9 @@ module ActiveSupport
|
|
|
127
176
|
# For example, this callback could log each time it is called, and thus
|
|
128
177
|
# indicate whether old option sets are still in use or can be removed from
|
|
129
178
|
# rotation.
|
|
179
|
+
#
|
|
180
|
+
#--
|
|
181
|
+
# Implemented by ActiveSupport::Messages::RotationCoordinator#on_rotation
|
|
130
182
|
|
|
131
183
|
##
|
|
132
184
|
private
|
|
@@ -32,6 +32,15 @@ module ActiveSupport
|
|
|
32
32
|
self
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def prepend(**options, &block)
|
|
36
|
+
raise ArgumentError, "Options cannot be specified when using a block" if block && !options.empty?
|
|
37
|
+
changing_configuration!
|
|
38
|
+
|
|
39
|
+
@rotate_options.unshift(block || options)
|
|
40
|
+
|
|
41
|
+
self
|
|
42
|
+
end
|
|
43
|
+
|
|
35
44
|
def rotate_defaults
|
|
36
45
|
rotate()
|
|
37
46
|
end
|
|
@@ -53,7 +53,14 @@ module ActiveSupport # :nodoc:
|
|
|
53
53
|
delegate :<=>, :=~, :match?, :acts_like_string?, to: :wrapped_string
|
|
54
54
|
|
|
55
55
|
# Creates a new Chars instance by wrapping _string_.
|
|
56
|
-
def initialize(string)
|
|
56
|
+
def initialize(string, deprecation: true)
|
|
57
|
+
if deprecation
|
|
58
|
+
ActiveSupport.deprecator.warn(
|
|
59
|
+
"ActiveSupport::Multibyte::Chars is deprecated and will be removed in Rails 8.2. " \
|
|
60
|
+
"Use normal string methods instead."
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
57
64
|
@wrapped_string = string
|
|
58
65
|
if string.encoding != Encoding::UTF_8
|
|
59
66
|
@wrapped_string = @wrapped_string.dup
|
|
@@ -12,6 +12,10 @@ module ActiveSupport # :nodoc:
|
|
|
12
12
|
#
|
|
13
13
|
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
|
|
14
14
|
def self.proxy_class=(klass)
|
|
15
|
+
ActiveSupport.deprecator.warn(
|
|
16
|
+
"ActiveSupport::Multibyte.proxy_class= is deprecated and will be removed in Rails 8.2. " \
|
|
17
|
+
"Use normal string methods instead."
|
|
18
|
+
)
|
|
15
19
|
@proxy_class = klass
|
|
16
20
|
end
|
|
17
21
|
|
|
@@ -17,24 +17,30 @@ module ActiveSupport
|
|
|
17
17
|
|
|
18
18
|
module FanoutIteration # :nodoc:
|
|
19
19
|
private
|
|
20
|
-
def iterate_guarding_exceptions(collection)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
exceptions
|
|
27
|
-
exceptions << e
|
|
28
|
-
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
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
collection.each do |s|
|
|
29
|
+
yield s
|
|
30
|
+
rescue Exception => e
|
|
31
|
+
exceptions ||= []
|
|
32
|
+
exceptions << e
|
|
33
33
|
end
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
38
44
|
end
|
|
39
45
|
end
|
|
40
46
|
|
|
@@ -53,7 +59,6 @@ module ActiveSupport
|
|
|
53
59
|
@other_subscribers = []
|
|
54
60
|
@all_listeners_for = Concurrent::Map.new
|
|
55
61
|
@groups_for = Concurrent::Map.new
|
|
56
|
-
@silenceable_groups_for = Concurrent::Map.new
|
|
57
62
|
end
|
|
58
63
|
|
|
59
64
|
def inspect # :nodoc:
|
|
@@ -102,11 +107,9 @@ module ActiveSupport
|
|
|
102
107
|
if key
|
|
103
108
|
@all_listeners_for.delete(key)
|
|
104
109
|
@groups_for.delete(key)
|
|
105
|
-
@silenceable_groups_for.delete(key)
|
|
106
110
|
else
|
|
107
111
|
@all_listeners_for.clear
|
|
108
112
|
@groups_for.clear
|
|
109
|
-
@silenceable_groups_for.clear
|
|
110
113
|
end
|
|
111
114
|
end
|
|
112
115
|
|
|
@@ -184,25 +187,25 @@ module ActiveSupport
|
|
|
184
187
|
end
|
|
185
188
|
end
|
|
186
189
|
|
|
187
|
-
def
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
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
|
|
193
195
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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) }
|
|
198
200
|
end
|
|
199
201
|
|
|
200
202
|
unless silenceable_groups.empty?
|
|
201
|
-
groups = groups.dup
|
|
202
203
|
silenceable_groups.each do |group_class, subscriptions|
|
|
203
204
|
active_subscriptions = subscriptions.reject { |s| s.silenced?(name) }
|
|
204
205
|
unless active_subscriptions.empty?
|
|
205
|
-
groups
|
|
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
|
|
206
209
|
end
|
|
207
210
|
end
|
|
208
211
|
end
|
|
@@ -227,13 +230,11 @@ module ActiveSupport
|
|
|
227
230
|
class Handle
|
|
228
231
|
include FanoutIteration
|
|
229
232
|
|
|
230
|
-
def initialize(notifier, name, id, payload) # :nodoc:
|
|
233
|
+
def initialize(notifier, name, id, groups, payload) # :nodoc:
|
|
231
234
|
@name = name
|
|
232
235
|
@id = id
|
|
233
236
|
@payload = payload
|
|
234
|
-
@groups =
|
|
235
|
-
group_klass.new(grouped_listeners, name, id, payload)
|
|
236
|
-
end
|
|
237
|
+
@groups = groups
|
|
237
238
|
@state = :initialized
|
|
238
239
|
end
|
|
239
240
|
|
|
@@ -267,10 +268,31 @@ module ActiveSupport
|
|
|
267
268
|
end
|
|
268
269
|
end
|
|
269
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
|
+
|
|
270
284
|
include FanoutIteration
|
|
271
285
|
|
|
272
286
|
def build_handle(name, id, payload)
|
|
273
|
-
|
|
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
|
|
274
296
|
end
|
|
275
297
|
|
|
276
298
|
def start(name, id, payload)
|
|
@@ -286,8 +308,8 @@ module ActiveSupport
|
|
|
286
308
|
handle.finish_with_values(name, id, payload)
|
|
287
309
|
end
|
|
288
310
|
|
|
289
|
-
def publish(name,
|
|
290
|
-
iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name,
|
|
311
|
+
def publish(name, ...)
|
|
312
|
+
iterate_guarding_exceptions(listeners_for(name)) { |s| s.publish(name, ...) }
|
|
291
313
|
end
|
|
292
314
|
|
|
293
315
|
def publish_event(event)
|
|
@@ -387,9 +409,9 @@ module ActiveSupport
|
|
|
387
409
|
EventedGroup
|
|
388
410
|
end
|
|
389
411
|
|
|
390
|
-
def publish(
|
|
412
|
+
def publish(...)
|
|
391
413
|
if @can_publish
|
|
392
|
-
@delegate.publish
|
|
414
|
+
@delegate.publish(...)
|
|
393
415
|
end
|
|
394
416
|
end
|
|
395
417
|
|
|
@@ -419,8 +441,8 @@ module ActiveSupport
|
|
|
419
441
|
TimedGroup
|
|
420
442
|
end
|
|
421
443
|
|
|
422
|
-
def publish(
|
|
423
|
-
@delegate.call
|
|
444
|
+
def publish(...)
|
|
445
|
+
@delegate.call(...)
|
|
424
446
|
end
|
|
425
447
|
end
|
|
426
448
|
|
|
@@ -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.
|
|
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.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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.
|
|
48
|
-
app.reloader.before_class_unload
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
@@ -96,12 +119,6 @@ module ActiveSupport
|
|
|
96
119
|
config.eager_load_namespaces << TZInfo
|
|
97
120
|
end
|
|
98
121
|
|
|
99
|
-
initializer "active_support.to_time_preserves_timezone" do |app|
|
|
100
|
-
config.after_initialize do
|
|
101
|
-
ActiveSupport.to_time_preserves_timezone = app.config.active_support.to_time_preserves_timezone
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
122
|
# Sets the default week start
|
|
106
123
|
# If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised.
|
|
107
124
|
initializer "active_support.initialize_beginning_of_week" do |app|
|
|
@@ -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
|
|
@@ -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
|
|
@@ -51,8 +68,8 @@ module ActiveSupport
|
|
|
51
68
|
# is forked. For each process a new database will be created suffixed
|
|
52
69
|
# with the worker number.
|
|
53
70
|
#
|
|
54
|
-
# test-
|
|
55
|
-
# test-
|
|
71
|
+
# test-database_0
|
|
72
|
+
# test-database_1
|
|
56
73
|
#
|
|
57
74
|
# If <tt>ENV["PARALLEL_WORKERS"]</tt> is set the workers argument will be ignored
|
|
58
75
|
# and the environment variable will be used instead. This is useful for CI
|
|
@@ -78,14 +95,45 @@ module ActiveSupport
|
|
|
78
95
|
# Because parallelization presents an overhead, it is only enabled when the
|
|
79
96
|
# number of tests to run is above the +threshold+ param. The default value is
|
|
80
97
|
# 50, and it's configurable via +config.active_support.test_parallelization_threshold+.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
98
|
+
#
|
|
99
|
+
# If you want to skip Rails default creation of one database per process in favor of
|
|
100
|
+
# writing your own implementation, you can set +parallelize_databases+, or configure it
|
|
101
|
+
# via +config.active_support.parallelize_test_databases+.
|
|
102
|
+
#
|
|
103
|
+
# parallelize(workers: :number_of_processors, parallelize_databases: false)
|
|
104
|
+
#
|
|
105
|
+
# Note that your test suite may deadlock if you attempt to use only one database
|
|
106
|
+
# with multiple processes.
|
|
107
|
+
def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold, parallelize_databases: ActiveSupport.parallelize_test_databases)
|
|
108
|
+
case
|
|
109
|
+
when ENV["PARALLEL_WORKERS"]
|
|
110
|
+
workers = ENV["PARALLEL_WORKERS"].to_i
|
|
111
|
+
when workers == :number_of_processors
|
|
112
|
+
workers = (Concurrent.available_processor_count || Concurrent.processor_count).floor
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if with == :processes
|
|
116
|
+
ActiveSupport.parallelize_test_databases = parallelize_databases
|
|
117
|
+
end
|
|
84
118
|
|
|
85
119
|
Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
|
|
86
120
|
end
|
|
87
121
|
|
|
88
|
-
#
|
|
122
|
+
# Before fork hook for parallel testing. This can be used to run anything
|
|
123
|
+
# before the processes are forked.
|
|
124
|
+
#
|
|
125
|
+
# In your +test_helper.rb+ add the following:
|
|
126
|
+
#
|
|
127
|
+
# class ActiveSupport::TestCase
|
|
128
|
+
# parallelize_before_fork do
|
|
129
|
+
# # run this before fork
|
|
130
|
+
# end
|
|
131
|
+
# end
|
|
132
|
+
def parallelize_before_fork(&block)
|
|
133
|
+
ActiveSupport::Testing::Parallelization.before_fork_hook(&block)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Setup hook for parallel testing. This can be used if you have multiple
|
|
89
137
|
# databases or any behavior that needs to be run after the process is forked
|
|
90
138
|
# but before the tests run.
|
|
91
139
|
#
|
|
@@ -141,11 +189,18 @@ module ActiveSupport
|
|
|
141
189
|
|
|
142
190
|
alias_method :method_name, :name
|
|
143
191
|
|
|
192
|
+
# Returns the current parallel worker ID if tests are running in parallel
|
|
193
|
+
def parallel_worker_id
|
|
194
|
+
self.class.parallel_worker_id
|
|
195
|
+
end
|
|
196
|
+
|
|
144
197
|
include ActiveSupport::Testing::TaggedLogging
|
|
145
198
|
prepend ActiveSupport::Testing::SetupAndTeardown
|
|
146
199
|
prepend ActiveSupport::Testing::TestsWithoutAssertions
|
|
147
200
|
include ActiveSupport::Testing::Assertions
|
|
148
201
|
include ActiveSupport::Testing::ErrorReporterAssertions
|
|
202
|
+
include ActiveSupport::Testing::EventReporterAssertions
|
|
203
|
+
include ActiveSupport::Testing::NotificationAssertions
|
|
149
204
|
include ActiveSupport::Testing::Deprecation
|
|
150
205
|
include ActiveSupport::Testing::ConstantStubbing
|
|
151
206
|
include ActiveSupport::Testing::TimeHelpers
|