activesupport 7.1.3.4 → 7.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +123 -1084
- data/lib/active_support/array_inquirer.rb +1 -1
- data/lib/active_support/backtrace_cleaner.rb +15 -3
- data/lib/active_support/broadcast_logger.rb +5 -4
- data/lib/active_support/cache/file_store.rb +15 -10
- data/lib/active_support/cache/mem_cache_store.rb +16 -74
- data/lib/active_support/cache/memory_store.rb +2 -1
- data/lib/active_support/cache/redis_cache_store.rb +16 -13
- data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
- data/lib/active_support/cache.rb +61 -68
- data/lib/active_support/callbacks.rb +74 -113
- data/lib/active_support/code_generator.rb +15 -10
- data/lib/active_support/core_ext/array/conversions.rb +0 -2
- data/lib/active_support/core_ext/class/subclasses.rb +15 -35
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +0 -2
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -4
- data/lib/active_support/core_ext/digest/uuid.rb +6 -0
- data/lib/active_support/core_ext/erb/util.rb +5 -0
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -148
- data/lib/active_support/core_ext/module/deprecation.rb +1 -4
- data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/duplicable.rb +24 -15
- data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
- data/lib/active_support/core_ext/object/json.rb +1 -1
- data/lib/active_support/core_ext/object/with.rb +5 -3
- data/lib/active_support/core_ext/pathname/blank.rb +4 -0
- data/lib/active_support/core_ext/range/overlap.rb +1 -1
- data/lib/active_support/core_ext/securerandom.rb +8 -24
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +0 -7
- data/lib/active_support/core_ext/time/calculations.rb +18 -28
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +0 -2
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +34 -40
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +0 -12
- data/lib/active_support/deprecation/constant_accessor.rb +47 -26
- data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
- data/lib/active_support/deprecation/reporting.rb +7 -2
- data/lib/active_support/deprecation.rb +8 -5
- data/lib/active_support/descendants_tracker.rb +9 -87
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -2
- data/lib/active_support/duration.rb +11 -6
- data/lib/active_support/error_reporter.rb +41 -3
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/execution_wrapper.rb +0 -1
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/fork_tracker.rb +2 -38
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +6 -8
- data/lib/active_support/html_safe_translation.rb +7 -4
- data/lib/active_support/json/encoding.rb +1 -1
- data/lib/active_support/log_subscriber.rb +1 -12
- data/lib/active_support/logger.rb +15 -2
- data/lib/active_support/logger_thread_safe_level.rb +0 -8
- data/lib/active_support/message_pack/extensions.rb +15 -2
- data/lib/active_support/message_verifier.rb +12 -0
- data/lib/active_support/messages/codec.rb +1 -1
- data/lib/active_support/multibyte/chars.rb +2 -2
- data/lib/active_support/notifications/fanout.rb +4 -7
- data/lib/active_support/notifications/instrumenter.rb +32 -21
- data/lib/active_support/notifications.rb +28 -27
- data/lib/active_support/number_helper/number_converter.rb +2 -2
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_options.rb +53 -15
- data/lib/active_support/proxy_object.rb +8 -5
- data/lib/active_support/railtie.rb +4 -11
- data/lib/active_support/string_inquirer.rb +1 -1
- data/lib/active_support/subscriber.rb +1 -0
- data/lib/active_support/syntax_error_proxy.rb +1 -11
- data/lib/active_support/tagged_logging.rb +4 -1
- data/lib/active_support/test_case.rb +3 -1
- data/lib/active_support/testing/assertions.rb +4 -4
- data/lib/active_support/testing/constant_stubbing.rb +30 -8
- data/lib/active_support/testing/deprecation.rb +5 -12
- data/lib/active_support/testing/isolation.rb +18 -8
- data/lib/active_support/testing/method_call_assertions.rb +2 -16
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/strict_warnings.rb +5 -4
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +3 -3
- data/lib/active_support/time_with_zone.rb +7 -3
- data/lib/active_support/values/time_zone.rb +10 -1
- data/lib/active_support/xml_mini.rb +11 -2
- data/lib/active_support.rb +3 -2
- metadata +35 -15
- data/lib/active_support/deprecation/instance_delegator.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
@@ -29,6 +29,16 @@ module ActiveSupport
|
|
29
29
|
# You can consume those events and the information they provide by registering
|
30
30
|
# a subscriber.
|
31
31
|
#
|
32
|
+
# ActiveSupport::Notifications.subscribe('render') do |event|
|
33
|
+
# event.name # => "render"
|
34
|
+
# event.duration # => 10 (in milliseconds)
|
35
|
+
# event.payload # => { extra: :information }
|
36
|
+
# event.allocations # => 1826 (objects)
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# +Event+ objects record CPU time and allocations. If you don't need this
|
40
|
+
# it's also possible to pass a block that accepts five arguments:
|
41
|
+
#
|
32
42
|
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
|
33
43
|
# name # => String, name of the event (such as 'render' from above)
|
34
44
|
# start # => Time, when the instrumented block started execution
|
@@ -42,20 +52,18 @@ module ActiveSupport
|
|
42
52
|
#
|
43
53
|
# ActiveSupport::Notifications.monotonic_subscribe('render') do |name, start, finish, id, payload|
|
44
54
|
# name # => String, name of the event (such as 'render' from above)
|
45
|
-
# start # =>
|
46
|
-
# finish # =>
|
55
|
+
# start # => Float, monotonic time when the instrumented block started execution
|
56
|
+
# finish # => Float, monotonic time when the instrumented block ended execution
|
47
57
|
# id # => String, unique ID for the instrumenter that fired the event
|
48
58
|
# payload # => Hash, the payload
|
49
59
|
# end
|
50
60
|
#
|
51
|
-
# The +start+ and +finish+ values above represent monotonic time.
|
52
|
-
#
|
53
61
|
# For instance, let's store all "render" events in an array:
|
54
62
|
#
|
55
63
|
# events = []
|
56
64
|
#
|
57
|
-
# ActiveSupport::Notifications.subscribe('render') do
|
58
|
-
# events <<
|
65
|
+
# ActiveSupport::Notifications.subscribe('render') do |event|
|
66
|
+
# events << event
|
59
67
|
# end
|
60
68
|
#
|
61
69
|
# That code returns right away, you are just subscribing to "render" events.
|
@@ -66,14 +74,10 @@ module ActiveSupport
|
|
66
74
|
# end
|
67
75
|
#
|
68
76
|
# event = events.first
|
69
|
-
# event.name
|
70
|
-
# event.duration
|
71
|
-
# event.payload
|
72
|
-
#
|
73
|
-
# The block in the <tt>subscribe</tt> call gets the name of the event, start
|
74
|
-
# timestamp, end timestamp, a string with a unique identifier for that event's instrumenter
|
75
|
-
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
|
76
|
-
# that order.
|
77
|
+
# event.name # => "render"
|
78
|
+
# event.duration # => 10 (in milliseconds)
|
79
|
+
# event.payload # => { extra: :information }
|
80
|
+
# event.allocations # => 1826 (objects)
|
77
81
|
#
|
78
82
|
# If an exception happens during that particular instrumentation the payload will
|
79
83
|
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
|
@@ -138,7 +142,7 @@ module ActiveSupport
|
|
138
142
|
# You can subscribe to some event temporarily while some block runs. For
|
139
143
|
# example, in
|
140
144
|
#
|
141
|
-
# callback = lambda {
|
145
|
+
# callback = lambda {|event| ... }
|
142
146
|
# ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
|
143
147
|
# ...
|
144
148
|
# end
|
@@ -161,7 +165,7 @@ module ActiveSupport
|
|
161
165
|
#
|
162
166
|
# The +subscribe+ method returns a subscriber object:
|
163
167
|
#
|
164
|
-
# subscriber = ActiveSupport::Notifications.subscribe("render") do
|
168
|
+
# subscriber = ActiveSupport::Notifications.subscribe("render") do |event|
|
165
169
|
# ...
|
166
170
|
# end
|
167
171
|
#
|
@@ -214,11 +218,15 @@ module ActiveSupport
|
|
214
218
|
# You can subscribe to events by passing a String to match exact event
|
215
219
|
# names, or by passing a Regexp to match all events that match a pattern.
|
216
220
|
#
|
217
|
-
#
|
218
|
-
#
|
221
|
+
# If the block passed to the method only takes one argument,
|
222
|
+
# it will yield an +Event+ object to the block:
|
223
|
+
#
|
224
|
+
# ActiveSupport::Notifications.subscribe(/render/) do |event|
|
225
|
+
# @event = event
|
219
226
|
# end
|
220
227
|
#
|
221
|
-
#
|
228
|
+
# Otherwise the +block+ will receive five arguments with information
|
229
|
+
# about the event:
|
222
230
|
#
|
223
231
|
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
|
224
232
|
# name # => String, name of the event (such as 'render' from above)
|
@@ -228,16 +236,9 @@ module ActiveSupport
|
|
228
236
|
# payload # => Hash, the payload
|
229
237
|
# end
|
230
238
|
#
|
231
|
-
# If the block passed to the method only takes one parameter,
|
232
|
-
# it will yield an event object to the block:
|
233
|
-
#
|
234
|
-
# ActiveSupport::Notifications.subscribe(/render/) do |event|
|
235
|
-
# @event = event
|
236
|
-
# end
|
237
|
-
#
|
238
239
|
# Raises an error if invalid event name type is passed:
|
239
240
|
#
|
240
|
-
# ActiveSupport::Notifications.subscribe(:render) {
|
241
|
+
# ActiveSupport::Notifications.subscribe(:render) {|event| ...}
|
241
242
|
# #=> ArgumentError (pattern must be specified as a String, Regexp or empty)
|
242
243
|
#
|
243
244
|
def subscribe(pattern = nil, callback = nil, &block)
|
@@ -164,11 +164,11 @@ module ActiveSupport
|
|
164
164
|
end
|
165
165
|
|
166
166
|
def translate_number_value_with_default(key, **i18n_options)
|
167
|
-
I18n.translate(key,
|
167
|
+
I18n.translate(key, default: default_value(key), scope: :number, **i18n_options)
|
168
168
|
end
|
169
169
|
|
170
170
|
def translate_in_locale(key, **i18n_options)
|
171
|
-
translate_number_value_with_default(key,
|
171
|
+
translate_number_value_with_default(key, locale: options[:locale], **i18n_options)
|
172
172
|
end
|
173
173
|
|
174
174
|
def default_value(key)
|
@@ -46,18 +46,14 @@ module ActiveSupport
|
|
46
46
|
super(key.to_sym, *identifiers)
|
47
47
|
end
|
48
48
|
|
49
|
-
def method_missing(
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
def method_missing(method, *args)
|
50
|
+
if method.end_with?("=")
|
51
|
+
self[method.name.chomp("=")] = args.first
|
52
|
+
elsif method.end_with?("!")
|
53
|
+
name_string = method.name.chomp("!")
|
54
|
+
self[name_string].presence || raise(KeyError.new(":#{name_string} is blank"))
|
53
55
|
else
|
54
|
-
|
55
|
-
|
56
|
-
if bangs
|
57
|
-
self[name_string].presence || raise(KeyError.new(":#{name_string} is blank"))
|
58
|
-
else
|
59
|
-
self[name_string]
|
60
|
-
end
|
56
|
+
self[method.name]
|
61
57
|
end
|
62
58
|
end
|
63
59
|
|
@@ -92,18 +88,60 @@ module ActiveSupport
|
|
92
88
|
# h.boy # => 'John'
|
93
89
|
class InheritableOptions < OrderedOptions
|
94
90
|
def initialize(parent = nil)
|
95
|
-
|
91
|
+
@parent = parent
|
92
|
+
if @parent.kind_of?(OrderedOptions)
|
96
93
|
# use the faster _get when dealing with OrderedOptions
|
97
|
-
super() { |h, k| parent._get(k) }
|
98
|
-
elsif parent
|
99
|
-
super() { |h, k| parent[k] }
|
94
|
+
super() { |h, k| @parent._get(k) }
|
95
|
+
elsif @parent
|
96
|
+
super() { |h, k| @parent[k] }
|
100
97
|
else
|
101
98
|
super()
|
99
|
+
@parent = {}
|
102
100
|
end
|
103
101
|
end
|
104
102
|
|
103
|
+
def to_h
|
104
|
+
@parent.merge(self)
|
105
|
+
end
|
106
|
+
|
107
|
+
def ==(other)
|
108
|
+
to_h == other.to_h
|
109
|
+
end
|
110
|
+
|
111
|
+
def inspect
|
112
|
+
"#<#{self.class.name} #{to_h.inspect}>"
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_s
|
116
|
+
to_h.to_s
|
117
|
+
end
|
118
|
+
|
119
|
+
def pretty_print(pp)
|
120
|
+
pp.pp_hash(to_h)
|
121
|
+
end
|
122
|
+
|
123
|
+
alias_method :own_key?, :key?
|
124
|
+
private :own_key?
|
125
|
+
|
126
|
+
def key?(key)
|
127
|
+
super || @parent.key?(key)
|
128
|
+
end
|
129
|
+
|
130
|
+
def overridden?(key)
|
131
|
+
!!(@parent && @parent.key?(key) && own_key?(key.to_sym))
|
132
|
+
end
|
133
|
+
|
105
134
|
def inheritable_copy
|
106
135
|
self.class.new(self)
|
107
136
|
end
|
137
|
+
|
138
|
+
def to_a
|
139
|
+
entries
|
140
|
+
end
|
141
|
+
|
142
|
+
def each(&block)
|
143
|
+
to_h.each(&block)
|
144
|
+
self
|
145
|
+
end
|
108
146
|
end
|
109
147
|
end
|
@@ -1,11 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
|
-
|
5
|
-
#
|
6
|
-
# A class with no predefined methods that behaves similarly to Builder's
|
7
|
-
# BlankSlate. Used for proxy classes.
|
8
|
-
class ProxyObject < ::BasicObject
|
4
|
+
class ProxyObject < ::BasicObject # :nodoc:
|
9
5
|
undef_method :==
|
10
6
|
undef_method :equal?
|
11
7
|
|
@@ -13,5 +9,12 @@ module ActiveSupport
|
|
13
9
|
def raise(*args)
|
14
10
|
::Object.send(:raise, *args)
|
15
11
|
end
|
12
|
+
|
13
|
+
def self.inherited(_subclass)
|
14
|
+
::ActiveSupport.deprecator.warn(<<~MSG)
|
15
|
+
ActiveSupport::ProxyObject is deprecated and will be removed in Rails 8.0.
|
16
|
+
Use Ruby's built-in BasicObject instead.
|
17
|
+
MSG
|
18
|
+
end
|
16
19
|
end
|
17
20
|
end
|
@@ -89,10 +89,11 @@ module ActiveSupport
|
|
89
89
|
begin
|
90
90
|
TZInfo::DataSource.get
|
91
91
|
rescue TZInfo::DataSourceNotFound => e
|
92
|
-
raise e.exception
|
92
|
+
raise e.exception('tzinfo-data is not present. Please add gem "tzinfo-data" to your Gemfile and run bundle install')
|
93
93
|
end
|
94
94
|
require "active_support/core_ext/time/zones"
|
95
95
|
Time.zone_default = Time.find_zone!(app.config.time_zone)
|
96
|
+
config.eager_load_namespaces << TZInfo
|
96
97
|
end
|
97
98
|
|
98
99
|
# Sets the default week start
|
@@ -117,16 +118,8 @@ module ActiveSupport
|
|
117
118
|
|
118
119
|
initializer "active_support.set_configs" do |app|
|
119
120
|
app.config.active_support.each do |k, v|
|
120
|
-
|
121
|
-
|
122
|
-
elsif k == "remove_deprecated_time_with_zone_name"
|
123
|
-
ActiveSupport.deprecator.warn("config.active_support.remove_deprecated_time_with_zone_name is deprecated and will be removed in Rails 7.2.")
|
124
|
-
elsif k == "use_rfc4122_namespaced_uuids"
|
125
|
-
ActiveSupport.deprecator.warn("config.active_support.use_rfc4122_namespaced_uuids is deprecated and will be removed in Rails 7.2.")
|
126
|
-
else
|
127
|
-
k = "#{k}="
|
128
|
-
ActiveSupport.public_send(k, v) if ActiveSupport.respond_to? k
|
129
|
-
end
|
121
|
+
k = "#{k}="
|
122
|
+
ActiveSupport.public_send(k, v) if ActiveSupport.respond_to? k
|
130
123
|
end
|
131
124
|
end
|
132
125
|
|
@@ -67,6 +67,7 @@ module ActiveSupport
|
|
67
67
|
|
68
68
|
# Adds event subscribers for all new methods added to the class.
|
69
69
|
def method_added(event)
|
70
|
+
super
|
70
71
|
# Only public methods are added as subscribers, and only if a notifier
|
71
72
|
# has been set up. This means that subscribers will only be set up for
|
72
73
|
# classes that call #attach_to.
|
@@ -45,7 +45,7 @@ module ActiveSupport
|
|
45
45
|
|
46
46
|
private
|
47
47
|
def parse_message_for_trace
|
48
|
-
if
|
48
|
+
if __getobj__.to_s.start_with?("(eval")
|
49
49
|
# If the exception is coming from a call to eval, we need to keep
|
50
50
|
# the path of the file in which eval was called to ensure we can
|
51
51
|
# return the right source fragment to show the location of the
|
@@ -56,15 +56,5 @@ module ActiveSupport
|
|
56
56
|
__getobj__.to_s.split("\n")
|
57
57
|
end
|
58
58
|
end
|
59
|
-
|
60
|
-
if SyntaxError.method_defined?(:path) # Ruby 3.3+
|
61
|
-
def source_location_eval?
|
62
|
-
__getobj__.path.start_with?("(eval")
|
63
|
-
end
|
64
|
-
else # 3.2 and older versions of Ruby
|
65
|
-
def source_location_eval?
|
66
|
-
__getobj__.to_s.start_with?("(eval")
|
67
|
-
end
|
68
|
-
end
|
69
59
|
end
|
70
60
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/module/delegation"
|
4
4
|
require "active_support/core_ext/object/blank"
|
5
|
-
require "logger"
|
6
5
|
require "active_support/logger"
|
7
6
|
|
8
7
|
module ActiveSupport
|
@@ -119,6 +118,10 @@ module ActiveSupport
|
|
119
118
|
|
120
119
|
if logger.formatter
|
121
120
|
logger.formatter = logger.formatter.clone
|
121
|
+
|
122
|
+
# Workaround for https://bugs.ruby-lang.org/issues/20250
|
123
|
+
# Can be removed when Ruby 3.4 is the least supported version.
|
124
|
+
logger.formatter.object_id if logger.formatter.is_a?(Proc)
|
122
125
|
else
|
123
126
|
# Ensure we set a default formatter so we aren't extending nil!
|
124
127
|
logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require "minitest"
|
4
4
|
require "active_support/testing/tagged_logging"
|
5
5
|
require "active_support/testing/setup_and_teardown"
|
6
|
+
require "active_support/testing/tests_without_assertions"
|
6
7
|
require "active_support/testing/assertions"
|
7
8
|
require "active_support/testing/error_reporter_assertions"
|
8
9
|
require "active_support/testing/deprecation"
|
@@ -78,7 +79,7 @@ module ActiveSupport
|
|
78
79
|
# number of tests to run is above the +threshold+ param. The default value is
|
79
80
|
# 50, and it's configurable via +config.active_support.test_parallelization_threshold+.
|
80
81
|
def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold)
|
81
|
-
workers = Concurrent.
|
82
|
+
workers = Concurrent.processor_count if workers == :number_of_processors
|
82
83
|
workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
|
83
84
|
|
84
85
|
Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
|
@@ -142,6 +143,7 @@ module ActiveSupport
|
|
142
143
|
|
143
144
|
include ActiveSupport::Testing::TaggedLogging
|
144
145
|
prepend ActiveSupport::Testing::SetupAndTeardown
|
146
|
+
prepend ActiveSupport::Testing::TestsWithoutAssertions
|
145
147
|
include ActiveSupport::Testing::Assertions
|
146
148
|
include ActiveSupport::Testing::ErrorReporterAssertions
|
147
149
|
include ActiveSupport::Testing::Deprecation
|
@@ -195,7 +195,7 @@ module ActiveSupport
|
|
195
195
|
retval = _assert_nothing_raised_or_warn("assert_changes", &block)
|
196
196
|
|
197
197
|
unless from == UNTRACKED
|
198
|
-
error = "Expected change from #{from.inspect}, got #{before}"
|
198
|
+
error = "Expected change from #{from.inspect}, got #{before.inspect}"
|
199
199
|
error = "#{message}.\n#{error}" if message
|
200
200
|
assert from === before, error
|
201
201
|
end
|
@@ -203,12 +203,12 @@ module ActiveSupport
|
|
203
203
|
after = exp.call
|
204
204
|
|
205
205
|
error = "#{expression.inspect} didn't change"
|
206
|
-
error = "#{error}. It was already #{to}" if before == to
|
206
|
+
error = "#{error}. It was already #{to.inspect}" if before == to
|
207
207
|
error = "#{message}.\n#{error}" if message
|
208
208
|
refute_equal before, after, error
|
209
209
|
|
210
210
|
unless to == UNTRACKED
|
211
|
-
error = "Expected change to #{to}, got #{after}\n"
|
211
|
+
error = "Expected change to #{to.inspect}, got #{after.inspect}\n"
|
212
212
|
error = "#{message}.\n#{error}" if message
|
213
213
|
assert to === after, error
|
214
214
|
end
|
@@ -242,7 +242,7 @@ module ActiveSupport
|
|
242
242
|
retval = _assert_nothing_raised_or_warn("assert_no_changes", &block)
|
243
243
|
|
244
244
|
unless from == UNTRACKED
|
245
|
-
error = "Expected initial value of #{from.inspect}"
|
245
|
+
error = "Expected initial value of #{from.inspect}, got #{before.inspect}"
|
246
246
|
error = "#{message}.\n#{error}" if message
|
247
247
|
assert from === before, error
|
248
248
|
end
|
@@ -15,17 +15,39 @@ module ActiveSupport
|
|
15
15
|
# Using this method rather than forcing <tt>World::List::Import::LARGE_IMPORT_THRESHOLD = 5000</tt> prevents
|
16
16
|
# warnings from being thrown, and ensures that the old value is returned after the test has completed.
|
17
17
|
#
|
18
|
+
# If the constant doesn't already exists, but you need it set for the duration of the block
|
19
|
+
# you can do so by passing `exists: false`.
|
20
|
+
#
|
21
|
+
# stub_const(object, :SOME_CONST, 1, exists: false) do
|
22
|
+
# assert_equal 1, SOME_CONST
|
23
|
+
# end
|
24
|
+
#
|
18
25
|
# Note: Stubbing a const will stub it across all threads. So if you have concurrent threads
|
19
26
|
# (like separate test suites running in parallel) that all depend on the same constant, it's possible
|
20
27
|
# divergent stubbing will trample on each other.
|
21
|
-
def stub_const(mod, constant, new_value)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
28
|
+
def stub_const(mod, constant, new_value, exists: true)
|
29
|
+
if exists
|
30
|
+
begin
|
31
|
+
old_value = mod.const_get(constant, false)
|
32
|
+
mod.send(:remove_const, constant)
|
33
|
+
mod.const_set(constant, new_value)
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
mod.send(:remove_const, constant)
|
37
|
+
mod.const_set(constant, old_value)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
if mod.const_defined?(constant)
|
41
|
+
raise NameError, "already defined constant #{constant} in #{mod.name}"
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
mod.const_set(constant, new_value)
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
mod.send(:remove_const, constant)
|
49
|
+
end
|
50
|
+
end
|
29
51
|
end
|
30
52
|
end
|
31
53
|
end
|
@@ -29,10 +29,11 @@ module ActiveSupport
|
|
29
29
|
# end
|
30
30
|
def assert_deprecated(match = nil, deprecator = nil, &block)
|
31
31
|
match, deprecator = nil, match if match.is_a?(ActiveSupport::Deprecation)
|
32
|
+
|
32
33
|
unless deprecator
|
33
|
-
|
34
|
-
deprecator = ActiveSupport::Deprecation._instance
|
34
|
+
raise ArgumentError, "No deprecator given"
|
35
35
|
end
|
36
|
+
|
36
37
|
result, warnings = collect_deprecations(deprecator, &block)
|
37
38
|
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
|
38
39
|
if match
|
@@ -51,11 +52,7 @@ module ActiveSupport
|
|
51
52
|
# assert_not_deprecated(ActiveSupport::Deprecation.new) do
|
52
53
|
# CustomDeprecator.warn "message" # passes assertion, different deprecator
|
53
54
|
# end
|
54
|
-
def assert_not_deprecated(deprecator
|
55
|
-
unless deprecator
|
56
|
-
ActiveSupport.deprecator.warn("assert_not_deprecated without a deprecator is deprecated")
|
57
|
-
deprecator = ActiveSupport::Deprecation._instance
|
58
|
-
end
|
55
|
+
def assert_not_deprecated(deprecator, &block)
|
59
56
|
result, deprecations = collect_deprecations(deprecator, &block)
|
60
57
|
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
|
61
58
|
result
|
@@ -69,11 +66,7 @@ module ActiveSupport
|
|
69
66
|
# ActiveSupport::Deprecation.new.warn "other message"
|
70
67
|
# :result
|
71
68
|
# end # => [:result, ["message"]]
|
72
|
-
def collect_deprecations(deprecator
|
73
|
-
unless deprecator
|
74
|
-
ActiveSupport.deprecator.warn("collect_deprecations without a deprecator is deprecated")
|
75
|
-
deprecator = ActiveSupport::Deprecation._instance
|
76
|
-
end
|
69
|
+
def collect_deprecations(deprecator)
|
77
70
|
old_behavior = deprecator.behavior
|
78
71
|
deprecations = []
|
79
72
|
deprecator.behavior = Proc.new do |message, callstack|
|
@@ -5,9 +5,11 @@ module ActiveSupport
|
|
5
5
|
module Isolation
|
6
6
|
require "thread"
|
7
7
|
|
8
|
+
SubprocessCrashed = Class.new(StandardError)
|
9
|
+
|
8
10
|
def self.included(klass) # :nodoc:
|
9
11
|
klass.class_eval do
|
10
|
-
parallelize_me!
|
12
|
+
parallelize_me! unless Minitest.parallel_executor.is_a?(ActiveSupport::Testing::ParallelizeExecutor)
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|
@@ -16,10 +18,17 @@ module ActiveSupport
|
|
16
18
|
end
|
17
19
|
|
18
20
|
def run
|
19
|
-
serialized = run_in_isolation do
|
21
|
+
status, serialized = run_in_isolation do
|
20
22
|
super
|
21
23
|
end
|
22
24
|
|
25
|
+
unless status&.success?
|
26
|
+
error = SubprocessCrashed.new("Subprocess exited with an error: #{status.inspect}\noutput: #{serialized.inspect}")
|
27
|
+
error.set_backtrace(caller)
|
28
|
+
self.failures << Minitest::UnexpectedError.new(error)
|
29
|
+
return defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
|
30
|
+
end
|
31
|
+
|
23
32
|
Marshal.load(serialized)
|
24
33
|
end
|
25
34
|
|
@@ -50,13 +59,13 @@ module ActiveSupport
|
|
50
59
|
end
|
51
60
|
|
52
61
|
write.puts [result].pack("m")
|
53
|
-
exit!
|
62
|
+
exit!(0)
|
54
63
|
end
|
55
64
|
|
56
65
|
write.close
|
57
66
|
result = read.read
|
58
|
-
Process.wait2(pid)
|
59
|
-
result.unpack1("m")
|
67
|
+
_, status = Process.wait2(pid)
|
68
|
+
return status, result.unpack1("m")
|
60
69
|
end
|
61
70
|
end
|
62
71
|
end
|
@@ -75,7 +84,7 @@ module ActiveSupport
|
|
75
84
|
File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
|
76
85
|
file.puts [Marshal.dump(test_result)].pack("m")
|
77
86
|
end
|
78
|
-
exit!
|
87
|
+
exit!(0)
|
79
88
|
else
|
80
89
|
Tempfile.open("isolation") do |tmpfile|
|
81
90
|
env = {
|
@@ -93,13 +102,14 @@ module ActiveSupport
|
|
93
102
|
|
94
103
|
child = IO.popen([env, Gem.ruby, *load_path_args, $0, *ORIG_ARGV, test_opts])
|
95
104
|
|
105
|
+
status = nil
|
96
106
|
begin
|
97
|
-
Process.
|
107
|
+
_, status = Process.wait2(child.pid)
|
98
108
|
rescue Errno::ECHILD # The child process may exit before we wait
|
99
109
|
nil
|
100
110
|
end
|
101
111
|
|
102
|
-
return tmpfile.read.unpack1("m")
|
112
|
+
return status, tmpfile.read.unpack1("m")
|
103
113
|
end
|
104
114
|
end
|
105
115
|
end
|
@@ -30,22 +30,8 @@ module ActiveSupport
|
|
30
30
|
assert_called(object, method_name, message, times: 0, &block)
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
# the Minitest 5.16 / Ruby 3.0 kwargs transition. It can go away
|
36
|
-
# when we drop support for Ruby 2.7.
|
37
|
-
if Minitest::Mock.instance_method(:expect).parameters.map(&:first).include?(:keyrest)
|
38
|
-
def expect_called_with(mock, args, returns: false, **kwargs)
|
39
|
-
mock.expect(:call, returns, args, **kwargs)
|
40
|
-
end
|
41
|
-
else
|
42
|
-
def expect_called_with(mock, args, returns: false, **kwargs)
|
43
|
-
if !kwargs.empty?
|
44
|
-
mock.expect(:call, returns, [*args, kwargs])
|
45
|
-
else
|
46
|
-
mock.expect(:call, returns, args)
|
47
|
-
end
|
48
|
-
end
|
33
|
+
def expect_called_with(mock, args, returns: false, **kwargs)
|
34
|
+
mock.expect(:call, returns, args, **kwargs)
|
49
35
|
end
|
50
36
|
|
51
37
|
def assert_called_on_instance_of(klass, method_name, message = nil, times: 1, returns: nil)
|
@@ -5,6 +5,8 @@ Warning[:deprecated] = true
|
|
5
5
|
|
6
6
|
module ActiveSupport
|
7
7
|
module RaiseWarnings # :nodoc:
|
8
|
+
class WarningError < StandardError; end
|
9
|
+
|
8
10
|
PROJECT_ROOT = File.expand_path("../../../../", __dir__)
|
9
11
|
ALLOWED_WARNINGS = Regexp.union(
|
10
12
|
/circular require considered harmful.*delayed_job/, # Bug in delayed job.
|
@@ -21,18 +23,17 @@ module ActiveSupport
|
|
21
23
|
%r{/lib/mail/parsers/.*assigned but unused variable - testEof}
|
22
24
|
)
|
23
25
|
|
24
|
-
def warn(message,
|
26
|
+
def warn(message, ...)
|
25
27
|
return if SUPPRESSED_WARNINGS.match?(message)
|
26
28
|
|
27
29
|
super
|
28
30
|
|
29
31
|
return unless message.include?(PROJECT_ROOT)
|
30
32
|
return if ALLOWED_WARNINGS.match?(message)
|
31
|
-
return unless ENV["RAILS_STRICT_WARNINGS"] || ENV["
|
33
|
+
return unless ENV["RAILS_STRICT_WARNINGS"] || ENV["BUILDKITE"]
|
32
34
|
|
33
|
-
raise message
|
35
|
+
raise WarningError.new(message)
|
34
36
|
end
|
35
|
-
ruby2_keywords :warn if respond_to?(:ruby2_keywords, true)
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
module Testing
|
5
|
+
# Warns when a test case does not perform any assertions.
|
6
|
+
#
|
7
|
+
# This is helpful in detecting broken tests that do not perform intended assertions.
|
8
|
+
module TestsWithoutAssertions # :nodoc:
|
9
|
+
def after_teardown
|
10
|
+
super
|
11
|
+
|
12
|
+
if assertions.zero? && !skipped? && !error?
|
13
|
+
file, line = method(name).source_location
|
14
|
+
warn "Test is missing assertions: `#{name}` #{file}:#{line}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|