activesupport 6.0.3.4 → 6.1.3

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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +371 -448
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support.rb +13 -1
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +3 -3
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache.rb +85 -44
  10. data/lib/active_support/cache/file_store.rb +4 -3
  11. data/lib/active_support/cache/mem_cache_store.rb +29 -18
  12. data/lib/active_support/cache/memory_store.rb +46 -26
  13. data/lib/active_support/cache/redis_cache_store.rb +27 -27
  14. data/lib/active_support/cache/strategy/local_cache.rb +21 -6
  15. data/lib/active_support/callbacks.rb +65 -56
  16. data/lib/active_support/concern.rb +46 -2
  17. data/lib/active_support/configurable.rb +3 -3
  18. data/lib/active_support/configuration_file.rb +46 -0
  19. data/lib/active_support/core_ext.rb +1 -1
  20. data/lib/active_support/core_ext/benchmark.rb +2 -2
  21. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  22. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  23. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  24. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  25. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  26. data/lib/active_support/core_ext/enumerable.rb +76 -4
  27. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  28. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  29. data/lib/active_support/core_ext/hash/except.rb +1 -1
  30. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  31. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  32. data/lib/active_support/core_ext/load_error.rb +1 -1
  33. data/lib/active_support/core_ext/marshal.rb +2 -0
  34. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  35. data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
  36. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
  37. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  38. data/lib/active_support/core_ext/module/delegation.rb +38 -28
  39. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  40. data/lib/active_support/core_ext/name_error.rb +29 -2
  41. data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
  42. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  43. data/lib/active_support/core_ext/object/json.rb +13 -2
  44. data/lib/active_support/core_ext/object/try.rb +2 -2
  45. data/lib/active_support/core_ext/range/compare_range.rb +9 -3
  46. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  47. data/lib/active_support/core_ext/regexp.rb +8 -1
  48. data/lib/active_support/core_ext/string/access.rb +5 -24
  49. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  50. data/lib/active_support/core_ext/string/inflections.rb +38 -4
  51. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  52. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  53. data/lib/active_support/core_ext/string/output_safety.rb +10 -10
  54. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  55. data/lib/active_support/core_ext/symbol.rb +3 -0
  56. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  57. data/lib/active_support/core_ext/time/calculations.rb +27 -3
  58. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  59. data/lib/active_support/core_ext/uri.rb +5 -1
  60. data/lib/active_support/current_attributes.rb +7 -2
  61. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  62. data/lib/active_support/dependencies.rb +43 -19
  63. data/lib/active_support/deprecation.rb +6 -1
  64. data/lib/active_support/deprecation/behaviors.rb +15 -2
  65. data/lib/active_support/deprecation/disallowed.rb +56 -0
  66. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  67. data/lib/active_support/deprecation/method_wrappers.rb +3 -2
  68. data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
  69. data/lib/active_support/deprecation/reporting.rb +50 -7
  70. data/lib/active_support/descendants_tracker.rb +6 -2
  71. data/lib/active_support/duration.rb +71 -22
  72. data/lib/active_support/duration/iso8601_serializer.rb +15 -9
  73. data/lib/active_support/encrypted_file.rb +19 -2
  74. data/lib/active_support/environment_inquirer.rb +20 -0
  75. data/lib/active_support/evented_file_update_checker.rb +69 -133
  76. data/lib/active_support/fork_tracker.rb +62 -0
  77. data/lib/active_support/gem_version.rb +2 -2
  78. data/lib/active_support/hash_with_indifferent_access.rb +43 -24
  79. data/lib/active_support/i18n_railtie.rb +14 -19
  80. data/lib/active_support/inflector/inflections.rb +1 -2
  81. data/lib/active_support/inflector/methods.rb +35 -31
  82. data/lib/active_support/inflector/transliterate.rb +4 -4
  83. data/lib/active_support/json/decoding.rb +4 -4
  84. data/lib/active_support/json/encoding.rb +5 -1
  85. data/lib/active_support/key_generator.rb +1 -1
  86. data/lib/active_support/locale/en.yml +7 -3
  87. data/lib/active_support/log_subscriber.rb +8 -0
  88. data/lib/active_support/logger.rb +1 -1
  89. data/lib/active_support/logger_silence.rb +2 -26
  90. data/lib/active_support/logger_thread_safe_level.rb +34 -12
  91. data/lib/active_support/message_encryptor.rb +4 -7
  92. data/lib/active_support/message_verifier.rb +5 -5
  93. data/lib/active_support/messages/metadata.rb +9 -1
  94. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  95. data/lib/active_support/messages/rotator.rb +6 -5
  96. data/lib/active_support/multibyte/chars.rb +4 -42
  97. data/lib/active_support/multibyte/unicode.rb +9 -83
  98. data/lib/active_support/notifications.rb +32 -5
  99. data/lib/active_support/notifications/fanout.rb +23 -8
  100. data/lib/active_support/notifications/instrumenter.rb +6 -15
  101. data/lib/active_support/number_helper.rb +29 -14
  102. data/lib/active_support/number_helper/number_converter.rb +1 -1
  103. data/lib/active_support/number_helper/number_to_currency_converter.rb +3 -7
  104. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  105. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  106. data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -3
  107. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  108. data/lib/active_support/option_merger.rb +3 -2
  109. data/lib/active_support/ordered_options.rb +8 -2
  110. data/lib/active_support/parameter_filter.rb +16 -11
  111. data/lib/active_support/per_thread_registry.rb +1 -1
  112. data/lib/active_support/rails.rb +1 -4
  113. data/lib/active_support/railtie.rb +23 -1
  114. data/lib/active_support/rescuable.rb +4 -4
  115. data/lib/active_support/secure_compare_rotator.rb +51 -0
  116. data/lib/active_support/security_utils.rb +19 -12
  117. data/lib/active_support/string_inquirer.rb +4 -2
  118. data/lib/active_support/subscriber.rb +12 -7
  119. data/lib/active_support/tagged_logging.rb +29 -4
  120. data/lib/active_support/testing/assertions.rb +18 -11
  121. data/lib/active_support/testing/parallelization.rb +12 -95
  122. data/lib/active_support/testing/parallelization/server.rb +78 -0
  123. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  124. data/lib/active_support/testing/time_helpers.rb +40 -3
  125. data/lib/active_support/time_with_zone.rb +67 -43
  126. data/lib/active_support/values/time_zone.rb +20 -10
  127. data/lib/active_support/xml_mini/rexml.rb +8 -1
  128. metadata +34 -36
  129. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  130. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  131. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  132. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  133. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  134. data/lib/active_support/core_ext/range/include_range.rb +0 -9
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This is private interface.
3
+ # This is a private interface.
4
4
  #
