activesupport 6.1.5 → 7.0.0.alpha1

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.

Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +151 -584
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +0 -2
  6. data/lib/active_support/benchmarkable.rb +2 -2
  7. data/lib/active_support/cache/file_store.rb +15 -9
  8. data/lib/active_support/cache/mem_cache_store.rb +119 -28
  9. data/lib/active_support/cache/memory_store.rb +21 -13
  10. data/lib/active_support/cache/null_store.rb +10 -2
  11. data/lib/active_support/cache/redis_cache_store.rb +39 -59
  12. data/lib/active_support/cache/strategy/local_cache.rb +29 -49
  13. data/lib/active_support/cache.rb +189 -45
  14. data/lib/active_support/callbacks.rb +35 -31
  15. data/lib/active_support/concern.rb +5 -5
  16. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  17. data/lib/active_support/concurrency/share_lock.rb +2 -2
  18. data/lib/active_support/configurable.rb +6 -3
  19. data/lib/active_support/configuration_file.rb +1 -1
  20. data/lib/active_support/core_ext/array/access.rb +1 -5
  21. data/lib/active_support/core_ext/array/conversions.rb +6 -6
  22. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  23. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  24. data/lib/active_support/core_ext/date/blank.rb +1 -1
  25. data/lib/active_support/core_ext/date/calculations.rb +2 -2
  26. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  27. data/lib/active_support/core_ext/digest/uuid.rb +13 -13
  28. data/lib/active_support/core_ext/enumerable.rb +64 -12
  29. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  30. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  31. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  32. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  33. data/lib/active_support/core_ext/name_error.rb +2 -8
  34. data/lib/active_support/core_ext/numeric/conversions.rb +2 -2
  35. data/lib/active_support/core_ext/object/blank.rb +2 -2
  36. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  37. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  38. data/lib/active_support/core_ext/object/json.rb +29 -24
  39. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  40. data/lib/active_support/core_ext/object/try.rb +20 -20
  41. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  42. data/lib/active_support/core_ext/range/each.rb +1 -1
  43. data/lib/active_support/core_ext/range/include_time_with_zone.rb +1 -1
  44. data/lib/active_support/core_ext/string/filters.rb +1 -1
  45. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  46. data/lib/active_support/core_ext/string/output_safety.rb +60 -36
  47. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  48. data/lib/active_support/core_ext/time/calculations.rb +4 -5
  49. data/lib/active_support/core_ext/time/zones.rb +2 -17
  50. data/lib/active_support/core_ext/uri.rb +0 -14
  51. data/lib/active_support/current_attributes.rb +17 -1
  52. data/lib/active_support/dependencies/interlock.rb +10 -18
  53. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  54. data/lib/active_support/dependencies.rb +58 -788
  55. data/lib/active_support/deprecation/behaviors.rb +4 -1
  56. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  57. data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
  58. data/lib/active_support/deprecation.rb +1 -1
  59. data/lib/active_support/descendants_tracker.rb +12 -9
  60. data/lib/active_support/digest.rb +4 -4
  61. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  62. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  63. data/lib/active_support/duration.rb +80 -52
  64. data/lib/active_support/encrypted_configuration.rb +11 -1
  65. data/lib/active_support/encrypted_file.rb +1 -1
  66. data/lib/active_support/environment_inquirer.rb +1 -1
  67. data/lib/active_support/evented_file_update_checker.rb +1 -1
  68. data/lib/active_support/execution_wrapper.rb +13 -16
  69. data/lib/active_support/fork_tracker.rb +2 -4
  70. data/lib/active_support/gem_version.rb +4 -4
  71. data/lib/active_support/hash_with_indifferent_access.rb +3 -1
  72. data/lib/active_support/i18n.rb +1 -0
  73. data/lib/active_support/inflector/inflections.rb +11 -4
  74. data/lib/active_support/inflector/methods.rb +23 -47
  75. data/lib/active_support/json/encoding.rb +3 -3
  76. data/lib/active_support/key_generator.rb +18 -1
  77. data/lib/active_support/locale/en.yml +1 -1
  78. data/lib/active_support/log_subscriber.rb +13 -3
  79. data/lib/active_support/logger_thread_safe_level.rb +5 -13
  80. data/lib/active_support/message_encryptor.rb +3 -3
  81. data/lib/active_support/message_verifier.rb +4 -4
  82. data/lib/active_support/messages/metadata.rb +2 -2
  83. data/lib/active_support/multibyte/chars.rb +10 -11
  84. data/lib/active_support/multibyte.rb +1 -1
  85. data/lib/active_support/notifications/fanout.rb +31 -11
  86. data/lib/active_support/notifications/instrumenter.rb +17 -0
  87. data/lib/active_support/notifications.rb +10 -0
  88. data/lib/active_support/number_helper/number_converter.rb +1 -3
  89. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  90. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  91. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  92. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  93. data/lib/active_support/number_helper/rounding_helper.rb +1 -5
  94. data/lib/active_support/number_helper.rb +0 -2
  95. data/lib/active_support/option_merger.rb +4 -16
  96. data/lib/active_support/ordered_hash.rb +1 -1
  97. data/lib/active_support/parameter_filter.rb +5 -0
  98. data/lib/active_support/per_thread_registry.rb +1 -1
  99. data/lib/active_support/railtie.rb +33 -10
  100. data/lib/active_support/reloader.rb +1 -1
  101. data/lib/active_support/rescuable.rb +2 -2
  102. data/lib/active_support/secure_compare_rotator.rb +1 -1
  103. data/lib/active_support/string_inquirer.rb +0 -2
  104. data/lib/active_support/subscriber.rb +5 -0
  105. data/lib/active_support/test_case.rb +9 -21
  106. data/lib/active_support/testing/assertions.rb +34 -4
  107. data/lib/active_support/testing/deprecation.rb +1 -1
  108. data/lib/active_support/testing/isolation.rb +1 -1
  109. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  110. data/lib/active_support/testing/parallelization/server.rb +4 -0
  111. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  112. data/lib/active_support/testing/parallelization.rb +4 -0
  113. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  114. data/lib/active_support/testing/stream.rb +3 -5
  115. data/lib/active_support/testing/tagged_logging.rb +1 -1
  116. data/lib/active_support/testing/time_helpers.rb +13 -2
  117. data/lib/active_support/time_with_zone.rb +19 -6
  118. data/lib/active_support/values/time_zone.rb +25 -11
  119. data/lib/active_support/xml_mini/jdom.rb +1 -1
  120. data/lib/active_support/xml_mini/libxml.rb +5 -5
  121. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  122. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  123. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  124. data/lib/active_support/xml_mini/rexml.rb +1 -1
  125. data/lib/active_support/xml_mini.rb +2 -1
  126. data/lib/active_support.rb +14 -1
  127. metadata +11 -26
  128. data/lib/active_support/core_ext/marshal.rb +0 -26
  129. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -120
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/hash/deep_merge"
4
- require "active_support/core_ext/symbol/starts_ends_with"
5
4
 
