activesupport 6.0.3.7 → 7.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +220 -533
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_support/actionable_error.rb +1 -1
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +3 -3
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/cache/file_store.rb +18 -11
- data/lib/active_support/cache/mem_cache_store.rb +143 -37
- data/lib/active_support/cache/memory_store.rb +56 -28
- data/lib/active_support/cache/null_store.rb +10 -2
- data/lib/active_support/cache/redis_cache_store.rb +63 -88
- data/lib/active_support/cache/strategy/local_cache.rb +46 -57
- data/lib/active_support/cache.rb +273 -82
- data/lib/active_support/callbacks.rb +226 -118
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +49 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +9 -6
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +9 -7
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array.rb +1 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +21 -40
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +4 -4
- data/lib/active_support/core_ext/date/conversions.rb +5 -4
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date.rb +1 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +39 -13
- data/lib/active_support/core_ext/enumerable.rb +139 -15
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/conversions.rb +2 -2
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +2 -2
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +40 -36
- data/lib/active_support/core_ext/module/introspection.rb +1 -25
- data/lib/active_support/core_ext/name_error.rb +23 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +79 -72
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric.rb +1 -0
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +11 -0
- data/lib/active_support/core_ext/object/json.rb +42 -26
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/object/with_options.rb +20 -1
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +6 -25
- data/lib/active_support/core_ext/range/conversions.rb +8 -8
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +1 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +8 -1
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +39 -5
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +69 -45
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +26 -6
- data/lib/active_support/core_ext/time/conversions.rb +6 -3
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +4 -19
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -23
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +39 -16
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +58 -764
- data/lib/active_support/deprecation/behaviors.rb +19 -3
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +6 -5
- data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +177 -64
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +24 -10
- data/lib/active_support/duration.rb +134 -55
- data/lib/active_support/encrypted_configuration.rb +11 -1
- data/lib/active_support/encrypted_file.rb +20 -3
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +70 -134
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +30 -4
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +51 -25
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +14 -19
- data/lib/active_support/inflector/inflections.rb +24 -9
- data/lib/active_support/inflector/methods.rb +29 -49
- data/lib/active_support/inflector/transliterate.rb +4 -4
- data/lib/active_support/isolated_execution_state.rb +56 -0
- data/lib/active_support/json/decoding.rb +4 -4
- data/lib/active_support/json/encoding.rb +8 -4
- data/lib/active_support/key_generator.rb +19 -2
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber.rb +21 -3
- data/lib/active_support/logger.rb +1 -1
- data/lib/active_support/logger_silence.rb +2 -26
- data/lib/active_support/logger_thread_safe_level.rb +34 -21
- data/lib/active_support/message_encryptor.rb +12 -10
- data/lib/active_support/message_verifier.rb +50 -18
- data/lib/active_support/messages/metadata.rb +11 -3
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +6 -5
- data/lib/active_support/multibyte/chars.rb +13 -52
- data/lib/active_support/multibyte/unicode.rb +1 -87
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +110 -69
- data/lib/active_support/notifications/instrumenter.rb +37 -29
- data/lib/active_support/notifications.rb +47 -26
- data/lib/active_support/number_helper/number_converter.rb +2 -4
- data/lib/active_support/number_helper/number_to_currency_converter.rb +10 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
- data/lib/active_support/number_helper/rounding_helper.rb +12 -32
- data/lib/active_support/number_helper.rb +29 -16
- data/lib/active_support/option_merger.rb +9 -16
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +8 -2
- data/lib/active_support/parameter_filter.rb +21 -11
- data/lib/active_support/per_thread_registry.rb +6 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +77 -5
- data/lib/active_support/rescuable.rb +6 -6
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +2 -2
- data/lib/active_support/subscriber.rb +19 -25
- data/lib/active_support/tagged_logging.rb +31 -6
- data/lib/active_support/test_case.rb +9 -21
- data/lib/active_support/testing/assertions.rb +49 -12
- data/lib/active_support/testing/deprecation.rb +52 -1
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +5 -5
- data/lib/active_support/testing/parallelization/server.rb +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +16 -95
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +3 -5
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +53 -5
- data/lib/active_support/time_with_zone.rb +120 -55
- data/lib/active_support/values/time_zone.rb +49 -18
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +4 -4
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +9 -2
- data/lib/active_support/xml_mini.rb +5 -4
- data/lib/active_support.rb +29 -1
- metadata +46 -45
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
- data/lib/active_support/core_ext/hash/compact.rb +0 -5
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
- data/lib/active_support/core_ext/marshal.rb +0 -24
- data/lib/active_support/core_ext/module/reachable.rb +0 -6
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
- data/lib/active_support/core_ext/range/include_range.rb +0 -9
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -1,30 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "digest/sha2"
|
4
|
-
|
5
3
|
module ActiveSupport
|
6
4
|
module SecurityUtils
|
7
5
|
# Constant time string comparison, for fixed length strings.
|
8
6
|
#
|
9
7
|
# The values compared should be of fixed length, such as strings
|
10
8
|
# that have already been processed by HMAC. Raises in case of length mismatch.
|
11
|
-
def fixed_length_secure_compare(a, b)
|
12
|
-
raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
|
13
9
|
|
14
|
-
|
10
|
+
if defined?(OpenSSL.fixed_length_secure_compare)
|
11
|
+
def fixed_length_secure_compare(a, b)
|
12
|
+
OpenSSL.fixed_length_secure_compare(a, b)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
def fixed_length_secure_compare(a, b)
|
16
|
+
raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
|
17
|
+
|
18
|
+
l = a.unpack "C#{a.bytesize}"
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
20
|
+
res = 0
|
21
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
22
|
+
res == 0
|
23
|
+
end
|
19
24
|
end
|
20
25
|
module_function :fixed_length_secure_compare
|
21
26
|
|
22
|
-
#
|
27
|
+
# Secure string comparison for strings of variable length.
|
23
28
|
#
|
24
|
-
#
|
25
|
-
# via
|
29
|
+
# While a timing attack would not be able to discern the content of
|
30
|
+
# a secret compared via secure_compare, it is possible to determine
|
31
|
+
# the secret length. This should be considered when using secure_compare
|
32
|
+
# to compare weak, short secrets to user input.
|
26
33
|
def secure_compare(a, b)
|
27
|
-
|
34
|
+
a.bytesize == b.bytesize && fixed_length_secure_compare(a, b)
|
28
35
|
end
|
29
36
|
module_function :secure_compare
|
30
37
|
end
|
@@ -19,11 +19,11 @@ module ActiveSupport
|
|
19
19
|
class StringInquirer < String
|
20
20
|
private
|
21
21
|
def respond_to_missing?(method_name, include_private = false)
|
22
|
-
(
|
22
|
+
method_name.end_with?("?") || super
|
23
23
|
end
|
24
24
|
|
25
25
|
def method_missing(method_name, *arguments)
|
26
|
-
if method_name
|
26
|
+
if method_name.end_with?("?")
|
27
27
|
self == method_name[0..-2]
|
28
28
|
else
|
29
29
|
super
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/per_thread_registry"
|
4
3
|
require "active_support/notifications"
|
5
4
|
|
6
5
|
module ActiveSupport
|
@@ -31,15 +30,16 @@ module ActiveSupport
|
|
31
30
|
class Subscriber
|
32
31
|
class << self
|
33
32
|
# Attach the subscriber to a namespace.
|
34
|
-
def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications)
|
33
|
+
def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications, inherit_all: false)
|
35
34
|
@namespace = namespace
|
36
35
|
@subscriber = subscriber
|
37
36
|
@notifier = notifier
|
37
|
+
@inherit_all = inherit_all
|
38
38
|
|
39
39
|
subscribers << subscriber
|
40
40
|
|
41
41
|
# Add event subscribers for all existing methods on the class.
|
42
|
-
subscriber
|
42
|
+
fetch_public_methods(subscriber, inherit_all).each do |event|
|
43
43
|
add_event_subscriber(event)
|
44
44
|
end
|
45
45
|
end
|
@@ -55,7 +55,7 @@ module ActiveSupport
|
|
55
55
|
subscribers.delete(subscriber)
|
56
56
|
|
57
57
|
# Remove event subscribers of all existing methods on the class.
|
58
|
-
subscriber
|
58
|
+
fetch_public_methods(subscriber, true).each do |event|
|
59
59
|
remove_event_subscriber(event)
|
60
60
|
end
|
61
61
|
|
@@ -81,18 +81,18 @@ module ActiveSupport
|
|
81
81
|
attr_reader :subscriber, :notifier, :namespace
|
82
82
|
|
83
83
|
def add_event_subscriber(event) # :doc:
|
84
|
-
return if invalid_event?(event
|
84
|
+
return if invalid_event?(event)
|
85
85
|
|
86
86
|
pattern = prepare_pattern(event)
|
87
87
|
|
88
|
-
# Don't add multiple subscribers (
|
88
|
+
# Don't add multiple subscribers (e.g. if methods are redefined).
|
89
89
|
return if pattern_subscribed?(pattern)
|
90
90
|
|
91
91
|
subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
|
92
92
|
end
|
93
93
|
|
94
94
|
def remove_event_subscriber(event) # :doc:
|
95
|
-
return if invalid_event?(event
|
95
|
+
return if invalid_event?(event)
|
96
96
|
|
97
97
|
pattern = prepare_pattern(event)
|
98
98
|
|
@@ -107,7 +107,7 @@ module ActiveSupport
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def invalid_event?(event)
|
110
|
-
%
|
110
|
+
%i{ start finish }.include?(event.to_sym)
|
111
111
|
end
|
112
112
|
|
113
113
|
def prepare_pattern(event)
|
@@ -117,6 +117,10 @@ module ActiveSupport
|
|
117
117
|
def pattern_subscribed?(pattern)
|
118
118
|
subscriber.patterns.key?(pattern)
|
119
119
|
end
|
120
|
+
|
121
|
+
def fetch_public_methods(subscriber, inherit_all)
|
122
|
+
subscriber.public_methods(inherit_all) - Subscriber.public_instance_methods(true)
|
123
|
+
end
|
120
124
|
end
|
121
125
|
|
122
126
|
attr_reader :patterns # :nodoc:
|
@@ -145,25 +149,15 @@ module ActiveSupport
|
|
145
149
|
send(method, event)
|
146
150
|
end
|
147
151
|
|
152
|
+
def publish_event(event) # :nodoc:
|
153
|
+
method = event.name.split(".").first
|
154
|
+
send(method, event)
|
155
|
+
end
|
156
|
+
|
148
157
|
private
|
149
158
|
def event_stack
|
150
|
-
|
159
|
+
registry = ActiveSupport::IsolatedExecutionState[:active_support_subscriber_queue_registry] ||= {}
|
160
|
+
registry[@queue_key] ||= []
|
151
161
|
end
|
152
162
|
end
|
153
|
-
|
154
|
-
# This is a registry for all the event stacks kept for subscribers.
|
155
|
-
#
|
156
|
-
# See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
|
157
|
-
# for further details.
|
158
|
-
class SubscriberQueueRegistry # :nodoc:
|
159
|
-
extend PerThreadRegistry
|
160
|
-
|
161
|
-
def initialize
|
162
|
-
@registry = {}
|
163
|
-
end
|
164
|
-
|
165
|
-
def get_queue(queue_key)
|
166
|
-
@registry[queue_key] ||= []
|
167
|
-
end
|
168
|
-
end
|
169
163
|
end
|
@@ -8,11 +8,20 @@ require "active_support/logger"
|
|
8
8
|
module ActiveSupport
|
9
9
|
# Wraps any standard Logger object to provide tagging capabilities.
|
10
10
|
#
|
11
|
+
# May be called with a block:
|
12
|
+
#
|
11
13
|
# logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
|
12
14
|
# logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
|
13
15
|
# logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
|
14
16
|
# logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
|
15
17
|
#
|
18
|
+
# If called without a block, a new logger will be returned with applied tags:
|
19
|
+
#
|
20
|
+
# logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
|
21
|
+
# logger.tagged("BCX").info "Stuff" # Logs "[BCX] Stuff"
|
22
|
+
# logger.tagged("BCX", "Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
|
23
|
+
# logger.tagged("BCX").tagged("Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
|
24
|
+
#
|
16
25
|
# This is used by the default Rails.logger as configured by Railties to make
|
17
26
|
# it easy to stamp log lines with subdomains, request ids, and anything else
|
18
27
|
# to aid debugging of multi-user production applications.
|
@@ -31,9 +40,10 @@ module ActiveSupport
|
|
31
40
|
end
|
32
41
|
|
33
42
|
def push_tags(*tags)
|
34
|
-
tags.flatten
|
35
|
-
|
36
|
-
|
43
|
+
tags.flatten!
|
44
|
+
tags.reject!(&:blank?)
|
45
|
+
current_tags.concat tags
|
46
|
+
tags
|
37
47
|
end
|
38
48
|
|
39
49
|
def pop_tags(size = 1)
|
@@ -47,7 +57,7 @@ module ActiveSupport
|
|
47
57
|
def current_tags
|
48
58
|
# We use our object ID here to avoid conflicting with other instances
|
49
59
|
thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
|
50
|
-
|
60
|
+
IsolatedExecutionState[thread_key] ||= []
|
51
61
|
end
|
52
62
|
|
53
63
|
def tags_text
|
@@ -60,8 +70,16 @@ module ActiveSupport
|
|
60
70
|
end
|
61
71
|
end
|
62
72
|
|
73
|
+
module LocalTagStorage # :nodoc:
|
74
|
+
attr_accessor :current_tags
|
75
|
+
|
76
|
+
def self.extended(base)
|
77
|
+
base.current_tags = []
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
63
81
|
def self.new(logger)
|
64
|
-
logger = logger.
|
82
|
+
logger = logger.clone
|
65
83
|
|
66
84
|
if logger.formatter
|
67
85
|
logger.formatter = logger.formatter.dup
|
@@ -77,7 +95,14 @@ module ActiveSupport
|
|
77
95
|
delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
|
78
96
|
|
79
97
|
def tagged(*tags)
|
80
|
-
|
98
|
+
if block_given?
|
99
|
+
formatter.tagged(*tags) { yield self }
|
100
|
+
else
|
101
|
+
logger = ActiveSupport::TaggedLogging.new(self)
|
102
|
+
logger.formatter.extend LocalTagStorage
|
103
|
+
logger.push_tags(*formatter.current_tags, *tags)
|
104
|
+
logger
|
105
|
+
end
|
81
106
|
end
|
82
107
|
|
83
108
|
def flush
|
@@ -12,6 +12,7 @@ require "active_support/testing/constant_lookup"
|
|
12
12
|
require "active_support/testing/time_helpers"
|
13
13
|
require "active_support/testing/file_fixtures"
|
14
14
|
require "active_support/testing/parallelization"
|
15
|
+
require "active_support/testing/parallelize_executor"
|
15
16
|
require "concurrent/utility/processor_counter"
|
16
17
|
|
17
18
|
module ActiveSupport
|
@@ -71,26 +72,17 @@ module ActiveSupport
|
|
71
72
|
#
|
72
73
|
# The threaded parallelization uses minitest's parallel executor directly.
|
73
74
|
# The processes parallelization uses a Ruby DRb server.
|
74
|
-
|
75
|
+
#
|
76
|
+
# Because parallelization presents an overhead, it is only enabled when the
|
77
|
+
# number of tests to run is above the +threshold+ param. The default value is
|
78
|
+
# 50, and it's configurable via +config.active_support.test_parallelization_threshold+.
|
79
|
+
def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold)
|
75
80
|
workers = Concurrent.physical_processor_count if workers == :number_of_processors
|
76
81
|
workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
|
77
82
|
|
78
83
|
return if workers <= 1
|
79
84
|
|
80
|
-
|
81
|
-
when :processes
|
82
|
-
Testing::Parallelization.new(workers)
|
83
|
-
when :threads
|
84
|
-
Minitest::Parallel::Executor.new(workers)
|
85
|
-
else
|
86
|
-
raise ArgumentError, "#{with} is not a supported parallelization executor."
|
87
|
-
end
|
88
|
-
|
89
|
-
self.lock_threads = false if defined?(self.lock_threads) && with == :threads
|
90
|
-
|
91
|
-
Minitest.parallel_executor = executor
|
92
|
-
|
93
|
-
parallelize_me!
|
85
|
+
Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
|
94
86
|
end
|
95
87
|
|
96
88
|
# Set up hook for parallel testing. This can be used if you have multiple
|
@@ -107,9 +99,7 @@ module ActiveSupport
|
|
107
99
|
# end
|
108
100
|
# end
|
109
101
|
def parallelize_setup(&block)
|
110
|
-
ActiveSupport::Testing::Parallelization.after_fork_hook
|
111
|
-
yield worker
|
112
|
-
end
|
102
|
+
ActiveSupport::Testing::Parallelization.after_fork_hook(&block)
|
113
103
|
end
|
114
104
|
|
115
105
|
# Clean up hook for parallel testing. This can be used to drop databases
|
@@ -126,9 +116,7 @@ module ActiveSupport
|
|
126
116
|
# end
|
127
117
|
# end
|
128
118
|
def parallelize_teardown(&block)
|
129
|
-
ActiveSupport::Testing::Parallelization.run_cleanup_hook
|
130
|
-
yield worker
|
131
|
-
end
|
119
|
+
ActiveSupport::Testing::Parallelization.run_cleanup_hook(&block)
|
132
120
|
end
|
133
121
|
end
|
134
122
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActiveSupport
|
4
6
|
module Testing
|
5
7
|
module Assertions
|
@@ -30,6 +32,8 @@ module ActiveSupport
|
|
30
32
|
# end
|
31
33
|
def assert_nothing_raised
|
32
34
|
yield
|
35
|
+
rescue => error
|
36
|
+
raise Minitest::UnexpectedError.new(error)
|
33
37
|
end
|
34
38
|
|
35
39
|
# Test numeric difference between the return value of an expression as a
|
@@ -87,7 +91,7 @@ module ActiveSupport
|
|
87
91
|
else
|
88
92
|
difference = args[0] || 1
|
89
93
|
message = args[1]
|
90
|
-
|
94
|
+
Array(expression).index_with(difference)
|
91
95
|
end
|
92
96
|
|
93
97
|
exps = expressions.keys.map { |e|
|
@@ -95,7 +99,7 @@ module ActiveSupport
|
|
95
99
|
}
|
96
100
|
before = exps.map(&:call)
|
97
101
|
|
98
|
-
retval =
|
102
|
+
retval = _assert_nothing_raised_or_warn("assert_difference", &block)
|
99
103
|
|
100
104
|
expressions.zip(exps, before) do |(code, diff), exp, before_value|
|
101
105
|
error = "#{code.inspect} didn't change by #{diff}"
|
@@ -172,10 +176,10 @@ module ActiveSupport
|
|
172
176
|
exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
|
173
177
|
|
174
178
|
before = exp.call
|
175
|
-
retval =
|
179
|
+
retval = _assert_nothing_raised_or_warn("assert_changes", &block)
|
176
180
|
|
177
181
|
unless from == UNTRACKED
|
178
|
-
error = "
|
182
|
+
error = "Expected change from #{from.inspect}"
|
179
183
|
error = "#{message}.\n#{error}" if message
|
180
184
|
assert from === before, error
|
181
185
|
end
|
@@ -185,12 +189,10 @@ module ActiveSupport
|
|
185
189
|
error = "#{expression.inspect} didn't change"
|
186
190
|
error = "#{error}. It was already #{to}" if before == to
|
187
191
|
error = "#{message}.\n#{error}" if message
|
188
|
-
|
192
|
+
refute_equal before, after, error
|
189
193
|
|
190
194
|
unless to == UNTRACKED
|
191
|
-
error = "
|
192
|
-
error = "#{error}Expected: #{to.inspect}\n"
|
193
|
-
error = "#{error} Actual: #{after.inspect}"
|
195
|
+
error = "Expected change to #{to}\n"
|
194
196
|
error = "#{message}.\n#{error}" if message
|
195
197
|
assert to === after, error
|
196
198
|
end
|
@@ -205,24 +207,59 @@ module ActiveSupport
|
|
205
207
|
# post :create, params: { status: { ok: true } }
|
206
208
|
# end
|
207
209
|
#
|
210
|
+
# Provide the optional keyword argument :from to specify the expected
|
211
|
+
# initial value.
|
212
|
+
#
|
213
|
+
# assert_no_changes -> { Status.all_good? }, from: true do
|
214
|
+
# post :create, params: { status: { ok: true } }
|
215
|
+
# end
|
216
|
+
#
|
208
217
|
# An error message can be specified.
|
209
218
|
#
|
210
219
|
# assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
|
211
220
|
# post :create, params: { status: { ok: false } }
|
212
221
|
# end
|
213
|
-
def assert_no_changes(expression, message = nil, &block)
|
222
|
+
def assert_no_changes(expression, message = nil, from: UNTRACKED, &block)
|
214
223
|
exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
|
215
224
|
|
216
225
|
before = exp.call
|
217
|
-
retval =
|
226
|
+
retval = _assert_nothing_raised_or_warn("assert_no_changes", &block)
|
227
|
+
|
228
|
+
unless from == UNTRACKED
|
229
|
+
error = "Expected initial value of #{from.inspect}"
|
230
|
+
error = "#{message}.\n#{error}" if message
|
231
|
+
assert from === before, error
|
232
|
+
end
|
233
|
+
|
218
234
|
after = exp.call
|
219
235
|
|
220
|
-
error = "#{expression.inspect}
|
236
|
+
error = "#{expression.inspect} changed"
|
221
237
|
error = "#{message}.\n#{error}" if message
|
222
|
-
|
238
|
+
|
239
|
+
if before.nil?
|
240
|
+
assert_nil after, error
|
241
|
+
else
|
242
|
+
assert_equal before, after, error
|
243
|
+
end
|
223
244
|
|
224
245
|
retval
|
225
246
|
end
|
247
|
+
|
248
|
+
private
|
249
|
+
def _assert_nothing_raised_or_warn(assertion, &block)
|
250
|
+
assert_nothing_raised(&block)
|
251
|
+
rescue Minitest::UnexpectedError => e
|
252
|
+
if tagged_logger && tagged_logger.warn?
|
253
|
+
warning = <<~MSG
|
254
|
+
#{self.class} - #{name}: #{e.error.class} raised.
|
255
|
+
If you expected this exception, use `assert_raises` as near to the code that raises as possible.
|
256
|
+
Other block based assertions (e.g. `#{assertion}`) can be used, as long as `assert_raises` is inside their block.
|
257
|
+
MSG
|
258
|
+
tagged_logger.warn warning
|
259
|
+
end
|
260
|
+
|
261
|
+
raise
|
262
|
+
end
|
226
263
|
end
|
227
264
|
end
|
228
265
|
end
|
@@ -4,7 +4,30 @@ require "active_support/deprecation"
|
|
4
4
|
|
5
5
|
module ActiveSupport
|
6
6
|
module Testing
|
7
|
-
module Deprecation
|
7
|
+
module Deprecation
|
8
|
+
# Asserts that a matching deprecation warning was emitted by the given deprecator during the execution of the yielded block.
|
9
|
+
#
|
10
|
+
# assert_deprecated(/foo/, CustomDeprecator) do
|
11
|
+
# CustomDeprecator.warn "foo should no longer be used"
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# The +match+ object may be a +Regexp+, or +String+ appearing in the message.
|
15
|
+
#
|
16
|
+
# assert_deprecated('foo', CustomDeprecator) do
|
17
|
+
# CustomDeprecator.warn "foo should no longer be used"
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# If the +match+ is omitted (or explicitly +nil+), any deprecation warning will match.
|
21
|
+
#
|
22
|
+
# assert_deprecated(nil, CustomDeprecator) do
|
23
|
+
# CustomDeprecator.warn "foo should no longer be used"
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
|
27
|
+
#
|
28
|
+
# assert_deprecated do
|
29
|
+
# ActiveSupport::Deprecation.warn "foo should no longer be used"
|
30
|
+
# end
|
8
31
|
def assert_deprecated(match = nil, deprecator = nil, &block)
|
9
32
|
result, warnings = collect_deprecations(deprecator, &block)
|
10
33
|
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
|
@@ -15,12 +38,40 @@ module ActiveSupport
|
|
15
38
|
result
|
16
39
|
end
|
17
40
|
|
41
|
+
# Asserts that no deprecation warnings are emitted by the given deprecator during the execution of the yielded block.
|
42
|
+
#
|
43
|
+
# assert_not_deprecated(CustomDeprecator) do
|
44
|
+
# CustomDeprecator.warn "message" # fails assertion
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
|
48
|
+
#
|
49
|
+
# assert_not_deprecated do
|
50
|
+
# ActiveSupport::Deprecation.warn "message" # fails assertion
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# assert_not_deprecated do
|
54
|
+
# CustomDeprecator.warn "message" # passes assertion
|
55
|
+
# end
|
18
56
|
def assert_not_deprecated(deprecator = nil, &block)
|
19
57
|
result, deprecations = collect_deprecations(deprecator, &block)
|
20
58
|
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
|
21
59
|
result
|
22
60
|
end
|
23
61
|
|
62
|
+
# Returns an array of all the deprecation warnings emitted by the given
|
63
|
+
# +deprecator+ during the execution of the yielded block.
|
64
|
+
#
|
65
|
+
# collect_deprecations(CustomDeprecator) do
|
66
|
+
# CustomDeprecator.warn "message"
|
67
|
+
# end # => ["message"]
|
68
|
+
#
|
69
|
+
# If no +deprecator+ is given, defaults to ActiveSupport::Deprecation.
|
70
|
+
#
|
71
|
+
# collect_deprecations do
|
72
|
+
# CustomDeprecator.warn "custom message"
|
73
|
+
# ActiveSupport::Deprecation.warn "message"
|
74
|
+
# end # => ["message"]
|
24
75
|
def collect_deprecations(deprecator = nil)
|
25
76
|
deprecator ||= ActiveSupport::Deprecation
|
26
77
|
old_behavior = deprecator.behavior
|
@@ -5,7 +5,7 @@ module ActiveSupport
|
|
5
5
|
module Isolation
|
6
6
|
require "thread"
|
7
7
|
|
8
|
-
def self.included(klass)
|
8
|
+
def self.included(klass) # :nodoc:
|
9
9
|
klass.class_eval do
|
10
10
|
parallelize_me!
|
11
11
|
end
|
@@ -63,7 +63,7 @@ module ActiveSupport
|
|
63
63
|
module Subprocess
|
64
64
|
ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
|
65
65
|
|
66
|
-
#
|
66
|
+
# Complicated H4X to get this working in windows / jruby with
|
67
67
|
# no forking.
|
68
68
|
def run_in_isolation(&blk)
|
69
69
|
require "tempfile"
|
@@ -6,10 +6,10 @@ module ActiveSupport
|
|
6
6
|
module Testing
|
7
7
|
module MethodCallAssertions # :nodoc:
|
8
8
|
private
|
9
|
-
def assert_called(object, method_name, message = nil, times: 1, returns: nil)
|
9
|
+
def assert_called(object, method_name, message = nil, times: 1, returns: nil, &block)
|
10
10
|
times_called = 0
|
11
11
|
|
12
|
-
object.stub(method_name, proc { times_called += 1; returns })
|
12
|
+
object.stub(method_name, proc { times_called += 1; returns }, &block)
|
13
13
|
|
14
14
|
error = "Expected #{method_name} to be called #{times} times, " \
|
15
15
|
"but was called #{times_called} times"
|
@@ -17,16 +17,16 @@ module ActiveSupport
|
|
17
17
|
assert_equal times, times_called, error
|
18
18
|
end
|
19
19
|
|
20
|
-
def assert_called_with(object, method_name, args, returns: nil)
|
20
|
+
def assert_called_with(object, method_name, args, returns: nil, &block)
|
21
21
|
mock = Minitest::Mock.new
|
22
22
|
|
23
|
-
if args.all?
|
23
|
+
if args.all?(Array)
|
24
24
|
args.each { |arg| mock.expect(:call, returns, arg) }
|
25
25
|
else
|
26
26
|
mock.expect(:call, returns, args)
|
27
27
|
end
|
28
28
|
|
29
|
-
object.stub(method_name, mock)
|
29
|
+
object.stub(method_name, mock, &block)
|
30
30
|
|
31
31
|
mock.verify
|
32
32
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "drb"
|
4
|
+
require "drb/unix" unless Gem.win_platform?
|
5
|
+
|
6
|
+
module ActiveSupport
|
7
|
+
module Testing
|
8
|
+
class Parallelization # :nodoc:
|
9
|
+
class Server
|
10
|
+
include DRb::DRbUndumped
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@queue = Queue.new
|
14
|
+
@active_workers = Concurrent::Map.new
|
15
|
+
@in_flight = Concurrent::Map.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def record(reporter, result)
|
19
|
+
raise DRb::DRbConnError if result.is_a?(DRb::DRbUnknown)
|
20
|
+
|
21
|
+
@in_flight.delete([result.klass, result.name])
|
22
|
+
|
23
|
+
reporter.synchronize do
|
24
|
+
reporter.record(result)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def <<(o)
|
29
|
+
o[2] = DRbObject.new(o[2]) if o
|
30
|
+
@queue << o
|
31
|
+
end
|
32
|
+
|
33
|
+
def pop
|
34
|
+
if test = @queue.pop
|
35
|
+
@in_flight[[test[0].to_s, test[1]]] = test
|
36
|
+
test
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def start_worker(worker_id)
|
41
|
+
@active_workers[worker_id] = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def stop_worker(worker_id)
|
45
|
+
@active_workers.delete(worker_id)
|
46
|
+
end
|
47
|
+
|
48
|
+
def active_workers?
|
49
|
+
@active_workers.size > 0
|
50
|
+
end
|
51
|
+
|
52
|
+
def interrupt
|
53
|
+
@queue.clear
|
54
|
+
end
|
55
|
+
|
56
|
+
def shutdown
|
57
|
+
# Wait for initial queue to drain
|
58
|
+
while @queue.length != 0
|
59
|
+
sleep 0.1
|
60
|
+
end
|
61
|
+
|
62
|
+
@queue.close
|
63
|
+
|
64
|
+
# Wait until all workers have finished
|
65
|
+
while active_workers?
|
66
|
+
sleep 0.1
|
67
|
+
end
|
68
|
+
|
69
|
+
@in_flight.values.each do |(klass, name, reporter)|
|
70
|
+
result = Minitest::Result.from(klass.new(name))
|
71
|
+
error = RuntimeError.new("result not reported")
|
72
|
+
error.set_backtrace([""])
|
73
|
+
result.failures << Minitest::UnexpectedError.new(error)
|
74
|
+
reporter.synchronize do
|
75
|
+
reporter.record(result)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|