activesupport 7.2.0 → 8.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -210
  3. data/lib/active_support/backtrace_cleaner.rb +1 -1
  4. data/lib/active_support/benchmark.rb +21 -0
  5. data/lib/active_support/benchmarkable.rb +3 -2
  6. data/lib/active_support/broadcast_logger.rb +14 -14
  7. data/lib/active_support/cache/file_store.rb +12 -2
  8. data/lib/active_support/cache/memory_store.rb +6 -2
  9. data/lib/active_support/cache/redis_cache_store.rb +5 -2
  10. data/lib/active_support/cache.rb +16 -11
  11. data/lib/active_support/callbacks.rb +1 -2
  12. data/lib/active_support/class_attribute.rb +26 -0
  13. data/lib/active_support/code_generator.rb +9 -0
  14. data/lib/active_support/concurrency/share_lock.rb +0 -1
  15. data/lib/active_support/configuration_file.rb +15 -6
  16. data/lib/active_support/core_ext/benchmark.rb +6 -9
  17. data/lib/active_support/core_ext/class/attribute.rb +10 -19
  18. data/lib/active_support/core_ext/date/conversions.rb +2 -0
  19. data/lib/active_support/core_ext/date_and_time/compatibility.rb +2 -2
  20. data/lib/active_support/core_ext/enumerable.rb +8 -3
  21. data/lib/active_support/core_ext/hash/except.rb +0 -12
  22. data/lib/active_support/core_ext/object/json.rb +15 -9
  23. data/lib/active_support/core_ext/time/calculations.rb +14 -2
  24. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  25. data/lib/active_support/current_attributes.rb +8 -3
  26. data/lib/active_support/deprecation/reporting.rb +2 -2
  27. data/lib/active_support/deprecation.rb +1 -1
  28. data/lib/active_support/encrypted_configuration.rb +20 -2
  29. data/lib/active_support/encrypted_file.rb +1 -1
  30. data/lib/active_support/error_reporter.rb +25 -1
  31. data/lib/active_support/gem_version.rb +3 -3
  32. data/lib/active_support/hash_with_indifferent_access.rb +16 -12
  33. data/lib/active_support/i18n_railtie.rb +19 -10
  34. data/lib/active_support/isolated_execution_state.rb +0 -1
  35. data/lib/active_support/json/encoding.rb +2 -2
  36. data/lib/active_support/notifications.rb +2 -2
  37. data/lib/active_support/number_helper.rb +22 -0
  38. data/lib/active_support/railtie.rb +4 -0
  39. data/lib/active_support/subscriber.rb +1 -0
  40. data/lib/active_support/tagged_logging.rb +5 -0
  41. data/lib/active_support/testing/assertions.rb +72 -21
  42. data/lib/active_support/testing/isolation.rb +2 -2
  43. data/lib/active_support/testing/parallelization/server.rb +3 -0
  44. data/lib/active_support/testing/strict_warnings.rb +3 -0
  45. data/lib/active_support/testing/time_helpers.rb +2 -1
  46. data/lib/active_support/time_with_zone.rb +21 -12
  47. data/lib/active_support/values/time_zone.rb +11 -9
  48. data/lib/active_support.rb +10 -2
  49. metadata +40 -10
@@ -113,6 +113,11 @@ module ActiveSupport
113
113
  end
114
114
  end
115
115
 
116
+ # Returns an `ActiveSupport::Logger` that has already been wrapped with tagged logging concern.
117
+ def self.logger(*args, **kwargs)
118
+ new ActiveSupport::Logger.new(*args, **kwargs)
119
+ end
120
+
116
121
  def self.new(logger)
117
122
  logger = logger.clone
118
123
 
@@ -19,7 +19,7 @@ module ActiveSupport
19
19
  #
20
20
  # assert_not foo, 'foo should be false'
21
21
  def assert_not(object, message = nil)
22
- message ||= "Expected #{mu_pp(object)} to be nil or false"
22
+ message ||= -> { "Expected #{mu_pp(object)} to be nil or false" }
23
23
  assert !object, message
24
24
  end
25
25
 
@@ -118,9 +118,13 @@ module ActiveSupport
118
118
 
119
119
  expressions.zip(exps, before) do |(code, diff), exp, before_value|
120
120
  actual = exp.call