6
5
  module ActiveSupport
7
- class OptionMerger #:nodoc:
6
+ class OptionMerger # :nodoc:
8
7
  instance_methods.each do |method|
9
8
  undef_method(method) unless method.start_with?("__", "instance_eval", "class", "object_id")
10
9
  end
@@ -25,20 +24,9 @@ module ActiveSupport
25
24
  options = @options
26
25
  end
27
26
 
28
- invoke_method(method, arguments, options, &block)
29
- end
30
-
31
- if RUBY_VERSION >= "2.7"
32
- def invoke_method(method, arguments, options, &block)
33
- if options
34
- @context.__send__(method, *arguments, **options, &block)
35
- else
36
- @context.__send__(method, *arguments, &block)
37
- end
38
- end
39
- else
40
- def invoke_method(method, arguments, options, &block)
41
- arguments << options.dup if options
27
+ if options
28
+ @context.__send__(method, *arguments, **options, &block)
29
+ else
42
30
  @context.__send__(method, *arguments, &block)
43
31
  end
44
32
  end
@@ -21,7 +21,7 @@ module ActiveSupport
21
21
  #
22
22
  # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts
23
23
  # with other implementations.
24
- class OrderedHash < ::Hash
24
+ class OrderedHash < ::Hash # :nodoc:
25
25
  def to_yaml_type
