activesupport 5.2.4 → 6.0.2.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +391 -395
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -2
- data/lib/active_support.rb +2 -1
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +28 -1
- data/lib/active_support/cache.rb +45 -23
- data/lib/active_support/cache/file_store.rb +22 -22
- data/lib/active_support/cache/mem_cache_store.rb +5 -0
- data/lib/active_support/cache/memory_store.rb +9 -2
- data/lib/active_support/cache/null_store.rb +5 -0
- data/lib/active_support/cache/redis_cache_store.rb +37 -10
- data/lib/active_support/callbacks.rb +16 -5
- data/lib/active_support/concern.rb +24 -1
- data/lib/active_support/configurable.rb +7 -11
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
- data/lib/active_support/core_ext/class/attribute.rb +11 -16
- data/lib/active_support/core_ext/class/subclasses.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +97 -73
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/hash/compact.rb +2 -26
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +0 -29
- data/lib/active_support/core_ext/hash/slice.rb +3 -25
- data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
- data/lib/active_support/core_ext/module/delegation.rb +33 -7
- data/lib/active_support/core_ext/module/introspection.rb +37 -13
- data/lib/active_support/core_ext/module/reachable.rb +1 -6
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
- data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +1 -0
- data/lib/active_support/core_ext/object/try.rb +15 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +28 -13
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/include_range.rb +6 -0
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
- data/lib/active_support/core_ext/regexp.rb +0 -4
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +8 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +7 -2
- data/lib/active_support/core_ext/string/multibyte.rb +4 -3
- data/lib/active_support/core_ext/string/output_safety.rb +63 -5
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +31 -2
- data/lib/active_support/core_ext/uri.rb +1 -0
- data/lib/active_support/current_attributes.rb +8 -0
- data/lib/active_support/dependencies.rb +69 -16
- data/lib/active_support/dependencies/zeitwerk_integration.rb +118 -0
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/deprecation/behaviors.rb +1 -1
- data/lib/active_support/deprecation/method_wrappers.rb +8 -20
- data/lib/active_support/deprecation/proxy_wrappers.rb +24 -5
- data/lib/active_support/descendants_tracker.rb +56 -9
- data/lib/active_support/duration.rb +6 -5
- data/lib/active_support/duration/iso8601_parser.rb +2 -3
- data/lib/active_support/duration/iso8601_serializer.rb +3 -4
- data/lib/active_support/encrypted_configuration.rb +0 -4
- data/lib/active_support/encrypted_file.rb +2 -1
- data/lib/active_support/evented_file_update_checker.rb +39 -9
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +22 -18
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +13 -1
- data/lib/active_support/inflector/inflections.rb +1 -4
- data/lib/active_support/inflector/methods.rb +15 -27
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +23 -23
- data/lib/active_support/json/encoding.rb +6 -2
- data/lib/active_support/key_generator.rb +0 -32
- data/lib/active_support/lazy_load_hooks.rb +5 -1
- data/lib/active_support/locale/en.rb +31 -0
- data/lib/active_support/log_subscriber.rb +31 -8
- data/lib/active_support/logger.rb +0 -15
- data/lib/active_support/logger_silence.rb +28 -12
- data/lib/active_support/logger_thread_safe_level.rb +26 -4
- data/lib/active_support/message_encryptor.rb +3 -5
- data/lib/active_support/message_verifier.rb +3 -3
- data/lib/active_support/multibyte/chars.rb +29 -48
- data/lib/active_support/multibyte/unicode.rb +44 -281
- data/lib/active_support/notifications.rb +41 -4
- data/lib/active_support/notifications/fanout.rb +98 -13
- data/lib/active_support/notifications/instrumenter.rb +80 -8
- data/lib/active_support/number_helper.rb +7 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +1 -1
- data/lib/active_support/parameter_filter.rb +129 -0
- data/lib/active_support/rails.rb +0 -6
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/subscriber.rb +65 -26
- data/lib/active_support/tagged_logging.rb +13 -4
- data/lib/active_support/test_case.rb +91 -0
- data/lib/active_support/testing/assertions.rb +15 -1
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -1
- data/lib/active_support/testing/parallelization.rb +128 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +7 -7
- data/lib/active_support/time_with_zone.rb +15 -5
- data/lib/active_support/values/time_zone.rb +12 -7
- data/lib/active_support/xml_mini.rb +2 -9
- data/lib/active_support/xml_mini/jdom.rb +2 -2
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +2 -2
- metadata +30 -8
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -16,7 +16,7 @@ module ActiveSupport
|
|
16
16
|
# oh.keys # => [:a, :b], this order is guaranteed
|
17
17
|
#
|
18
18
|
# Also, maps the +omap+ feature for YAML files
|
19
|
-
# (See
|
19
|
+
# (See https://yaml.org/type/omap.html) to support ordered items
|
20
20
|
# when loading from yaml.
|
21
21
|
#
|
22
22
|
# <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/duplicable"
|
4
|
+
require "active_support/core_ext/array/extract"
|
5
|
+
|
6
|
+
module ActiveSupport
|
7
|
+
# +ParameterFilter+ allows you to specify keys for sensitive data from
|
8
|
+
# hash-like object and replace corresponding value. Filtering only certain
|
9
|
+
# sub-keys from a hash is possible by using the dot notation:
|
10
|
+
# 'credit_card.number'. If a proc is given, each key and value of a hash and
|
11
|
+
# all sub-hashes are passed to it, where the value or the key can be replaced
|
12
|
+
# using String#replace or similar methods.
|
13
|
+
#
|
14
|
+
# ActiveSupport::ParameterFilter.new([:password])
|
15
|
+
# => replaces the value to all keys matching /password/i with "[FILTERED]"
|
16
|
+
#
|
17
|
+
# ActiveSupport::ParameterFilter.new([:foo, "bar"])
|
18
|
+
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
|
19
|
+
#
|
20
|
+
# ActiveSupport::ParameterFilter.new(["credit_card.code"])
|
21
|
+
# => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
|
22
|
+
# change { file: { code: "xxxx"} }
|
23
|
+
#
|
24
|
+
# ActiveSupport::ParameterFilter.new([-> (k, v) do
|
25
|
+
# v.reverse! if k =~ /secret/i
|
26
|
+
# end])
|
27
|
+
# => reverses the value to all keys matching /secret/i
|
28
|
+
class ParameterFilter
|
29
|
+
FILTERED = "[FILTERED]" # :nodoc:
|
30
|
+
|
31
|
+
# Create instance with given filters. Supported type of filters are +String+, +Regexp+, and +Proc+.
|
32
|
+
# Other types of filters are treated as +String+ using +to_s+.
|
33
|
+
# For +Proc+ filters, key, value, and optional original hash is passed to block arguments.
|
34
|
+
#
|
35
|
+
# ==== Options
|
36
|
+
#
|
37
|
+
# * <tt>:mask</tt> - A replaced object when filtered. Defaults to +"[FILTERED]"+
|
38
|
+
def initialize(filters = [], mask: FILTERED)
|
39
|
+
@filters = filters
|
40
|
+
@mask = mask
|
41
|
+
end
|
42
|
+
|
43
|
+
# Mask value of +params+ if key matches one of filters.
|
44
|
+
def filter(params)
|
45
|
+
compiled_filter.call(params)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns filtered value for given key. For +Proc+ filters, third block argument is not populated.
|
49
|
+
def filter_param(key, value)
|
50
|
+
@filters.empty? ? value : compiled_filter.value_for_key(key, value)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def compiled_filter
|
56
|
+
@compiled_filter ||= CompiledFilter.compile(@filters, mask: @mask)
|
57
|
+
end
|
58
|
+
|
59
|
+
class CompiledFilter # :nodoc:
|
60
|
+
def self.compile(filters, mask:)
|
61
|
+
return lambda { |params| params.dup } if filters.empty?
|
62
|
+
|
63
|
+
strings, regexps, blocks = [], [], []
|
64
|
+
|
65
|
+
filters.each do |item|
|
66
|
+
case item
|
67
|
+
when Proc
|
68
|
+
blocks << item
|
69
|
+
when Regexp
|
70
|
+
regexps << item
|
71
|
+
else
|
72
|
+
strings << Regexp.escape(item.to_s)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
deep_regexps = regexps.extract! { |r| r.to_s.include?("\\.") }
|
77
|
+
deep_strings = strings.extract! { |s| s.include?("\\.") }
|
78
|
+
|
79
|
+
regexps << Regexp.new(strings.join("|"), true) unless strings.empty?
|
80
|
+
deep_regexps << Regexp.new(deep_strings.join("|"), true) unless deep_strings.empty?
|
81
|
+
|
82
|
+
new regexps, deep_regexps, blocks, mask: mask
|
83
|
+
end
|
84
|
+
|
85
|
+
attr_reader :regexps, :deep_regexps, :blocks
|
86
|
+
|
87
|
+
def initialize(regexps, deep_regexps, blocks, mask:)
|
88
|
+
@regexps = regexps
|
89
|
+
@deep_regexps = deep_regexps.any? ? deep_regexps : nil
|
90
|
+
@blocks = blocks
|
91
|
+
@mask = mask
|
92
|
+
end
|
93
|
+
|
94
|
+
def call(params, parents = [], original_params = params)
|
95
|
+
filtered_params = params.class.new
|
96
|
+
|
97
|
+
params.each do |key, value|
|
98
|
+
filtered_params[key] = value_for_key(key, value, parents, original_params)
|
99
|
+
end
|
100
|
+
|
101
|
+
filtered_params
|
102
|
+
end
|
103
|
+
|
104
|
+
def value_for_key(key, value, parents = [], original_params = nil)
|
105
|
+
parents.push(key) if deep_regexps
|
106
|
+
if regexps.any? { |r| r.match?(key) }
|
107
|
+
value = @mask
|
108
|
+
elsif deep_regexps && (joined = parents.join(".")) && deep_regexps.any? { |r| r.match?(joined) }
|
109
|
+
value = @mask
|
110
|
+
elsif value.is_a?(Hash)
|
111
|
+
value = call(value, parents, original_params)
|
112
|
+
elsif value.is_a?(Array)
|
113
|
+
# If we don't pop the current parent it will be duplicated as we
|
114
|
+
# process each array value.
|
115
|
+
parents.pop if deep_regexps
|
116
|
+
value = value.map { |v| value_for_key(key, v, parents, original_params) }
|
117
|
+
# Restore the parent stack after processing the array.
|
118
|
+
parents.push(key) if deep_regexps
|
119
|
+
elsif blocks.any?
|
120
|
+
key = key.dup if key.duplicable?
|
121
|
+
value = value.dup if value.duplicable?
|
122
|
+
blocks.each { |b| b.arity == 2 ? b.call(key, value) : b.call(key, value, original_params) }
|
123
|
+
end
|
124
|
+
parents.pop if deep_regexps
|
125
|
+
value
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/active_support/rails.rb
CHANGED
@@ -27,9 +27,3 @@ require "active_support/core_ext/module/delegation"
|
|
27
27
|
|
28
28
|
# Defines ActiveSupport::Deprecation.
|
29
29
|
require "active_support/deprecation"
|
30
|
-
|
31
|
-
# Defines Regexp#match?.
|
32
|
-
#
|
33
|
-
# This should be removed when Rails needs Ruby 2.4 or later, and the require
|
34
|
-
# added where other Regexp extensions are being used (easy to grep).
|
35
|
-
require "active_support/core_ext/regexp"
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/execution_wrapper"
|
4
|
+
require "active_support/executor"
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
7
|
#--
|
@@ -49,11 +50,9 @@ module ActiveSupport
|
|
49
50
|
def self.reload!
|
50
51
|
executor.wrap do
|
51
52
|
new.tap do |instance|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
instance.complete!
|
56
|
-
end
|
53
|
+
instance.run!
|
54
|
+
ensure
|
55
|
+
instance.complete!
|
57
56
|
end
|
58
57
|
end
|
59
58
|
prepare!
|
@@ -24,7 +24,7 @@ module ActiveSupport
|
|
24
24
|
# The values are first processed by SHA256, so that we don't leak length info
|
25
25
|
# via timing attacks.
|
26
26
|
def secure_compare(a, b)
|
27
|
-
fixed_length_secure_compare(::Digest::SHA256.
|
27
|
+
fixed_length_secure_compare(::Digest::SHA256.digest(a), ::Digest::SHA256.digest(b)) && a == b
|
28
28
|
end
|
29
29
|
module_function :secure_compare
|
30
30
|
end
|
@@ -24,6 +24,10 @@ module ActiveSupport
|
|
24
24
|
# After configured, whenever a "sql.active_record" notification is published,
|
25
25
|
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
|
26
26
|
# the +sql+ method.
|
27
|
+
#
|
28
|
+
# We can detach a subscriber as well:
|
29
|
+
#
|
30
|
+
# ActiveRecord::StatsSubscriber.detach_from(:active_record)
|
27
31
|
class Subscriber
|
28
32
|
class << self
|
29
33
|
# Attach the subscriber to a namespace.
|
@@ -40,6 +44,25 @@ module ActiveSupport
|
|
40
44
|
end
|
41
45
|
end
|
42
46
|
|
47
|
+
# Detach the subscriber from a namespace.
|
48
|
+
def detach_from(namespace, notifier = ActiveSupport::Notifications)
|
49
|
+
@namespace = namespace
|
50
|
+
@subscriber = find_attached_subscriber
|
51
|
+
@notifier = notifier
|
52
|
+
|
53
|
+
return unless subscriber
|
54
|
+
|
55
|
+
subscribers.delete(subscriber)
|
56
|
+
|
57
|
+
# Remove event subscribers of all existing methods on the class.
|
58
|
+
subscriber.public_methods(false).each do |event|
|
59
|
+
remove_event_subscriber(event)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Reset notifier so that event subscribers will not add for new methods added to the class.
|
63
|
+
@notifier = nil
|
64
|
+
end
|
65
|
+
|
43
66
|
# Adds event subscribers for all new methods added to the class.
|
44
67
|
def method_added(event)
|
45
68
|
# Only public methods are added as subscribers, and only if a notifier
|
@@ -54,62 +77,78 @@ module ActiveSupport
|
|
54
77
|
@@subscribers ||= []
|
55
78
|
end
|
56
79
|
|
57
|
-
|
58
|
-
|
59
|
-
protected
|
80
|
+
private
|
81
|
+
attr_reader :subscriber, :notifier, :namespace
|
60
82
|
|
61
|
-
|
83
|
+
def add_event_subscriber(event) # :doc:
|
84
|
+
return if invalid_event?(event.to_s)
|
62
85
|
|
63
|
-
|
86
|
+
pattern = prepare_pattern(event)
|
87
|
+
|
88
|
+
# Don't add multiple subscribers (eg. if methods are redefined).
|
89
|
+
return if pattern_subscribed?(pattern)
|
64
90
|
|
65
|
-
|
66
|
-
|
91
|
+
subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
|
92
|
+
end
|
67
93
|
|
68
|
-
|
94
|
+
def remove_event_subscriber(event) # :doc:
|
95
|
+
return if invalid_event?(event.to_s)
|
69
96
|
|
70
|
-
|
71
|
-
return if subscriber.patterns.include?(pattern)
|
97
|
+
pattern = prepare_pattern(event)
|
72
98
|
|
73
|
-
|
74
|
-
|
75
|
-
|
99
|
+
return unless pattern_subscribed?(pattern)
|
100
|
+
|
101
|
+
notifier.unsubscribe(subscriber.patterns[pattern])
|
102
|
+
subscriber.patterns.delete(pattern)
|
103
|
+
end
|
104
|
+
|
105
|
+
def find_attached_subscriber
|
106
|
+
subscribers.find { |attached_subscriber| attached_subscriber.instance_of?(self) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def invalid_event?(event)
|
110
|
+
%w{ start finish }.include?(event.to_s)
|
111
|
+
end
|
112
|
+
|
113
|
+
def prepare_pattern(event)
|
114
|
+
"#{event}.#{namespace}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def pattern_subscribed?(pattern)
|
118
|
+
subscriber.patterns.key?(pattern)
|
119
|
+
end
|
76
120
|
end
|
77
121
|
|
78
122
|
attr_reader :patterns # :nodoc:
|
79
123
|
|
80
124
|
def initialize
|
81
125
|
@queue_key = [self.class.name, object_id].join "-"
|
82
|
-
@patterns =
|
126
|
+
@patterns = {}
|
83
127
|
super
|
84
128
|
end
|
85
129
|
|
86
130
|
def start(name, id, payload)
|
87
|
-
|
131
|
+
event = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload)
|
132
|
+
event.start!
|
88
133
|
parent = event_stack.last
|
89
|
-
parent <<
|
134
|
+
parent << event if parent
|
90
135
|
|
91
|
-
event_stack.push
|
136
|
+
event_stack.push event
|
92
137
|
end
|
93
138
|
|
94
139
|
def finish(name, id, payload)
|
95
|
-
|
96
|
-
event
|
97
|
-
event.end = finished
|
140
|
+
event = event_stack.pop
|
141
|
+
event.finish!
|
98
142
|
event.payload.merge!(payload)
|
99
143
|
|
100
|
-
method = name.split("."
|
144
|
+
method = name.split(".").first
|
101
145
|
send(method, event)
|
102
146
|
end
|
103
147
|
|
104
148
|
private
|
105
|
-
|
106
149
|
def event_stack
|
107
150
|
SubscriberQueueRegistry.instance.get_queue(@queue_key)
|
108
151
|
end
|
109
|
-
|
110
|
-
def now
|
111
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
112
|
-
end
|
113
152
|
end
|
114
153
|
|
115
154
|
# This is a registry for all the event stacks kept for subscribers.
|
@@ -46,21 +46,30 @@ module ActiveSupport
|
|
46
46
|
|
47
47
|
def current_tags
|
48
48
|
# We use our object ID here to avoid conflicting with other instances
|
49
|
-
thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
|
49
|
+
thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
|
50
50
|
Thread.current[thread_key] ||= []
|
51
51
|
end
|
52
52
|
|
53
53
|
def tags_text
|
54
54
|
tags = current_tags
|
55
|
-
if tags.
|
55
|
+
if tags.one?
|
56
|
+
"[#{tags[0]}] "
|
57
|
+
elsif tags.any?
|
56
58
|
tags.collect { |tag| "[#{tag}] " }.join
|
57
59
|
end
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
61
63
|
def self.new(logger)
|
62
|
-
|
63
|
-
|
64
|
+
logger = logger.dup
|
65
|
+
|
66
|
+
if logger.formatter
|
67
|
+
logger.formatter = logger.formatter.dup
|
68
|
+
else
|
69
|
+
# Ensure we set a default formatter so we aren't extending nil!
|
70
|
+
logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
|
71
|
+
end
|
72
|
+
|
64
73
|
logger.formatter.extend Formatter
|
65
74
|
logger.extend(self)
|
66
75
|
end
|
@@ -11,6 +11,8 @@ require "active_support/testing/isolation"
|
|
11
11
|
require "active_support/testing/constant_lookup"
|
12
12
|
require "active_support/testing/time_helpers"
|
13
13
|
require "active_support/testing/file_fixtures"
|
14
|
+
require "active_support/testing/parallelization"
|
15
|
+
require "concurrent/utility/processor_counter"
|
14
16
|
|
15
17
|
module ActiveSupport
|
16
18
|
class TestCase < ::Minitest::Test
|
@@ -39,6 +41,95 @@ module ActiveSupport
|
|
39
41
|
def test_order
|
40
42
|
ActiveSupport.test_order ||= :random
|
41
43
|
end
|
44
|
+
|
45
|
+
# Parallelizes the test suite.
|
46
|
+
#
|
47
|
+
# Takes a +workers+ argument that controls how many times the process
|
48
|
+
# is forked. For each process a new database will be created suffixed
|
49
|
+
# with the worker number.
|
50
|
+
#
|
51
|
+
# test-database-0
|
52
|
+
# test-database-1
|
53
|
+
#
|
54
|
+
# If <tt>ENV["PARALLEL_WORKERS"]</tt> is set the workers argument will be ignored
|
55
|
+
# and the environment variable will be used instead. This is useful for CI
|
56
|
+
# environments, or other environments where you may need more workers than
|
57
|
+
# you do for local testing.
|
58
|
+
#
|
59
|
+
# If the number of workers is set to +1+ or fewer, the tests will not be
|
60
|
+
# parallelized.
|
61
|
+
#
|
62
|
+
# If +workers+ is set to +:number_of_processors+, the number of workers will be
|
63
|
+
# set to the actual core count on the machine you are on.
|
64
|
+
#
|
65
|
+
# The default parallelization method is to fork processes. If you'd like to
|
66
|
+
# use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+
|
67
|
+
# method. Note the threaded parallelization does not create multiple
|
68
|
+
# database and will not work with system tests at this time.
|
69
|
+
#
|
70
|
+
# parallelize(workers: :number_of_processors, with: :threads)
|
71
|
+
#
|
72
|
+
# The threaded parallelization uses minitest's parallel executor directly.
|
73
|
+
# The processes parallelization uses a Ruby DRb server.
|
74
|
+
def parallelize(workers: :number_of_processors, with: :processes)
|
75
|
+
workers = Concurrent.physical_processor_count if workers == :number_of_processors
|
76
|
+
workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
|
77
|
+
|
78
|
+
return if workers <= 1
|
79
|
+
|
80
|
+
executor = case with
|
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!
|
94
|
+
end
|
95
|
+
|
96
|
+
# Set up hook for parallel testing. This can be used if you have multiple
|
97
|
+
# databases or any behavior that needs to be run after the process is forked
|
98
|
+
# but before the tests run.
|
99
|
+
#
|
100
|
+
# Note: this feature is not available with the threaded parallelization.
|
101
|
+
#
|
102
|
+
# In your +test_helper.rb+ add the following:
|
103
|
+
#
|
104
|
+
# class ActiveSupport::TestCase
|
105
|
+
# parallelize_setup do
|
106
|
+
# # create databases
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
def parallelize_setup(&block)
|
110
|
+
ActiveSupport::Testing::Parallelization.after_fork_hook do |worker|
|
111
|
+
yield worker
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Clean up hook for parallel testing. This can be used to drop databases
|
116
|
+
# if your app uses multiple write/read databases or other clean up before
|
117
|
+
# the tests finish. This runs before the forked process is closed.
|
118
|
+
#
|
119
|
+
# Note: this feature is not available with the threaded parallelization.
|
120
|
+
#
|
121
|
+
# In your +test_helper.rb+ add the following:
|
122
|
+
#
|
123
|
+
# class ActiveSupport::TestCase
|
124
|
+
# parallelize_teardown do
|
125
|
+
# # drop databases
|
126
|
+
# end
|
127
|
+
# end
|
128
|
+
def parallelize_teardown(&block)
|
129
|
+
ActiveSupport::Testing::Parallelization.run_cleanup_hook do |worker|
|
130
|
+
yield worker
|
131
|
+
end
|
132
|
+
end
|
42
133
|
end
|
43
134
|
|
44
135
|
alias_method :method_name, :name
|