5
5
  # Rails components cherry pick from Active Support as needed, but there are a
6
6
  # few features that are used for sure in some way or another and it is not worth
@@ -13,9 +13,6 @@
13
13
  # Defines Object#blank? and Object#present?.
14
14
  require "active_support/core_ext/object/blank"
15
15
 
16
- # Rails own autoload, eager_load, etc.
17
- require "active_support/dependencies/autoload"
18
-
19
16
  # Support for ClassMethods and the included macro.
20
17
  require "active_support/concern"
21
18
 
@@ -22,12 +22,25 @@ module ActiveSupport
22
22
  app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all }
23
23
  app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all }
24
24
  app.executor.to_complete { ActiveSupport::CurrentAttributes.reset_all }
25
+
26
+ ActiveSupport.on_load(:active_support_test_case) do
27
+ require "active_support/current_attributes/test_helper"
28
+ include ActiveSupport::CurrentAttributes::TestHelper
29
+ end
25
30
  end
26
31
 
27
32
  initializer "active_support.deprecation_behavior" do |app|
28
33
  if deprecation = app.config.active_support.deprecation
29
34
  ActiveSupport::Deprecation.behavior = deprecation
30
35
  end
36
+
37
+ if disallowed_deprecation = app.config.active_support.disallowed_deprecation
38
+ ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation
39
+ end
40
+
41
+ if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings
42
+ ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings
43
+ end
31
44
  end
32
45
 
33
46
  # Sets the default value for Time.zone
@@ -65,15 +78,24 @@ module ActiveSupport
65
78
  initializer "active_support.set_configs" do |app|
66
79
  app.config.active_support.each do |k, v|
67
80
  k = "#{k}="
68
- ActiveSupport.send(k, v) if ActiveSupport.respond_to? k
81
+ ActiveSupport.public_send(k, v) if ActiveSupport.respond_to? k
69
82
  end
70
83
  end
71
84
 
72
85
  initializer "active_support.set_hash_digest_class" do |app|
73
86
  config.after_initialize do