26
26
  "!tag:yaml.org,2002:omap"
27
27
  end
@@ -16,6 +16,11 @@ module ActiveSupport
16
16
  # ActiveSupport::ParameterFilter.new([:foo, "bar"])
17
17
  # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
18
18
  #
19
+ # ActiveSupport::ParameterFilter.new([/\Apin\z/i, /\Apin_/i])
20
+ # => replaces the value for the exact (case-insensitive) key 'pin' and all
21
+ # (case-insensitive) keys beginning with 'pin_', with "[FILTERED]".
22
+ # Does not match keys with 'pin' as a substring, such as 'shipping_id'.
23
+ #
19
24
  # ActiveSupport::ParameterFilter.new(["credit_card.code"])
20
25
  # => replaces { credit_card: {code: "xxxx"} } with "[FILTERED]", does not
21
26
  # change { file: { code: "xxxx"} }
@@ -56,6 +56,6 @@ module ActiveSupport
56
56
 
57
57
  send(name, *args, &block)
58
58
  end
59
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
59
+ ruby2_keywords(:method_missing)
60
60
  end
61
61
  end
@@ -9,6 +9,15 @@ module ActiveSupport
9
9
 
10
10
  config.eager_load_namespaces << ActiveSupport
11
11
 
12
+ initializer "active_support.remove_deprecated_time_with_zone_name" do |app|
13
+ config.after_initialize do
14
+ if app.config.active_support.remove_deprecated_time_with_zone_name
15
+ require "active_support/time_with_zone"
16
+ TimeWithZone.singleton_class.remove_method(:name)
17
+ end
18
+ end
19
+ end
20
+
12
21
  initializer "active_support.set_authenticated_message_encryption" do |app|
13
22
  config.after_initialize do
14
23
  unless app.config.active_support.use_authenticated_message_encryption.nil?
@@ -30,16 +39,22 @@ module ActiveSupport
30
39
  end
31
40
 
32
41
  initializer "active_support.deprecation_behavior" do |app|
33
- if deprecation = app.config.active_support.deprecation
34
- ActiveSupport::Deprecation.behavior = deprecation
35
- end
42
+ if app.config.active_support.report_deprecations == false
43
+ ActiveSupport::Deprecation.silenced = true
44
+ ActiveSupport::Deprecation.behavior = :silence
45
+ ActiveSupport::Deprecation.disallowed_behavior = :silence
46
+ else
47
+ if deprecation = app.config.active_support.deprecation
48
+ ActiveSupport::Deprecation.behavior = deprecation
49
+ end
36
50
 
37
- if disallowed_deprecation = app.config.active_support.disallowed_deprecation
38
- ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation
39
- end
51
+ if disallowed_deprecation = app.config.active_support.disallowed_deprecation
52
+ ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation
53
+ end
40
54
 
41
- if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings
42
- ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings
55
+ if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings
56
+ ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings
57
+ end
43
58
  end
44
59
  end
45
60
 
@@ -88,9 +103,9 @@ module ActiveSupport
88
103
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
89
104
  config.active_support.use_sha1_digests is deprecated and will
90
105
  be removed from Rails 7.0. Use
