activesupport 7.1.4.1 → 7.2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +127 -1129
  3. data/lib/active_support/array_inquirer.rb +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +10 -3
  5. data/lib/active_support/broadcast_logger.rb +18 -18
  6. data/lib/active_support/cache/file_store.rb +15 -10
  7. data/lib/active_support/cache/mem_cache_store.rb +16 -74
  8. data/lib/active_support/cache/memory_store.rb +2 -1
  9. data/lib/active_support/cache/redis_cache_store.rb +16 -13
  10. data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
  11. data/lib/active_support/cache.rb +60 -68
  12. data/lib/active_support/callbacks.rb +74 -113
  13. data/lib/active_support/core_ext/array/conversions.rb +0 -2
  14. data/lib/active_support/core_ext/class/subclasses.rb +15 -35
  15. data/lib/active_support/core_ext/date/blank.rb +4 -0
  16. data/lib/active_support/core_ext/date/conversions.rb +0 -2
  17. data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
  18. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  19. data/lib/active_support/core_ext/date_time/conversions.rb +0 -4
  20. data/lib/active_support/core_ext/digest/uuid.rb +6 -0
  21. data/lib/active_support/core_ext/erb/util.rb +5 -0
  22. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  23. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  24. data/lib/active_support/core_ext/module/delegation.rb +20 -163
  25. data/lib/active_support/core_ext/module/deprecation.rb +1 -4
  26. data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
  27. data/lib/active_support/core_ext/object/blank.rb +45 -1
  28. data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
  29. data/lib/active_support/core_ext/object/json.rb +6 -4
  30. data/lib/active_support/core_ext/object/with.rb +5 -3
  31. data/lib/active_support/core_ext/pathname/blank.rb +4 -0
  32. data/lib/active_support/core_ext/range/overlap.rb +1 -1
  33. data/lib/active_support/core_ext/securerandom.rb +8 -24
  34. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  35. data/lib/active_support/core_ext/string/filters.rb +1 -1
  36. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  37. data/lib/active_support/core_ext/string/output_safety.rb +0 -7
  38. data/lib/active_support/core_ext/time/calculations.rb +18 -28
  39. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  40. data/lib/active_support/core_ext/time/conversions.rb +0 -2
  41. data/lib/active_support/core_ext/time/zones.rb +1 -1
  42. data/lib/active_support/core_ext.rb +0 -1
  43. data/lib/active_support/current_attributes.rb +38 -40
  44. data/lib/active_support/delegation.rb +202 -0
  45. data/lib/active_support/dependencies/autoload.rb +0 -12
  46. data/lib/active_support/deprecation/constant_accessor.rb +47 -26
  47. data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
  48. data/lib/active_support/deprecation/reporting.rb +9 -4
  49. data/lib/active_support/deprecation.rb +8 -5
  50. data/lib/active_support/descendants_tracker.rb +9 -87
  51. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  52. data/lib/active_support/duration/iso8601_serializer.rb +1 -2
  53. data/lib/active_support/duration.rb +11 -6
  54. data/lib/active_support/encrypted_file.rb +1 -1
  55. data/lib/active_support/error_reporter.rb +41 -3
  56. data/lib/active_support/evented_file_update_checker.rb +0 -1
  57. data/lib/active_support/execution_wrapper.rb +0 -1
  58. data/lib/active_support/file_update_checker.rb +1 -1
  59. data/lib/active_support/fork_tracker.rb +2 -38
  60. data/lib/active_support/gem_version.rb +2 -2
  61. data/lib/active_support/hash_with_indifferent_access.rb +6 -8
  62. data/lib/active_support/html_safe_translation.rb +3 -0
  63. data/lib/active_support/log_subscriber.rb +0 -12
  64. data/lib/active_support/logger.rb +15 -2
  65. data/lib/active_support/logger_thread_safe_level.rb +0 -8
  66. data/lib/active_support/message_pack/extensions.rb +15 -2
  67. data/lib/active_support/message_verifier.rb +12 -0
  68. data/lib/active_support/multibyte/chars.rb +2 -2
  69. data/lib/active_support/notifications/fanout.rb +4 -7
  70. data/lib/active_support/notifications/instrumenter.rb +21 -18
  71. data/lib/active_support/notifications.rb +28 -27
  72. data/lib/active_support/number_helper/number_converter.rb +2 -2
  73. data/lib/active_support/option_merger.rb +2 -2
  74. data/lib/active_support/ordered_options.rb +53 -15
  75. data/lib/active_support/proxy_object.rb +8 -5
  76. data/lib/active_support/railtie.rb +4 -11
  77. data/lib/active_support/string_inquirer.rb +1 -1
  78. data/lib/active_support/subscriber.rb +1 -0
  79. data/lib/active_support/tagged_logging.rb +0 -1
  80. data/lib/active_support/test_case.rb +3 -1
  81. data/lib/active_support/testing/assertions.rb +4 -4
  82. data/lib/active_support/testing/constant_stubbing.rb +30 -8
  83. data/lib/active_support/testing/deprecation.rb +5 -12
  84. data/lib/active_support/testing/isolation.rb +20 -8
  85. data/lib/active_support/testing/method_call_assertions.rb +2 -16
  86. data/lib/active_support/testing/parallelization/server.rb +3 -0
  87. data/lib/active_support/testing/strict_warnings.rb +8 -4
  88. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  89. data/lib/active_support/testing/time_helpers.rb +3 -3
  90. data/lib/active_support/time_with_zone.rb +8 -4
  91. data/lib/active_support/values/time_zone.rb +7 -7
  92. data/lib/active_support/xml_mini.rb +11 -2
  93. data/lib/active_support.rb +2 -1
  94. metadata +49 -15
  95. data/lib/active_support/deprecation/instance_delegator.rb +0 -65
  96. data/lib/active_support/ruby_features.rb +0 -7
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "mutex_m"
4
3
  require "concurrent/map"