74
87
  if app.config.active_support.use_sha1_digests
88
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
89
+ config.active_support.use_sha1_digests is deprecated and will
90
+ be removed from Rails 6.2. Use
91
+ config.active_support.hash_digest_class = ::Digest::SHA1 instead.
92
+ MSG
75
93
  ActiveSupport::Digest.hash_digest_class = ::Digest::SHA1
76
94
  end
95
+
96
+ if klass = app.config.active_support.hash_digest_class
97
+ ActiveSupport::Digest.hash_digest_class = klass
98
+ end
77
99
  end
78
100
  end
79
101
  end
@@ -14,12 +14,12 @@ module ActiveSupport
14
14
  end
15
15
 
16
16
  module ClassMethods
17
- # Rescue exceptions raised in controller actions.
17
+ # Registers exception classes with a handler to be called by <tt>rescue_with_handler</tt>.
18
18
  #
19
19
  # <tt>rescue_from</tt> receives a series of exception classes or class
20
- # names, and a trailing <tt>:with</tt> option with the name of a method
21
- # or a Proc object to be called to handle them. Alternatively a block can
22
- # be given.
20
+ # names, and an exception handler specified by a trailing <tt>:with</tt>
21
+ # option containing the name of a method or a Proc object. Alternatively, a block
22
+ # can be given as the handler.
23
23
  #
24
24
  # Handlers that take one argument will be called with the exception, so
25
25
  # that the exception can be inspected when dealing with it.
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/security_utils"
4
+ require "active_support/messages/rotator"
5
+
6
+ module ActiveSupport
7
+ # The ActiveSupport::SecureCompareRotator is a wrapper around +ActiveSupport::SecurityUtils.secure_compare+
8
+ # and allows you to rotate a previously defined value to a new one.
9
+ #
10
+ # It can be used as follow:
11
+ #
12
+ # rotator = ActiveSupport::SecureCompareRotator.new('new_production_value')
13
+ # rotator.rotate('previous_production_value')
14
+ # rotator.secure_compare!('previous_production_value')
15
+ #
16
+ # One real use case example would be to rotate a basic auth credentials:
17
+ #
18
+ # class MyController < ApplicationController
19
+ # def authenticate_request
20
+ # rotator = ActiveSupport::SecureComparerotator.new('new_password')
21
+ # rotator.rotate('old_password')
22
+ #
23
+ # authenticate_or_request_with_http_basic do |username, password|
24
+ # rotator.secure_compare!(password)
25
+ # rescue ActiveSupport::SecureCompareRotator::InvalidMatch
26
+ # false
27
+ # end
28
+ # end
29
+ # end
30
+ class SecureCompareRotator
31
+ include SecurityUtils
32
+ prepend Messages::Rotator
33
+
34
+ InvalidMatch = Class.new(StandardError)
35
+
36
+ def initialize(value, **_options)
37
+ @value = value
38
+ end
39
+
40
+ def secure_compare!(other_value, on_rotation: @on_rotation)
41
+ secure_compare(@value, other_value) ||
42
+ run_rotations(on_rotation) { |wrapper| wrapper.secure_compare!(other_value) } ||
43
+ raise(InvalidMatch)
44
+ end
45
+
46
+ private
47
+ def build_rotation(previous_value, _options)
48
+ self.class.new(previous_value)
49
+ end
50
+ end
51
+ end
@@ -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
- l = a.unpack "C#{a.bytesize}"
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
- res = 0
17
- b.each_byte { |byte| res |= byte ^ l.shift }
18
- res == 0
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
- # Constant time string comparison, for variable length strings.
27
+ # Secure string comparison for strings of variable length.
23
28
  #
24
- # The values are first processed by SHA256, so that we don't leak length info
25
- # via timing attacks.
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
- fixed_length_secure_compare(::Digest::SHA256.digest(a), ::Digest::SHA256.digest(b)) && a == b
34
+ a.length == b.length && fixed_length_secure_compare(a, b)
28
35
  end
29
36
  module_function :secure_compare
30
37
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/symbol/starts_ends_with"
4
+
3
5
  module ActiveSupport
4
6
  # Wrapping a string in this class gives you a prettier way to test
5
7
  # for equality. The value returned by <tt>Rails.env</tt> is wrapped
@@ -19,11 +21,11 @@ module ActiveSupport
19
21
  class StringInquirer < String
20
22
  private
21
23
  def respond_to_missing?(method_name, include_private = false)