91
- config.active_support.hash_digest_class = ::Digest::SHA1 instead.
106
+ config.active_support.hash_digest_class = OpenSSL::Digest::SHA1 instead.
92
107
  MSG
93
- ActiveSupport::Digest.hash_digest_class = ::Digest::SHA1
108
+ ActiveSupport::Digest.hash_digest_class = OpenSSL::Digest::SHA1
94
109
  end
95
110
 
96
111
  if klass = app.config.active_support.hash_digest_class
@@ -98,5 +113,13 @@ module ActiveSupport
98
113
  end
99
114
  end
100
115
  end
116
+
117
+ initializer "active_support.set_key_generator_hash_digest_class" do |app|
118
+ config.after_initialize do
119
+ if klass = app.config.active_support.key_generator_hash_digest_class
120
+ ActiveSupport::KeyGenerator.hash_digest_class = klass
121
+ end
122
+ end
123
+ end
101
124
  end
102
125
  end
@@ -58,7 +58,7 @@ module ActiveSupport
58
58
  prepare!
59
59
  end
60
60
 
61
- def self.run!(reset: false) # :nodoc:
61
+ def self.run! # :nodoc:
62
62
  if check!
63
63
  super
64
64
  else
@@ -100,7 +100,7 @@ module ActiveSupport
100
100
  end
101
101
  end
102
102
 
103
- def handler_for_rescue(exception, object: self) #:nodoc:
103
+ def handler_for_rescue(exception, object: self) # :nodoc:
104
104
  case rescuer = find_rescue_handler(exception)
105
105
  when Symbol
106
106
  method = object.method(rescuer)
@@ -167,7 +167,7 @@ module ActiveSupport
167
167
 
168
168
  # Internal handler lookup. Delegates to class method. Some libraries call
169
169
  # this directly, so keeping it around for compatibility.
170
- def handler_for_rescue(exception) #:nodoc:
170
+ def handler_for_rescue(exception) # :nodoc:
171
171
  self.class.handler_for_rescue exception, object: self
172
172
  end
173
173
  end
@@ -17,7 +17,7 @@ module ActiveSupport
17
17
  #
18
18
  # class MyController < ApplicationController
19
19
  # def authenticate_request
20
- # rotator = ActiveSupport::SecureComparerotator.new('new_password')
20
+ # rotator = ActiveSupport::SecureCompareRotator.new('new_password')
21
21
  # rotator.rotate('old_password')
22
22
  #
23
23
  # authenticate_or_request_with_http_basic do |username, password|
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/symbol/starts_ends_with"
4
-
5
3
  module ActiveSupport
6
4
  # Wrapping a string in this class gives you a prettier way to test
7
5
  # for equality. The value returned by <tt>Rails.env</tt> is wrapped
@@ -150,6 +150,11 @@ module ActiveSupport
150
150
  send(method, event)
151
151
  end
152
152
 
153
+ def publish_event(event) # :nodoc:
154
+ method = event.name.split(".").first
155
+ send(method, event)
156
+ end
157
+
153
158
  private
154
159
  def event_stack
155
160
  SubscriberQueueRegistry.instance.get_queue(@queue_key)
@@ -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
- def parallelize(workers: :number_of_processors, with: :processes)
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
- 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!
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 do |worker|
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 do |worker|
130
- yield worker
131
- end
119
+ ActiveSupport::Testing::Parallelization.run_cleanup_hook(&block)
132
120
  end
133
121
  end
134
122
 
@@ -99,7 +99,7 @@ module ActiveSupport
99
99
  }
100
100
  before = exps.map(&:call)
101
101
 
102
- retval = assert_nothing_raised(&block)
102
+ retval = _assert_nothing_raised_or_warn("assert_difference", &block)
103
103
 
104
104
  expressions.zip(exps, before) do |(code, diff), exp, before_value|
105
105
  error = "#{code.inspect} didn't change by #{diff}"
@@ -176,7 +176,7 @@ module ActiveSupport
176
176
  exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