121
- error = "#{code.inspect} didn't change by #{diff}, but by #{actual - before_value}"
122
- error = "#{message}.\n#{error}" if message
123
- assert_equal(before_value + diff, actual, error)
121
+ rich_message = -> do
122
+ code_string = code.respond_to?(:call) ? _callable_to_source_string(code) : code
123
+ error = "`#{code_string}` didn't change by #{diff}, but by #{actual - before_value}"
124
+ error = "#{message}.\n#{error}" if message
125
+ error
126
+ end
127
+ assert_equal(before_value + diff, actual, rich_message)
124
128
  end
125
129
 
126
130
  retval
@@ -195,22 +199,32 @@ module ActiveSupport
195
199
  retval = _assert_nothing_raised_or_warn("assert_changes", &block)
196
200
 
197
201
  unless from == UNTRACKED
198
- error = "Expected change from #{from.inspect}, got #{before.inspect}"
199
- error = "#{message}.\n#{error}" if message
200
- assert from === before, error
202
+ rich_message = -> do
203
+ error = "Expected change from #{from.inspect}, got #{before.inspect}"
204
+ error = "#{message}.\n#{error}" if message
205
+ error
206
+ end
207
+ assert from === before, rich_message
201
208
  end
202
209
 
203
210
  after = exp.call
204
211
 
205
- error = "#{expression.inspect} didn't change"
206
- error = "#{error}. It was already #{to.inspect}" if before == to
207
- error = "#{message}.\n#{error}" if message
208
- refute_equal before, after, error
212
+ rich_message = -> do
213
+ code_string = expression.respond_to?(:call) ? _callable_to_source_string(expression) : expression
214
+ error = "`#{code_string}` didn't change"
215
+ error = "#{error}. It was already #{to.inspect}" if before == to
216
+ error = "#{message}.\n#{error}" if message
217
+ error
218
+ end
219
+ refute_equal before, after, rich_message
209
220
 
210
221
  unless to == UNTRACKED
211
- error = "Expected change to #{to.inspect}, got #{after.inspect}\n"
212
- error = "#{message}.\n#{error}" if message
213
- assert to === after, error
222
+ rich_message = -> do
223
+ error = "Expected change to #{to.inspect}, got #{after.inspect}\n"
224
+ error = "#{message}.\n#{error}" if message
225
+ error
226
+ end
227
+ assert to === after, rich_message
214
228
  end
215
229
 
216
230
  retval
@@ -242,20 +256,27 @@ module ActiveSupport
242
256
  retval = _assert_nothing_raised_or_warn("assert_no_changes", &block)
243
257
 
244
258
  unless from == UNTRACKED
245
- error = "Expected initial value of #{from.inspect}, got #{before.inspect}"
246
- error = "#{message}.\n#{error}" if message
247
- assert from === before, error
259
+ rich_message = -> do
260
+ error = "Expected initial value of #{from.inspect}, got #{before.inspect}"
261
+ error = "#{message}.\n#{error}" if message
262
+ error
263
+ end
264
+ assert from === before, rich_message
248
265
  end
249
266
 
250
267
  after = exp.call
251
268
 
252
- error = "#{expression.inspect} changed"
253
- error = "#{message}.\n#{error}" if message
269
+ rich_message = -> do
270
+ code_string = expression.respond_to?(:call) ? _callable_to_source_string(expression) : expression
271
+ error = "`#{code_string}` changed"
272
+ error = "#{message}.\n#{error}" if message
273
+ error
274
+ end
254
275
 
255
276
  if before.nil?
256
- assert_nil after, error
277
+ assert_nil after, rich_message
257
278
  else
258
- assert_equal before, after, error
279
+ assert_equal before, after, rich_message
259
280
  end
260
281
 
261
282
  retval
@@ -276,6 +297,36 @@ module ActiveSupport
276
297
 
277
298
  raise
278
299
  end
300
+
301
+ def _callable_to_source_string(callable)
302
+ if defined?(RubyVM::AbstractSyntaxTree) && callable.is_a?(Proc)
303
+ ast = begin
304
+ RubyVM::AbstractSyntaxTree.of(callable, keep_script_lines: true)
305
+ rescue SystemCallError
306
+ # Failed to get the source somehow
307
+ return callable
308
+ end
309
+ return callable unless ast
310
+
311
+ source = ast.source
312
+ source.strip!
313
+
314
+ # We ignore procs defined with do/end as they are likely multi-line anyway.
315
+ if source.start_with?("{")
316
+ source.delete_suffix!("}")
317
+ source.delete_prefix!("{")
318
+ source.strip!
319
+ # It won't read nice if the callable contains multiple
320
+ # lines, and it should be a rare occurence anyway.
321
+ # Same if it takes arguments.
322
+ if !source.include?("\n") && !source.start_with?("|")
323
+ return source
324
+ end
325
+ end
326
+ end
327
+
328
+ callable
329
+ end
279
330
  end