22
- (method_name[-1] == "?") || super
24
+ method_name.end_with?("?") || super
23
25
  end
24
26
 
25
27
  def method_missing(method_name, *arguments)
26
- if method_name[-1] == "?"
28
+ if method_name.end_with?("?")
27
29
  self == method_name[0..-2]
28
30
  else
29
31
  super
@@ -31,15 +31,16 @@ module ActiveSupport
31
31
  class Subscriber
32
32
  class << self
33
33
  # Attach the subscriber to a namespace.
34
- def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications)
34
+ def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications, inherit_all: false)
35
35
  @namespace = namespace
36
36
  @subscriber = subscriber
37
37
  @notifier = notifier
38
+ @inherit_all = inherit_all
38
39
 
39
40
  subscribers << subscriber
40
41
 
41
42
  # Add event subscribers for all existing methods on the class.
42
- subscriber.public_methods(false).each do |event|
43
+ fetch_public_methods(subscriber, inherit_all).each do |event|
43
44
  add_event_subscriber(event)
44
45
  end
45
46
  end
@@ -55,7 +56,7 @@ module ActiveSupport
55
56
  subscribers.delete(subscriber)
56
57
 
57
58
  # Remove event subscribers of all existing methods on the class.
58
- subscriber.public_methods(false).each do |event|
59
+ fetch_public_methods(subscriber, true).each do |event|
59
60
  remove_event_subscriber(event)
60
61
  end
61
62
 
@@ -81,18 +82,18 @@ module ActiveSupport
81
82
  attr_reader :subscriber, :notifier, :namespace
82
83
 
83
84
  def add_event_subscriber(event) # :doc:
84
- return if invalid_event?(event.to_s)
85
+ return if invalid_event?(event)
85
86
 
86
87
  pattern = prepare_pattern(event)
87
88
 
88
- # Don't add multiple subscribers (eg. if methods are redefined).
89
+ # Don't add multiple subscribers (e.g. if methods are redefined).
89
90
  return if pattern_subscribed?(pattern)
90
91
 
91
92
  subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
92
93
  end
93
94
 
94
95
  def remove_event_subscriber(event) # :doc:
95
- return if invalid_event?(event.to_s)
96
+ return if invalid_event?(event)
96
97
 
97
98
  pattern = prepare_pattern(event)
98
99
 
@@ -107,7 +108,7 @@ module ActiveSupport
107
108
  end
108
109
 
109
110
  def invalid_event?(event)
110
- %w{ start finish }.include?(event.to_s)
111
+ %i{ start finish }.include?(event.to_sym)
111
112
  end
112
113
 
113
114
  def prepare_pattern(event)
@@ -117,6 +118,10 @@ module ActiveSupport
117
118
  def pattern_subscribed?(pattern)
118
119
  subscriber.patterns.key?(pattern)
119
120
  end
121
+
122
+ def fetch_public_methods(subscriber, inherit_all)
123
+ subscriber.public_methods(inherit_all) - Subscriber.public_instance_methods(true)
124
+ end
120
125
  end
121
126
 
122
127
  attr_reader :patterns # :nodoc:
@@ -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.reject(&:blank?).tap do |new_tags|
35
- current_tags.concat new_tags
36
- end
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)
@@ -60,6 +70,14 @@ 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
82
  logger = logger.dup
65
83
 