177
177
 
178
178
  before = exp.call
179
- retval = assert_nothing_raised(&block)
179
+ retval = _assert_nothing_raised_or_warn("assert_changes", &block)
180
180
 
181
181
  unless from == UNTRACKED
182
182
  error = "Expected change from #{from.inspect}"
@@ -207,16 +207,30 @@ module ActiveSupport
207
207
  # post :create, params: { status: { ok: true } }
208
208
  # end
209
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
+ #
210
217
  # An error message can be specified.
211
218
  #
212
219
  # assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
213
220
  # post :create, params: { status: { ok: false } }
214
221
  # end
215
- def assert_no_changes(expression, message = nil, &block)
222
+ def assert_no_changes(expression, message = nil, from: UNTRACKED, &block)
216
223
  exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
217
224
 
218
225
  before = exp.call
219
- retval = assert_nothing_raised(&block)
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
+
220
234
  after = exp.call
221
235
 
222
236
  error = "#{expression.inspect} changed"
@@ -230,6 +244,22 @@ module ActiveSupport
230
244
 
231
245
  retval
232
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
233
263
  end
234
264
  end
235
265
  end
@@ -4,7 +4,7 @@ require "active_support/deprecation"
4
4
 
5
5
  module ActiveSupport
6
6
  module Testing
7
- module Deprecation #:nodoc:
7
+ module Deprecation # :nodoc:
8
8
  def assert_deprecated(match = nil, deprecator = nil, &block)
9
9
  result, warnings = collect_deprecations(deprecator, &block)
10
10
  assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
@@ -5,7 +5,7 @@ module ActiveSupport
5
5
  module Isolation
6
6
  require "thread"
7
7
 
8
- def self.included(klass) #:nodoc:
8
+ def self.included(klass) # :nodoc:
9
9
  klass.class_eval do
10
10
  parallelize_me!
11
11
  end
@@ -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 }) { yield }
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? { |arg| arg.is_a?(Array) }
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) { yield }
29
+ object.stub(method_name, mock, &block)
30
30
 
31
31
  mock.verify
32
32
  end
@@ -49,6 +49,10 @@ module ActiveSupport
49
49
  @active_workers.size > 0
50
50
  end
51
51
 
52
+ def interrupt
53
+ @queue.clear
54
+ end
55
+
52
56
  def shutdown
53
57
  # Wait for initial queue to drain
54
58
  while @queue.length != 0
@@ -69,6 +69,9 @@ module ActiveSupport
69
69
  Minitest::UnexpectedError.new(error)
70
70
  end
71
71
  @queue.record(reporter, result)
72
+ rescue Interrupt
73
+ @queue.interrupt
74
+ raise
72
75
  end
73
76
 
74
77
  set_process_title("(idle)")
@@ -42,6 +42,10 @@ module ActiveSupport
42
42
  @queue_server << work
43
43
  end
44
44
 
45
+ def size
46
+ @worker_count
47
+ end
48
+
45
49
  def shutdown
46
50
  @queue_server.shutdown