280
331
  end
281
332
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/testing/parallelize_executor"
4
+
3
5
  module ActiveSupport
4
6
  module Testing
5
7
  module Isolation
6
- require "thread"
7
-
8
8
  SubprocessCrashed = Class.new(StandardError)
9
9
 
10
10
  def self.included(klass) # :nodoc:
@@ -6,6 +6,8 @@ require "drb/unix" unless Gem.win_platform?
6
6
  module ActiveSupport
7
7
  module Testing
8
8
  class Parallelization # :nodoc:
9
+ PrerecordResultClass = Struct.new(:name)
10
+
9
11
  class Server
10
12
  include DRb::DRbUndumped
11
13
 
@@ -21,6 +23,7 @@ module ActiveSupport
21
23
  @in_flight.delete([result.klass, result.name])
22
24
 
23
25
  reporter.synchronize do
26
+ reporter.prerecord(PrerecordResultClass.new(result.klass), result.name)
24
27
  reporter.record(result)
25
28
  end
26
29
  end
@@ -14,6 +14,9 @@ module ActiveSupport
14
14
  # Expected non-verbose warning emitted by Rails.
15
15
  /Ignoring .*\.yml because it has expired/,
16
16
  /Failed to validate the schema cache because/,
17
+
18
+ # TODO: We need to decide what to do with this.
19
+ /Status code :unprocessable_entity is deprecated/
17
20
  )
18
21
 
19
22
  SUPPRESSED_WARNINGS = Regexp.union(
@@ -166,9 +166,10 @@ module ActiveSupport
166
166
  else
167
167
  now = date_or_time
168
168
  now = now.to_time unless now.is_a?(Time)
169
- now = now.change(usec: 0) unless with_usec
170
169
  end
171
170
 
171
+ now = now.change(usec: 0) unless with_usec
172
+
172
173
  # +now+ must be in local system timezone, because +Time.at(now)+
173
174
  # and +now.to_date+ (see stubs below) will use +now+'s timezone too!
174
175
  now = now.getlocal
@@ -138,7 +138,7 @@ module ActiveSupport
138
138
  #
139
139
  # Time.zone.now.inspect # => "Thu, 04 Dec 2014 11:00:25.624541392 EST -05:00"
140
140
  def inspect
141
- "#{time.strftime('%a, %d %b %Y %H:%M:%S.%9N')} #{zone} #{formatted_offset}"
141
+ "#{time.strftime('%F %H:%M:%S.%9N')} #{zone} #{formatted_offset}"
142
142
  end
143
143
 
144
144
  # Returns a string of the object's date and time in the ISO 8601 standard
@@ -157,11 +157,11 @@ module ActiveSupport
157
157
  # to +false+.
158
158
  #
159
159
  # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
160
- # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
160
+ # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").as_json
161
161
  # # => "2005-02-01T05:15:10.000-10:00"
162
162
  #
163
163
  # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
164
- # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
164
+ # Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").as_json
165
165
  # # => "2005/02/01 05:15:10 -1000"
166
166
  def as_json(options = nil)
167
167
  if ActiveSupport::JSON::Encoding.use_standard_json_time_format
@@ -215,8 +215,7 @@ module ActiveSupport
215
215
  elsif formatter = ::Time::DATE_FORMATS[format]
216
216
  formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
217
217
  else
218
- # Change to to_s when deprecation is gone.
219
- "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}"
218
+ to_s
220
219
  end
221
220
  end
222
221
  alias_method :to_formatted_s, :to_fs
@@ -300,7 +299,16 @@ module ActiveSupport
300
299
  if duration_of_variable_length?(other)
301
300
  method_missing(:+, other)
302
301
  else
303
- result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other)
302
+ begin
303
+ result = utc + other
304
+ rescue TypeError
305
+ result = utc.to_datetime.since(other)
306
+ ActiveSupport.deprecator.warn(
307
+ "Adding an instance of #{other.class} to an instance of #{self.class} is deprecated. This behavior will raise " \
308
+ "a `TypeError` in Rails 8.1."
309
+ )
310
+ result.in_time_zone(time_zone)
311
+ end
304
312
  result.in_time_zone(time_zone)