@@ -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
- formatter.tagged(*tags) { yield self }
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
@@ -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
- Hash[Array(expression).map { |e| [e, difference] }]
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 = yield
102
+ retval = assert_nothing_raised(&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 = yield
179
+ retval = assert_nothing_raised(&block)
176
180
 
177
181
  unless from == UNTRACKED
178
- error = "#{expression.inspect} isn't #{from.inspect}"
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
- assert before != after, error
192
+ assert_not_equal before, after, error
189
193
 
190
194
  unless to == UNTRACKED
191
- error = "#{expression.inspect} didn't change to as expected\n"
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
@@ -214,12 +216,17 @@ module ActiveSupport
214
216
  exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
215
217
 
216
218
  before = exp.call
217
- retval = yield
219
+ retval = assert_nothing_raised(&block)
218
220
  after = exp.call
219
221
 
220
- error = "#{expression.inspect} did change to #{after}"
222
+ error = "#{expression.inspect} changed"
221
223
  error = "#{message}.\n#{error}" if message
222
- assert before == after, error
224
+
225
+ if before.nil?
226
+ assert_nil after, error
227
+ else
228
+ assert_equal before, after, error
229
+ end
223
230
 
224
231
  retval
225
232
  end
@@ -3,37 +3,12 @@
3
3
  require "drb"
4
4
  require "drb/unix" unless Gem.win_platform?
5
5
  require "active_support/core_ext/module/attribute_accessors"
6
+ require "active_support/testing/parallelization/server"
7
+ require "active_support/testing/parallelization/worker"
6
8
 
7
9
  module ActiveSupport
8
10
  module Testing
9
11
  class Parallelization # :nodoc:
10
- class Server
11
- include DRb::DRbUndumped
12
-
13
- def initialize
14
- @queue = Queue.new
15
- end
16
-
17
- def record(reporter, result)
18
- raise DRb::DRbConnError if result.is_a?(DRb::DRbUnknown)
19
-
20
- reporter.synchronize do
21
- reporter.record(result)
22
- end
23
- end
24
-
25
- def <<(o)
26
- o[2] = DRbObject.new(o[2]) if o
27
- @queue << o
28
- end
29
-
30
- def length
31
- @queue.length
32
- end
33
-
34
- def pop; @queue.pop; end
35
- end
36
-
37
12
  @@after_fork_hooks = []
38
13
 
39
14
  def self.after_fork_hook(&blk)
@@ -50,85 +25,27 @@ module ActiveSupport
50
25
 
51
26
  cattr_reader :run_cleanup_hooks
52
27
 
53
- def initialize(queue_size)
54
- @queue_size = queue_size
55
- @queue = Server.new
56
- @pool = []
57
-
58
- @url = DRb.start_service("drbunix:", @queue).uri
59
- end
60
-
61
- def after_fork(worker)
62
- self.class.after_fork_hooks.each do |cb|
63
- cb.call(worker)
64
- end
65
- end
66
-
67
- def run_cleanup(worker)
68
- self.class.run_cleanup_hooks.each do |cb|
69
- cb.call(worker)
70
- end
28
+ def initialize(worker_count)
29
+ @worker_count = worker_count
30
+ @queue_server = Server.new
31
+ @worker_pool = []
32
+ @url = DRb.start_service("drbunix:", @queue_server).uri
71
33
  end
72
34
 
73
35
  def start
74
- @pool = @queue_size.times.map do |worker|
75
- fork do
76
- DRb.stop_service
77
-
78
- begin
79
- after_fork(worker)
80
- rescue => setup_exception; end
81
-
82
- queue = DRbObject.new_with_uri(@url)
83
-
84
- while job = queue.pop
85
- klass = job[0]
86
- method = job[1]
87
- reporter = job[2]
88
- result = klass.with_info_handler reporter do
89
- Minitest.run_one_method(klass, method)
90
- end
91
-
92
- add_setup_exception(result, setup_exception) if setup_exception
93
-
94
- begin
95
- queue.record(reporter, result)
96
- rescue DRb::DRbConnError
97
- result.failures.map! do |failure|
98
- if failure.respond_to?(:error)
99
- # minitest >5.14.0
100
- error = DRb::DRbRemoteError.new(failure.error)
101
- else
102
- error = DRb::DRbRemoteError.new(failure.exception)
103
- end
104
- Minitest::UnexpectedError.new(error)
105
- end
106
- queue.record(reporter, result)
107
- end
108
- end
109
- ensure
110
- run_cleanup(worker)
111
- end
36
+ @worker_pool = @worker_count.times.map do |worker|
37
+ Worker.new(worker, @url).start
112
38
  end
113
39
  end
114
40
 
115
41
  def <<(work)
116
- @queue << work
42
+ @queue_server << work
117
43
  end
118
44
 
119
45
  def shutdown
120
- @queue_size.times { @queue << nil }
121
- @pool.each { |pid| Process.waitpid pid }
122
-
123
- if @queue.length > 0
124
- raise "Queue not empty, but all workers have finished. This probably means that a worker crashed and #{@queue.length} tests were missed."
125
- end
46
+ @queue_server.shutdown
47
+ @worker_pool.each { |pid| Process.waitpid pid }
126
48
  end
127
-
128
- private
129
- def add_setup_exception(result, setup_exception)
130
- result.failures.prepend Minitest::UnexpectedError.new(setup_exception)
131
- end
132
49
  end
133
50
  end
134
51
  end