47
51
  @worker_pool.each { |pid| Process.waitpid pid }
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module Testing
5
+ class ParallelizeExecutor # :nodoc:
6
+ attr_reader :size, :parallelize_with, :threshold
7
+
8
+ def initialize(size:, with:, threshold: ActiveSupport.test_parallelization_threshold)
9
+ @size = size
10
+ @parallelize_with = with
11
+ @threshold = threshold
12
+ end
13
+
14
+ def start
15
+ parallelize if should_parallelize?
16
+ show_execution_info
17
+
18
+ parallel_executor.start if parallelized?
19
+ end
20
+
21
+ def <<(work)
22
+ parallel_executor << work if parallelized?
23
+ end
24
+
25
+ def shutdown
26
+ parallel_executor.shutdown if parallelized?
27
+ end
28
+
29
+ private
30
+ def parallel_executor
31
+ @parallel_executor ||= build_parallel_executor
32
+ end
33
+
34
+ def build_parallel_executor
35
+ case parallelize_with
36
+ when :processes
37
+ Testing::Parallelization.new(size)
38
+ when :threads
39
+ ActiveSupport::TestCase.lock_threads = false if defined?(ActiveSupport::TestCase.lock_threads)
40
+ Minitest::Parallel::Executor.new(size)
41
+ else
42
+ raise ArgumentError, "#{parallelize_with} is not a supported parallelization executor."
43
+ end
44
+ end
45
+
46
+ def parallelize
47
+ @parallelized = true
48
+ Minitest::Test.parallelize_me!
49
+ end
50
+
51
+ def parallelized?
52
+ @parallelized if defined?(@parallelized)
53
+ end
54
+
55
+ def should_parallelize?
56
+ ENV["PARALLEL_WORKERS"] || tests_count > threshold
57
+ end
58
+
59
+ def tests_count
60
+ @tests_count ||= Minitest::Runnable.runnables.sum { |runnable| runnable.runnable_methods.size }
61
+ end
62
+
63
+ def show_execution_info
64
+ puts execution_info
65
+ end
66
+
67
+ def execution_info
68
+ if parallelized?
69
+ "Running #{tests_count} tests in parallel using #{parallel_executor.size} #{parallelize_with}"
70
+ else
71
+ "Running #{tests_count} tests in a single process (parallelization threshold is #{threshold})"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveSupport
4
4
  module Testing
5
- module Stream #:nodoc:
5
+ module Stream # :nodoc:
6
6
  private
7
7
  def silence_stream(stream)
8
8
  old_stream = stream.dup
@@ -14,11 +14,9 @@ module ActiveSupport
14
14
  old_stream.close
15
15
  end
16
16
 
17
- def quietly
17
+ def quietly(&block)
18
18
  silence_stream(STDOUT) do
19
- silence_stream(STDERR) do
20
- yield
21
- end
19
+ silence_stream(STDERR, &block)
22
20
  end
23
21
  end
24
22
 
@@ -4,7 +4,7 @@ module ActiveSupport
4
4
  module Testing
5
5
  # Logs a "PostsControllerTest: test name" heading before each test to
6
6
  # make test.log easier to search and follow along with.
7
- module TaggedLogging #:nodoc:
7
+ module TaggedLogging # :nodoc:
8
8
  attr_writer :tagged_logger
9
9
 
10
10
  def before_setup
@@ -126,7 +126,7 @@ module ActiveSupport
126
126
  # end
127
127
  # Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
128
128
  def travel_to(date_or_time)
129
- if block_given? && simple_stubs.stubbing(Time, :now)
129
+ if block_given? && in_block
130
130
  travel_to_nested_block_call = <<~MSG
131
131
 
132
132
  Calling `travel_to` with a block, when we have previously already made a call to `travel_to`, can lead to confusing time stubbing.
@@ -156,19 +156,28 @@ module ActiveSupport
156
156
 
157
157
  if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
158
158
  now = date_or_time.midnight.to_time
159
+ elsif date_or_time.is_a?(String)
160
+ now = Time.zone.parse(date_or_time)
159
161
  else
160
162
  now = date_or_time.to_time.change(usec: 0)
161
163
  end
162
164
 
165
+ stubbed_time = Time.now if simple_stubs.stubbing(Time, :now)
163
166
  simple_stubs.stub_object(Time, :now) { at(now.to_i) }
164
167
  simple_stubs.stub_object(Date, :today) { jd(now.to_date.jd) }
165
168
  simple_stubs.stub_object(DateTime, :now) { jd(now.to_date.jd, now.hour, now.min, now.sec, Rational(now.utc_offset, 86400)) }
166
169
 
167
170
  if block_given?
168
171
  begin