5
4
  require "set"
6
5
  require "active_support/core_ext/object/try"
@@ -49,15 +48,13 @@ module ActiveSupport
49
48
  #
50
49
  # This class is thread safe. All methods are reentrant.
51
50
  class Fanout
52
- include Mutex_m
53
-
54
51
  def initialize
52
+ @mutex = Mutex.new
55
53
  @string_subscribers = Concurrent::Map.new { |h, k| h.compute_if_absent(k) { [] } }
56
54
  @other_subscribers = []
57
55
  @all_listeners_for = Concurrent::Map.new
58
56
  @groups_for = Concurrent::Map.new
59
57
  @silenceable_groups_for = Concurrent::Map.new
60
- super
61
58
  end
62
59
 
63
60
  def inspect # :nodoc:
@@ -67,7 +64,7 @@ module ActiveSupport
67
64
 
68
65
  def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
69
66
  subscriber = Subscribers.new(pattern, callable || block, monotonic)
70
- synchronize do
67
+ @mutex.synchronize do
71
68
  case pattern
72
69
  when String
73
70
  @string_subscribers[pattern] << subscriber
@@ -83,7 +80,7 @@ module ActiveSupport
83
80
  end
84
81
 
85
82
  def unsubscribe(subscriber_or_name)
86
- synchronize do
83
+ @mutex.synchronize do
87
84
  case subscriber_or_name
88
85
  when String
89
86
  @string_subscribers[subscriber_or_name].clear
@@ -300,7 +297,7 @@ module ActiveSupport
300
297
 
301
298
  def all_listeners_for(name)
302
299
  # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
303
- @all_listeners_for[name] || synchronize do
300
+ @all_listeners_for[name] || @mutex.synchronize do
304
301
  # use synchronisation when accessing @subscribers
305
302
  @all_listeners_for[name] ||=
306
303
  @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
@@ -117,6 +117,8 @@ module ActiveSupport
117
117
  @cpu_time_finish = 0.0
118
118
  @allocation_count_start = 0
119
119
  @allocation_count_finish = 0
120
+ @gc_time_start = 0
121
+ @gc_time_finish = 0
120
122
  end
121
123
 
122
124
  def time
@@ -144,12 +146,14 @@ module ActiveSupport
144
146
  def start!
145
147
  @time = now
146
148
  @cpu_time_start = now_cpu
149
+ @gc_time_start = now_gc
147
150
  @allocation_count_start = now_allocations
148
151
  end
149
152
 
150
153
  # Record information at the time this event finishes
151
154
  def finish!
152
155
  @cpu_time_finish = now_cpu
156
+ @gc_time_finish = now_gc
153
157
  @end = now
154
158
  @allocation_count_finish = now_allocations
155
159
  end
@@ -173,28 +177,17 @@ module ActiveSupport
173
177
  @allocation_count_finish - @allocation_count_start
174
178
  end
175
179
 