305
313
  end
306
314
  end
@@ -336,7 +344,7 @@ module ActiveSupport
336
344
  elsif duration_of_variable_length?(other)
337
345
  method_missing(:-, other)
338
346
  else
339
- result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)
347
+ result = utc - other
340
348
  result.in_time_zone(time_zone)
341
349
  end
342
350
  end
@@ -479,11 +487,13 @@ module ActiveSupport
479
487
  @to_datetime ||= utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
480
488
  end
481
489
 
482
- # Returns an instance of +Time+, either with the same UTC offset
483
- # as +self+ or in the local system timezone depending on the setting
484
- # of +ActiveSupport.to_time_preserves_timezone+.
490
+ # Returns an instance of +Time+, either with the same timezone as +self+,
491
+ # with the same UTC offset as +self+ or in the local system timezone
492
+ # depending on the setting of +ActiveSupport.to_time_preserves_timezone+.
485
493
  def to_time
486
- if preserve_timezone
494
+ if preserve_timezone == :zone
495
+ @to_time_with_timezone ||= getlocal(time_zone)
496
+ elsif preserve_timezone
487
497
  @to_time_with_instance_offset ||= getlocal(utc_offset)
488
498
  else
489
499
  @to_time_with_system_offset ||= getlocal
@@ -535,7 +545,6 @@ module ActiveSupport
535
545
  # Ensure proxy class responds to all methods that underlying time instance
536
546
  # responds to.
537
547
  def respond_to_missing?(sym, include_priv)
538
- return false if sym.to_sym == :acts_like_date?
539
548
  time.respond_to?(sym, include_priv)
540
549
  end
541
550
 
@@ -208,9 +208,7 @@ module ActiveSupport
208
208
  TZInfo::Timezone.get(MAPPING[name] || name)
209
209
  end
210
210
 
211
- # :stopdoc:
212
- alias_method :create, :new
213
- # :startdoc:
211
+ alias_method :create, :new # :nodoc:
214
212
 
215
213
  # Returns a TimeZone instance with the given name, or +nil+ if no
216
214
  # such TimeZone instance exists. (This exists to support the use of
@@ -554,15 +552,11 @@ module ActiveSupport
554
552
  tzinfo.local_to_utc(time, dst)
555
553
  end
556
554
 
557
- # Available so that TimeZone instances respond like +TZInfo::Timezone+
558
- # instances.
559
- def period_for_utc(time)
555
+ def period_for_utc(time) # :nodoc:
560
556
  tzinfo.period_for_utc(time)
561
557
  end
562
558
 
563
- # Available so that TimeZone instances respond like +TZInfo::Timezone+
564
- # instances.
565
- def period_for_local(time, dst = true)
559
+ def period_for_local(time, dst = true) # :nodoc:
566
560
  tzinfo.period_for_local(time, dst) { |periods| periods.last }
567
561
  end
568
562
 
@@ -570,6 +564,14 @@ module ActiveSupport
570
564
  tzinfo.periods_for_local(time)
571
565
  end
572
566
 
567
+ def abbr(time) # :nodoc:
568
+ tzinfo.abbr(time)
569
+ end
570
+
571
+ def dst?(time) # :nodoc:
572
+ tzinfo.dst?(time)
573
+ end
574
+
573
575
  def init_with(coder) # :nodoc:
574
576
  initialize(coder["name"])
575
577
  end
@@ -58,10 +58,12 @@ module ActiveSupport
58
58
  eager_autoload do
59
59
  autoload :BacktraceCleaner
60
60
  autoload :ProxyObject
61
+ autoload :Benchmark
61
62
  autoload :Benchmarkable
62
63
  autoload :Cache
63
64
  autoload :Callbacks
64
65
  autoload :Configurable
66
+ autoload :ClassAttribute
65
67
  autoload :Deprecation
66
68
  autoload :Delegation
67
69
  autoload :Digest
@@ -115,9 +117,15 @@ module ActiveSupport
115
117
  end
116
118
 
117
119
  def self.to_time_preserves_timezone=(value)