172
+ self.in_block = true
169
173
  yield
170
174
  ensure
171
- travel_back
175
+ if stubbed_time
176
+ travel_to stubbed_time
177
+ else
178
+ travel_back
179
+ end
180
+ self.in_block = false
172
181
  end
173
182
  end
174
183
  end
@@ -230,6 +239,8 @@ module ActiveSupport
230
239
  def simple_stubs
231
240
  @simple_stubs ||= SimpleStubs.new
232
241
  end
242
+
243
+ attr_accessor :in_block
233
244
  end
234
245
  end
235
246
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "yaml"
4
+
3
5
  require "active_support/duration"
4
6
  require "active_support/values/time_zone"
5
7
  require "active_support/core_ext/object/acts_like"
@@ -40,6 +42,13 @@ module ActiveSupport
40
42
  class TimeWithZone
41
43
  # Report class name as 'Time' to thwart type checking.
42
44
  def self.name
45
+ ActiveSupport::Deprecation.warn(<<~EOM)
46
+ ActiveSupport::TimeWithZone.name has been deprecated and
47
+ from Rails 7.1 will use the default Ruby implementation.
48
+ You can set `config.active_support.remove_deprecated_time_with_zone_name = true`
49
+ to enable the new behavior now.
50
+ EOM
51
+
43
52
  "Time"
44
53
  end
45
54
 
@@ -172,12 +181,11 @@ module ActiveSupport
172
181
  end
173
182
  end
174
183
 
175
- def init_with(coder) #:nodoc:
184
+ def init_with(coder) # :nodoc:
176
185
  initialize(coder["utc"], coder["zone"], coder["time"])
177
186
  end
178
187
 
179
- def encode_with(coder) #:nodoc:
180
- coder.tag = "!ruby/object:ActiveSupport::TimeWithZone"
188
+ def encode_with(coder) # :nodoc:
181
189
  coder.map = { "utc" => utc, "zone" => time_zone, "time" => time }
182
190
  end
183
191
 
@@ -440,7 +448,7 @@ module ActiveSupport
440
448
  [time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
441
449
  end
442
450
 
443
- # Returns the object's date and time as a floating point number of seconds
451
+ # Returns the object's date and time as a floating-point number of seconds
444
452
  # since the Epoch (January 1, 1970 00:00 UTC).
445
453
  #
446
454
  # Time.zone.now.to_f # => 1417709320.285418
@@ -534,7 +542,7 @@ module ActiveSupport
534
542
  def method_missing(sym, *args, &block)
535
543
  wrap_with_time_zone time.__send__(sym, *args, &block)
536
544
  rescue NoMethodError => e
537
- raise e, e.message.sub(time.inspect, inspect), e.backtrace
545
+ raise e, e.message.sub(time.inspect, inspect).sub("Time", "ActiveSupport::TimeWithZone"), e.backtrace
538
546
  end
539
547
 
540
548
  private
@@ -568,7 +576,7 @@ module ActiveSupport
568
576
  end
569
577
 
570
578
  def duration_of_variable_length?(obj)
571
- ActiveSupport::Duration === obj && obj.parts.any? { |p| [:years, :months, :weeks, :days].include?(p[0]) }
579
+ ActiveSupport::Duration === obj && obj.variable?
572
580
  end
573
581
 
574
582
  def wrap_with_time_zone(time)
@@ -583,3 +591,8 @@ module ActiveSupport
583
591
  end
584
592
  end
585
593
  end
594
+
595
+ # These prevent Psych from calling `ActiveSupport::TimeWithZone.name`
596
+ # and triggering the deprecation warning about the change in Rails 7.1.
597
+ YAML.load_tags["!ruby/object:ActiveSupport::TimeWithZone"] = "ActiveSupport::TimeWithZone"
598
+ YAML.dump_tags[ActiveSupport::TimeWithZone] = "!ruby/object:ActiveSupport::TimeWithZone"