176
- def children # :nodoc:
177
- ActiveSupport.deprecator.warn <<~EOM
178
- ActiveSupport::Notifications::Event#children is deprecated and will
179
- be removed in Rails 7.2.
180
- EOM
181
- []
182
- end
183
-
184
- def parent_of?(event) # :nodoc:
185
- ActiveSupport.deprecator.warn <<~EOM
186
- ActiveSupport::Notifications::Event#parent_of? is deprecated and will
187
- be removed in Rails 7.2.
188
- EOM
189
- start = (time - event.time) * 1000
190
- start <= 0 && (start + duration >= event.duration)
180
+ # Returns the time spent in GC (in milliseconds) between the call to #start!
181
+ # and the call to #finish!
182
+ def gc_time
183
+ (@gc_time_finish - @gc_time_start) / 1_000_000.0
191
184
  end
192
185
 
193
186
  # Returns the difference in milliseconds between when the execution of the
194
187
  # event started and when it ended.
195
188
  #
196
- # ActiveSupport::Notifications.subscribe('wait') do |*args|
197
- # @event = ActiveSupport::Notifications::Event.new(*args)
189
+ # ActiveSupport::Notifications.subscribe('wait') do |event|
190
+ # @event = event
198
191
  # end
199
192
  #
200
193
  # ActiveSupport::Notifications.instrument('wait') do
@@ -218,11 +211,21 @@ module ActiveSupport
218
211
  Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
219
212
  end
220
213
  rescue
221
- def now_cpu # rubocop:disable Lint/DuplicateMethods
214
+ def now_cpu
222
215
  0.0
223
216
  end
224
217
  end
225
218
 
219
+ if GC.respond_to?(:total_time)
220
+ def now_gc
221
+ GC.total_time
222
+ end
223
+ else
224
+ def now_gc
225
+ 0
226
+ end
227
+ end
228
+
226
229
  if GC.stat.key?(:total_allocated_objects)
227
230
  def now_allocations
228
231
  GC.stat(:total_allocated_objects)
@@ -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 # => Monotonic time, when the instrumented block started execution
46
- # finish # => Monotonic time, when the instrumented block ended execution
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 |*args|
58
- # events << ActiveSupport::Notifications::Event.new(*args)
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 # => "render"
70
- # event.duration # => 10 (in milliseconds)
71
- # event.payload # => { extra: :information }
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 {|*args| ... }
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 |*args|
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
- # ActiveSupport::Notifications.subscribe(/render/) do |*args|
218
- # @event = ActiveSupport::Notifications::Event.new(*args)
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
- # The +block+ will receive five parameters with information about the event:
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) {|*args| ...}
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, **{ default: default_value(key), scope: :number }.merge!(i18n_options))
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, **{ locale: options[:locale] }.merge(i18n_options))
171
+ translate_number_value_with_default(key, locale: options[:locale], **i18n_options)
172
172
  end
173
173
 
174
174
  def default_value(key)
@@ -31,8 +31,8 @@ module ActiveSupport
31
31
  end
32
32
  end
33
33
 
34
- def respond_to_missing?(*arguments)
35
- @context.respond_to?(*arguments)
34
+ def respond_to_missing?(...)
35
+ @context.respond_to?(...)
36
36
  end
37
37
  end
38
38
  end
@@ -46,18 +46,14 @@ module ActiveSupport
46
46
  super(key.to_sym, *identifiers)
47
47
  end
48
48
 
49
- def method_missing(name, *args)
50
- name_string = +name.to_s
51
- if name_string.chomp!("=")
52
- self[name_string] = args.first
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
- bangs = name_string.chomp!("!")
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
- if parent.kind_of?(OrderedOptions)
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
- # = Active Support Proxy \Object
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 "tzinfo-data is not present. Please add gem 'tzinfo-data' to your Gemfile and run bundle install"
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
- if k == :disable_to_s_conversion
121
- ActiveSupport.deprecator.warn("config.active_support.disable_to_s_conversion is deprecated and will be removed in Rails 7.2.")
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
 
@@ -24,7 +24,7 @@ module ActiveSupport
24
24
  method_name.end_with?("?") || super
25
25
  end
26
26
 
27
- def method_missing(method_name, *arguments)
27
+ def method_missing(method_name, ...)
28
28
  if method_name.end_with?("?")
29
29
  self == method_name[0..-2]
30
30
  else
@@ -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.
@@ -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
@@ -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.physical_processor_count if workers == :number_of_processors
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
- old_value = mod.const_get(constant, false)
23
- mod.send(:remove_const, constant)
24
- mod.const_set(constant, new_value)
25
- yield
26
- ensure
27
- mod.send(:remove_const, constant)
28
- mod.const_set(constant, old_value)
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
- ActiveSupport.deprecator.warn("assert_deprecated without a deprecator is deprecated")
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 = nil, &block)
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 = nil)
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|