118
- unless value
120
+ if !value
119
121
  ActiveSupport.deprecator.warn(
120
- "Support for the pre-Ruby 2.4 behavior of to_time has been deprecated and will be removed in Rails 8.0."
122
+ "`to_time` will always preserve the receiver timezone rather than system local time in Rails 8.0. " \
123
+ "To opt in to the new behavior, set `config.active_support.to_time_preserves_timezone = :zone`."
124
+ )
125
+ elsif value != :zone
126
+ ActiveSupport.deprecator.warn(
127
+ "`to_time` will always preserve the full timezone rather than offset of the receiver in Rails 8.0. " \
128
+ "To opt in to the new behavior, set `config.active_support.to_time_preserves_timezone = :zone`."
121
129
  )
122
130
  end
123
131
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activesupport
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.2.0
4
+ version: 8.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-09 00:00:00.000000000 Z
11
+ date: 2024-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -168,6 +168,34 @@ dependencies:
168
168
  - - ">="
169
169
  - !ruby/object:Gem::Version
170
170
  version: '0.3'
171
+ - !ruby/object:Gem::Dependency
172
+ name: uri
173
+ requirement: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: 0.13.1
178
+ type: :runtime
179
+ prerelease: false
180
+ version_requirements: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: 0.13.1
185
+ - !ruby/object:Gem::Dependency
186
+ name: benchmark
187
+ requirement: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: '0.3'
192
+ type: :runtime
193
+ prerelease: false
194
+ version_requirements: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: '0.3'
171
199
  description: A toolkit of support libraries and Ruby core extensions extracted from
172
200
  the Rails framework. Rich support for multibyte strings, internationalization, time
173
201
  zones, and testing.
@@ -184,6 +212,7 @@ files:
184
212
  - lib/active_support/all.rb
185
213
  - lib/active_support/array_inquirer.rb
186
214
  - lib/active_support/backtrace_cleaner.rb
215
+ - lib/active_support/benchmark.rb
187
216
  - lib/active_support/benchmarkable.rb
188
217
  - lib/active_support/broadcast_logger.rb
189
218
  - lib/active_support/builder.rb
@@ -199,6 +228,7 @@ files:
199
228
  - lib/active_support/cache/strategy/local_cache.rb
200
229
  - lib/active_support/cache/strategy/local_cache_middleware.rb
201
230
  - lib/active_support/callbacks.rb
231
+ - lib/active_support/class_attribute.rb
202
232
  - lib/active_support/code_generator.rb
203
233
  - lib/active_support/concern.rb
204
234
  - lib/active_support/concurrency/load_interlock_aware_monitor.rb
@@ -466,12 +496,12 @@ licenses:
466
496
  - MIT
467
497
  metadata:
468
498
  bug_tracker_uri: https://github.com/rails/rails/issues
469
- changelog_uri: https://github.com/rails/rails/blob/v7.2.0/activesupport/CHANGELOG.md
470
- documentation_uri: https://api.rubyonrails.org/v7.2.0/
499
+ changelog_uri: https://github.com/rails/rails/blob/v8.0.0.beta1/activesupport/CHANGELOG.md
500
+ documentation_uri: https://api.rubyonrails.org/v8.0.0.beta1/
471
501
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
472
- source_code_uri: https://github.com/rails/rails/tree/v7.2.0/activesupport
502
+ source_code_uri: https://github.com/rails/rails/tree/v8.0.0.beta1/activesupport
473
503
  rubygems_mfa_required: 'true'
474
- post_install_message:
504
+ post_install_message:
475
505
  rdoc_options:
476
506
  - "--encoding"
477
507
  - UTF-8
@@ -481,15 +511,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
481
511
  requirements:
482
512
  - - ">="
483
513
  - !ruby/object:Gem::Version
484
- version: 3.1.0
514
+ version: 3.2.0
485
515
  required_rubygems_version: !ruby/object:Gem::Requirement
486
516
  requirements:
487
517
  - - ">="
488
518
  - !ruby/object:Gem::Version
489
519
  version: '0'
490
520
  requirements: []
491
- rubygems_version: 3.5.11
492
- signing_key:
521
+ rubygems_version: 3.5.16
522
+ signing_key:
493
523
  specification_version: 4
494
524
  summary: A toolkit of support libraries and Ruby core extensions extracted from the
495
525
  Rails framework.