activesupport 7.1.0 → 7.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82365193f8aa8e04a486a7ed6f82adeb64093abd70569d67c7756b065f754b82
4
- data.tar.gz: ececf5de5e7b69fe11b7edc1007e7b850c2463005ffba7f2763b85636e192d98
3
+ metadata.gz: 6db71966858e675d32617069b0df8dc17b1c5dfe8123c87ae96a28fdb6e2f5e6
4
+ data.tar.gz: 81ffe410567130147cd0c6f74943d40404a4031b2cf30f12bfaa2ee698cd5a57
5
5
  SHA512:
6
- metadata.gz: e6daffabed388ec6412bff231a3d2478f430625d266078fe186c92594353b87026dc22a492e689ea7a716a570eb75a9eb53582a45b33627a06444be1b560da7e
7
- data.tar.gz: 33dc1b13f205838ad773fc7c4bfda5bb6bec704cbdb7ffdaedfe938a91088c2dd98d1ade38514689dbb66d2f5f45e3871973b8981c21cd6deabec0cff41349da
6
+ metadata.gz: 99a9131ff9f97e739719621df9ba0a33d3b2fad9b1315397382235f29d33ee130f701d0c0ed767eacf01ccb31607eb26d7f5bb95f37ad86ac57f18817cd9b253
7
+ data.tar.gz: ffa6a0ffe90b88c87f65d85ddb32390d20f8c34ab2517f797c34d325839cbf434a0202650cdd54f118bee9a299cf478f83af4222c313f2746e267bbd19003914
data/CHANGELOG.md CHANGED
@@ -1,3 +1,91 @@
1
+ ## Rails 7.1.2 (November 10, 2023) ##
2
+
3
+ * Fix `:expires_in` option for `RedisCacheStore#write_multi`.
4
+
5
+ *fatkodima*
6
+
7
+ * Fix deserialization of non-string "purpose" field in Message serializer
8
+
9
+ *Jacopo Beschi*
10
+
11
+ * Prevent global cache options being overwritten when setting dynamic options
12
+ inside a `ActiveSupport::Cache::Store#fetch` block.
13
+
14
+ *Yasha Krasnou*
15
+
16
+ * Fix missing `require` resulting in `NoMethodError` when running
17
+ `bin/rails secrets:show` or `bin/rails secrets:edit`.
18
+
19
+ *Stephen Ierodiaconou*
20
+
21
+ * Ensure `{down,up}case_first` returns non-frozen string.
22
+
23
+ *Jonathan Hefner*
24
+
25
+ * Fix `#to_fs(:human_size)` to correctly work with negative numbers.
26
+
27
+ *Earlopain*
28
+
29
+ * Fix `BroadcastLogger#dup` so that it duplicates the logger's `broadcasts`.
30
+
31
+ *Andrew Novoselac*
32
+
33
+ * Fix issue where `bootstrap.rb` overwrites the `level` of a `BroadcastLogger`'s `broadcasts`.
34
+
35
+ *Andrew Novoselac*
36
+
37
+ * Fix `ActiveSupport::Cache` to handle outdated Marshal payload from Rails 6.1 format.
38
+
39
+ Active Support's Cache is supposed to treat a Marshal payload that can no longer be
40
+ deserialized as a cache miss. It fail to do so for compressed payload in the Rails 6.1
41
+ legacy format.
42
+
43
+ *Jean Boussier*
44
+
45
+ * Fix `OrderedOptions#dig` for array indexes.
46
+
47
+ *fatkodima*
48
+
49
+ * Fix time travel helpers to work when nested using with separate classes.
50
+
51
+ *fatkodima*
52
+
53
+ * Fix `delete_matched` for file cache store to work with keys longer than the
54
+ max filename size.
55
+
56
+ *fatkodima* and *Jonathan Hefner*
57
+
58
+ * Fix compatibility with the `semantic_logger` gem.
59
+
60
+ The `semantic_logger` gem doesn't behave exactly like stdlib logger in that
61
+ `SemanticLogger#level` returns a Symbol while stdlib `Logger#level` returns an Integer.
62
+
63
+ This caused the various `LogSubscriber` classes in Rails to break when assigned a
64
+ `SemanticLogger` instance.
65
+
66
+ *Jean Boussier*, *ojab*
67
+
68
+ ## Rails 7.1.1 (October 11, 2023) ##
69
+
70
+ * Add support for keyword arguments when delegating calls to custom loggers from `ActiveSupport::BroadcastLogger`.
71
+
72
+ *Edouard Chin*
73
+
74
+ * `NumberHelper`: handle objects responding `to_d`.
75
+
76
+ *fatkodima*
77
+
78
+ * Fix RedisCacheStore to properly set the TTL when incrementing or decrementing.
79
+
80
+ This bug was only impacting Redis server older than 7.0.
81
+
82
+ *Thomas Countz*
83
+
84
+ * Fix MemoryStore to prevent race conditions when incrementing or decrementing.
85
+
86
+ *Pierre Jambet*
87
+
88
+
1
89
  ## Rails 7.1.0 (October 05, 2023) ##
2
90
 
3
91
  * No changes.
@@ -868,7 +956,7 @@
868
956
 
869
957
  *Trevor Turk*
870
958
 
871
- * `ActiveSupport::Cache:Store#fetch` now passes an options accessor to the block.
959
+ * `ActiveSupport::Cache::Store#fetch` now passes an options accessor to the block.
872
960
 
873
961
  It makes possible to override cache options:
874
962
 
@@ -51,6 +51,26 @@ module ActiveSupport
51
51
  #
52
52
  # broadcast = BroadcastLogger.new
53
53
  # broadcast.info("Hello world") # The log message will appear nowhere.
54
+ #
55
+ # If you are adding a custom logger with custom methods to the broadcast,
56
+ # the `BroadcastLogger` will proxy them and return the raw value, or an array
57
+ # of raw values, depending on how many loggers in the broadcasts responded to
58
+ # the method:
59
+ #
60
+ # class MyLogger < ::Logger
61
+ # def loggable?
62
+ # true
63
+ # end
64
+ # end
65
+ #
66
+ # logger = BroadcastLogger.new
67
+ # logger.loggable? # => A NoMethodError exception is raised because no loggers in the broadcasts could respond.
68
+ #
69
+ # logger.broadcast_to(MyLogger.new(STDOUT))
70
+ # logger.loggable? # => true
71
+ # logger.broadcast_to(MyLogger.new(STDOUT))
72
+ # puts logger.broadcasts # => [MyLogger, MyLogger]
73
+ # logger.loggable? # [true, true]
54
74
  class BroadcastLogger
55
75
  include ActiveSupport::LoggerSilence
56
76
 
@@ -198,20 +218,28 @@ module ActiveSupport
198
218
  dispatch { |logger| logger.fatal! }
199
219
  end
200
220
 
221
+ def initialize_copy(other)
222
+ @broadcasts = []
223
+ @progname = other.progname.dup
224
+ @formatter = other.formatter.dup
225
+
226
+ broadcast_to(*other.broadcasts.map(&:dup))
227
+ end
228
+
201
229
  private
202
230
  def dispatch(&block)
203
231
  @broadcasts.each { |logger| block.call(logger) }
204
232
  end
205
233
 
206
- def method_missing(name, *args, &block)
234
+ def method_missing(name, *args, **kwargs, &block)
207
235
  loggers = @broadcasts.select { |logger| logger.respond_to?(name) }
208
236
 
209
237
  if loggers.none?
210
- super(name, *args, &block)
238
+ super(name, *args, **kwargs, &block)
211
239
  elsif loggers.one?
212
- loggers.first.send(name, *args, &block)
240
+ loggers.first.send(name, *args, **kwargs, &block)
213
241
  else
214
- loggers.map { |logger| logger.send(name, *args, &block) }
242
+ loggers.map { |logger| logger.send(name, *args, **kwargs, &block) }
215
243
  end
216
244
  end
217
245
 
@@ -121,7 +121,13 @@ module ActiveSupport
121
121
 
122
122
  private
123
123
  def uncompress(value)
124
- Marshal.load(Zlib::Inflate.inflate(value))
124
+ marshal_load(Zlib::Inflate.inflate(value))
125
+ end
126
+
127
+ def marshal_load(payload)
128
+ Marshal.load(payload)
129
+ rescue ArgumentError => error
130
+ raise Cache::DeserializationError, error.message
125
131
  end
126
132
  end
127
133
  end
@@ -176,7 +176,7 @@ module ActiveSupport
176
176
 
177
177
  # Translate a file path into a key.
178
178
  def file_path_key(path)
179
- fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
179
+ fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last.delete(File::SEPARATOR)
180
180
  URI.decode_www_form_component(fname, Encoding::UTF_8)
181
181
  end
182
182
 
@@ -270,14 +270,22 @@ module ActiveSupport
270
270
  def read_multi_entries(names, **options)
271
271
  keys_to_names = names.index_by { |name| normalize_key(name, options) }
272
272
 
273
- raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
273
+ raw_values = begin
274
+ @data.with { |c| c.get_multi(keys_to_names.keys) }
275
+ rescue Dalli::UnmarshalError
276
+ {}
277
+ end
278
+
274
279
  values = {}
275
280
 
276
281
  raw_values.each do |key, value|
277
282
  entry = deserialize_entry(value, raw: options[:raw])
278
283
 
279
284
  unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
280
- values[keys_to_names[key]] = entry.value
285
+ begin
286
+ values[keys_to_names[key]] = entry.value
287
+ rescue DeserializationError
288
+ end
281
289
  end
282
290
  end
283
291
 
@@ -238,16 +238,18 @@ module ActiveSupport
238
238
  key = normalize_key(name, options)
239
239
  version = normalize_version(name, options)
240
240
 
241
- entry = read_entry(key, **options)
241
+ synchronize do
242
+ entry = read_entry(key, **options)
242
243
 
243
- if !entry || entry.expired? || entry.mismatched?(version)
244
- write(name, Integer(amount), options)
245
- amount
246
- else
247
- num = entry.value.to_i + amount
248
- entry = Entry.new(num, expires_at: entry.expires_at, version: entry.version)
249
- write_entry(key, entry)
250
- num
244
+ if !entry || entry.expired? || entry.mismatched?(version)
245
+ write(name, Integer(amount), options)
246
+ amount
247
+ else
248
+ num = entry.value.to_i + amount
249
+ entry = Entry.new(num, expires_at: entry.expires_at, version: entry.version)
250
+ write_entry(key, entry)
251
+ num
252
+ end
251
253
  end
252
254
  end
253
255
  end
@@ -332,7 +332,10 @@ module ActiveSupport
332
332
  if value
333
333
  entry = deserialize_entry(value, raw: raw)
334
334
  unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options))
335
- results[name] = entry.value
335
+ begin
336
+ results[name] = entry.value
337
+ rescue DeserializationError
338
+ end
336
339
  end
337
340
  end
338
341
  end
@@ -383,7 +386,7 @@ module ActiveSupport
383
386
  end
384
387
 
385
388
  # Nonstandard store provider API to write multiple values at once.
386
- def write_multi_entries(entries, expires_in: nil, race_condition_ttl: nil, **options)
389
+ def write_multi_entries(entries, **options)
387
390
  return if entries.empty?
388
391
 
389
392
  failsafe :write_multi_entries do
@@ -438,18 +441,26 @@ module ActiveSupport
438
441
  redis.then do |c|
439
442
  c = c.node_for(key) if c.is_a?(Redis::Distributed)
440
443
 
441
- if options[:expires_in] && supports_expire_nx?
442
- c.pipelined do |pipeline|
443
- pipeline.incrby(key, amount)
444
- pipeline.call(:expire, key, options[:expires_in].to_i, "NX")
445
- end.first
444
+ expires_in = options[:expires_in]
445
+
446
+ if expires_in
447
+ if supports_expire_nx?
448
+ count, _ = c.pipelined do |pipeline|
449
+ pipeline.incrby(key, amount)
450
+ pipeline.call(:expire, key, expires_in.to_i, "NX")
451
+ end
452
+ else
453
+ count, ttl = c.pipelined do |pipeline|
454
+ pipeline.incrby(key, amount)
455
+ pipeline.ttl(key)
456
+ end
457
+ c.expire(key, expires_in.to_i) if ttl < 0
458
+ end
446
459
  else
447
460
  count = c.incrby(key, amount)
448
- if count != amount && options[:expires_in] && c.ttl(key) < 0
449
- c.expire(key, options[:expires_in].to_i)
450
- end
451
- count
452
461
  end
462
+
463
+ count
453
464
  end
454
465
  end
455
466
 
@@ -439,9 +439,9 @@ module ActiveSupport
439
439
  #
440
440
  # ==== Dynamic Options
441
441
  #
442
- # In some cases it may be necessary to to dynamically compute options based
443
- # on the cached value. For this purpose, a ActiveSupport::Cache::WriteOptions
444
- # instance is passed as a second argument to the block
442
+ # In some cases it may be necessary to dynamically compute options based
443
+ # on the cached value. To support this, an ActiveSupport::Cache::WriteOptions
444
+ # instance is passed as the second argument to the block. For example:
445
445
  #
446
446
  # cache.fetch("authentication-token:#{user.id}") do |key, options|
447
447
  # token = authenticate_to_service
@@ -449,12 +449,6 @@ module ActiveSupport
449
449
  # token
450
450
  # end
451
451
  #
452
- # Only some options can be set dynamically:
453
- #
454
- # - +:expires_in+
455
- # - +:expires_at+
456
- # - +:version+
457
- #
458
452
  def fetch(name, options = nil, &block)
459
453
  if block_given?
460
454
  options = merged_options(options)
@@ -465,7 +459,17 @@ module ActiveSupport
465
459
  instrument(:read, name, options) do |payload|
466
460
  cached_entry = read_entry(key, **options, event: payload)
467
461
  entry = handle_expired_entry(cached_entry, key, options)
468
- entry = nil if entry && entry.mismatched?(normalize_version(name, options))
462
+ if entry
463
+ if entry.mismatched?(normalize_version(name, options))
464
+ entry = nil
465
+ else
466
+ begin
467
+ entry.value
468
+ rescue DeserializationError
469
+ entry = nil
470
+ end
471
+ end
472
+ end
469
473
  payload[:super_operation] = :fetch if payload
470
474
  payload[:hit] = !!entry if payload
471
475
  end
@@ -517,7 +521,12 @@ module ActiveSupport
517
521
  nil
518
522
  else
519
523
  payload[:hit] = true if payload
520
- entry.value
524
+ begin
525
+ entry.value
526
+ rescue DeserializationError
527
+ payload[:hit] = false
528
+ nil
529
+ end
521
530
  end
522
531
  else
523
532
  payload[:hit] = false if payload
@@ -1044,6 +1053,8 @@ module ActiveSupport
1044
1053
  end
1045
1054
 
1046
1055
  def save_block_result_to_cache(name, options)
1056
+ options = options.dup
1057
+
1047
1058
  result = instrument(:generate, name, options) do
1048
1059
  yield(name, WriteOptions.new(options))
1049
1060
  end
@@ -52,7 +52,7 @@ class Date
52
52
  strftime(formatter)
53
53
  end
54
54
  else
55
- to_default_s
55
+ to_s
56
56
  end
57
57
  end
58
58
  alias_method :to_formatted_s, :to_fs
@@ -3,7 +3,7 @@
3
3
  require "active_support/concern"
4
4
 
5
5
  class Module
6
- # = Bite-sized separation of concerns
6
+ # == Bite-sized separation of concerns
7
7
  #
8
8
  # We often find ourselves with a medium-sized chunk of behavior that we'd
9
9
  # like to extract, but only mix in to a single class.
@@ -18,9 +18,9 @@ class Module
18
18
  # with a comment, as a least-bad alternative. Using modules in separate files
19
19
  # means tedious sifting to get a big-picture view.
20
20
  #
21
- # = Dissatisfying ways to separate small concerns
21
+ # == Dissatisfying ways to separate small concerns
22
22
  #
23
- # == Using comments:
23
+ # === Using comments:
24
24
  #
25
25
  # class Todo < ApplicationRecord
26
26
  # # Other todo implementation
@@ -37,7 +37,7 @@ class Module
37
37
  # end
38
38
  # end
39
39
  #
40
- # == With an inline module:
40
+ # === With an inline module:
41
41
  #
42
42
  # Noisy syntax.
43
43
  #
@@ -61,7 +61,7 @@ class Module
61
61
  # include EventTracking
62
62
  # end
63
63
  #
64
- # == Mix-in noise exiled to its own file:
64
+ # === Mix-in noise exiled to its own file:
65
65
  #
66
66
  # Once our chunk of behavior starts pushing the scroll-to-understand-it
67
67
  # boundary, we give in and move it to a separate file. At this size, the
@@ -75,7 +75,7 @@ class Module
75
75
  # include TodoEventTracking
76
76
  # end
77
77
  #
78
- # = Introducing Module#concerning
78
+ # == Introducing Module#concerning
79
79
  #
80
80
  # By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
81
81
  # separate bite-sized concerns.
@@ -29,7 +29,7 @@ require "active_support/core_ext/date/conversions"
29
29
  # It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
30
30
  # bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
31
31
  # ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
32
- # should give exactly the same results with or without active support.
32
+ # should give exactly the same results with or without Active Support.
33
33
 
34
34
  module ActiveSupport
35
35
  module ToJsonWithActiveSupportEncoder # :nodoc:
@@ -60,8 +60,8 @@ module ActiveSupport
60
60
  # [+raise+] Raise ActiveSupport::DeprecationException.
61
61
  # [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
62
62
  # [+log+] Log all deprecation warnings to +Rails.logger+.
63
- # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
64
- # [+report+] Use +ActiveSupport::ErrorReporter+ to report deprecations.
63
+ # [+notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
64
+ # [+report+] Use ActiveSupport::ErrorReporter to report deprecations.
65
65
  # [+silence+] Do nothing. On \Rails, set <tt>config.active_support.report_deprecations = false</tt> to disable all behaviors.
66
66
  #
67
67
  # Setting behaviors only affects deprecations that happen after boot time.
@@ -88,8 +88,8 @@ module ActiveSupport
88
88
  # [+raise+] Raise ActiveSupport::DeprecationException.
89
89
  # [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
90
90
  # [+log+] Log all deprecation warnings to +Rails.logger+.
91
- # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
92
- # [+report+] Use +ActiveSupport::ErrorReporter+ to report deprecations.
91
+ # [+notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
92
+ # [+report+] Use ActiveSupport::ErrorReporter to report deprecations.
93
93
  # [+silence+] Do nothing.
94
94
  #
95
95
  # Setting behaviors only affects deprecations that happen after boot time.
@@ -9,7 +9,7 @@ module ActiveSupport
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 1
12
- TINY = 0
12
+ TINY = 2
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -164,7 +164,7 @@ module ActiveSupport
164
164
  # upcase_first('w') # => "W"
165
165
  # upcase_first('') # => ""
166
166
  def upcase_first(string)
167
- string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
167
+ string.length > 0 ? string[0].upcase.concat(string[1..-1]) : +""
168
168
  end
169
169
 
170
170
  # Converts the first character in the string to lowercase.
@@ -173,7 +173,7 @@ module ActiveSupport
173
173
  # downcase_first('I') # => "i"
174
174
  # downcase_first('') # => ""
175
175
  def downcase_first(string)
176
- string.length > 0 ? string[0].downcase.concat(string[1..-1]) : ""
176
+ string.length > 0 ? string[0].downcase.concat(string[1..-1]) : +""
177
177
  end
178
178
 
179
179
  # Capitalizes all the words and replaces some characters in the string to
@@ -86,6 +86,12 @@ module ActiveSupport
86
86
  mattr_accessor :colorize_logging, default: true
87
87
  class_attribute :log_levels, instance_accessor: false, default: {} # :nodoc:
88
88
 
89
+ LEVEL_CHECKS = {
90
+ debug: -> (logger) { !logger.debug? },
91
+ info: -> (logger) { !logger.info? },
92
+ error: -> (logger) { !logger.error? },
93
+ }
94
+
89
95
  class << self
90
96
  def logger
91
97
  @logger ||= if defined?(Rails) && Rails.respond_to?(:logger)
@@ -122,7 +128,7 @@ module ActiveSupport
122
128
  end
123
129
 
124
130
  def subscribe_log_level(method, level)
125
- self.log_levels = log_levels.merge(method => ::Logger.const_get(level.upcase))
131
+ self.log_levels = log_levels.merge(method => LEVEL_CHECKS.fetch(level))
126
132
  set_event_levels
127
133
  end
128
134
  end
@@ -137,7 +143,7 @@ module ActiveSupport
137
143
  end
138
144
 
139
145
  def silenced?(event)
140
- logger.nil? || logger.level > @event_levels.fetch(event, Float::INFINITY)
146
+ logger.nil? || @event_levels[event]&.call(logger)
141
147
  end
142
148
 
143
149
  def call(event)
@@ -130,6 +130,7 @@ module ActiveSupport
130
130
  # indicate whether old option sets are still in use or can be removed from
131
131
  # rotation.
132
132
 
133
+ ##
133
134
  private
134
135
  def build(salt, secret_generator:, secret_generator_options:, **options)
135
136
  secret_length = MessageEncryptor.key_len(*options[:cipher])
@@ -126,6 +126,7 @@ module ActiveSupport
126
126
  # indicate whether old option sets are still in use or can be removed from
127
127
  # rotation.
128
128
 
129
+ ##
129
130
  private
130
131
  def build(salt, secret_generator:, secret_generator_options:, **options)
131
132
  MessageVerifier.new(secret_generator.call(salt, **secret_generator_options), **options)
@@ -82,7 +82,7 @@ module ActiveSupport
82
82
  throw :invalid_message_content, "expired"
83
83
  end
84
84
 
85
- if hash["pur"] != purpose&.to_s
85
+ if hash["pur"].to_s != purpose.to_s
86
86
  throw :invalid_message_content, "mismatched purpose"
87
87
  end
88
88
 
@@ -18,26 +18,30 @@ module ActiveSupport
18
18
  end
19
19
 
20
20
  module FanoutIteration # :nodoc:
21
- def iterate_guarding_exceptions(listeners)
22
- exceptions = nil
23
-
24
- listeners.each do |s|
25
- yield s
26
- rescue Exception => e
27
- exceptions ||= []
28
- exceptions << e
29
- end
21
+ private
22
+ def iterate_guarding_exceptions(collection)
23
+ exceptions = nil
30
24
 
31
- if exceptions
32
- if exceptions.size == 1
33
- raise exceptions.first
34
- else
35
- raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
25
+ collection.each do |s|
26
+ yield s
27
+ rescue Exception => e
28
+ exceptions ||= []
29
+ exceptions << e
36
30
  end
37
- end
38
31
 
39
- listeners
40
- end
32
+ if exceptions
33
+ exceptions = exceptions.flat_map do |exception|
34
+ exception.is_a?(InstrumentationSubscriberError) ? exception.exceptions : [exception]
35
+ end
36
+ if exceptions.size == 1
37
+ raise exceptions.first
38
+ else
39
+ raise InstrumentationSubscriberError.new(exceptions), cause: exceptions.first
40
+ end
41
+ end
42
+
43
+ collection
44
+ end
41
45
  end
42
46
 
43
47
  # This is a default queue implementation that ships with Notifications.
@@ -225,6 +229,8 @@ module ActiveSupport
225
229
  # handle.finish
226
230
  # end
227
231
  class Handle
232
+ include FanoutIteration
233
+
228
234
  def initialize(notifier, name, id, payload) # :nodoc:
229
235
  @name = name
230
236
  @id = id
@@ -239,7 +245,7 @@ module ActiveSupport
239
245
  ensure_state! :initialized
240
246
  @state = :started
241
247
 
242
- @groups.each do |group|
248
+ iterate_guarding_exceptions(@groups) do |group|
243
249
  group.start(@name, @id, @payload)
244
250
  end
245
251
  end
@@ -252,7 +258,7 @@ module ActiveSupport
252
258
  ensure_state! :started
253
259
  @state = :finished
254
260
 
255
- @groups.each do |group|
261
+ iterate_guarding_exceptions(@groups) do |group|
256
262
  group.finish(name, id, payload)
257
263
  end
258
264
  end
@@ -65,16 +65,16 @@ module ActiveSupport
65
65
  end
66
66
  end
67
67
 
68
- # Returns a "handle" for an event with the given +name+ and +payload+
68
+ # Returns a "handle" for an event with the given +name+ and +payload+.
69
69
  #
70
- # +#start+ and +#finish+ must each be called exactly once on the returned object.
70
+ # #start and #finish must each be called exactly once on the returned object.
71
71
  #
72
- # Where possible, it's best to use +#instrument+, which will record the
72
+ # Where possible, it's best to use #instrument, which will record the
73
73
  # start and finish of the event and correctly handle any exceptions.
74
74
  # +build_handle+ is a low-level API intended for cases where using
75
- # +#instrument+ isn't possible.
75
+ # +instrument+ isn't possible.
76
76
  #
77
- # See ActiveSupport::Notifications::Fanout::Handle
77
+ # See ActiveSupport::Notifications::Fanout::Handle.
78
78
  def build_handle(name, payload)
79
79
  @notifier.build_handle(name, @id, payload)
80
80
  end
@@ -146,21 +146,21 @@ module ActiveSupport
146
146
  @allocation_count_finish = now_allocations
147
147
  end
148
148
 
149
- # Returns the CPU time (in milliseconds) passed since the call to
150
- # +start!+ and the call to +finish!+
149
+ # Returns the CPU time (in milliseconds) passed between the call to
150
+ # #start! and the call to #finish!.
151
151
  def cpu_time
152
152
  @cpu_time_finish - @cpu_time_start
153
153
  end
154
154
 
155
- # Returns the idle time time (in milliseconds) passed since the call to
156
- # +start!+ and the call to +finish!+
155
+ # Returns the idle time time (in milliseconds) passed between the call to
156
+ # #start! and the call to #finish!.
157
157
  def idle_time
158
158
  diff = duration - cpu_time
159
159
  diff > 0.0 ? diff : 0.0
160
160
  end
161
161
 
162
- # Returns the number of allocations made since the call to +start!+ and
163
- # the call to +finish!+
162
+ # Returns the number of allocations made between the call to #start! and
163
+ # the call to #finish!.
164
164
  def allocations
165
165
  @allocation_count_finish - @allocation_count_start
166
166
  end
@@ -179,8 +179,10 @@ module ActiveSupport
179
179
  case number
180
180
  when Float, Rational
181
181
  number.to_d(0)
182
- else
182
+ when String
183
183
  BigDecimal(number, exception: false)
184
+ else
185
+ number.to_d rescue nil
184
186
  end
185
187
  end
186
188
  end
@@ -43,13 +43,13 @@ module ActiveSupport
43
43
 
44
44
  def exponent
45
45
  max = STORAGE_UNITS.size - 1
46
- exp = (Math.log(number) / Math.log(base)).to_i
46
+ exp = (Math.log(number.abs) / Math.log(base)).to_i
47
47
  exp = max if exp > max # avoid overflow for the highest unit
48
48
  exp
49
49
  end
50
50
 
51
51
  def smaller_than_base?
52
- number.to_i < base
52
+ number.to_i.abs < base
53
53
  end
54
54
 
55
55
  def base
@@ -42,8 +42,8 @@ module ActiveSupport
42
42
  super(key.to_sym)
43
43
  end
44
44
 
45
- def dig(*keys)
46
- super(*keys.flatten.map(&:to_sym))
45
+ def dig(key, *identifiers)
46
+ super(key.to_sym, *identifiers)
47
47
  end
48
48
 
49
49
  def method_missing(name, *args)
@@ -17,6 +17,7 @@ module ActiveSupport
17
17
  SUPPRESSED_WARNINGS = Regexp.union(
18
18
  # TODO: remove if https://github.com/mikel/mail/pull/1557 or similar fix
19
19
  %r{/lib/mail/parsers/.*statement not reached},
20
+ %r{/lib/mail/parsers/.*assigned but unused variable - disp_type_s},
20
21
  %r{/lib/mail/parsers/.*assigned but unused variable - testEof}
21
22
  )
22
23
 
@@ -25,7 +25,7 @@ module ActiveSupport
25
25
  unstub_object(stub)
26
26
  end
27
27
 
28
- new_name = "__simple_stub__#{method_name}"
28
+ new_name = "__simple_stub__#{method_name}__#{object_id}"
29
29
 
30
30
  @stubs[object.object_id][method_name] = Stub.new(object, method_name, new_name)
31
31
 
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.1.0
4
+ version: 7.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-05 00:00:00.000000000 Z
11
+ date: 2023-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -446,10 +446,10 @@ licenses:
446
446
  - MIT
447
447
  metadata:
448
448
  bug_tracker_uri: https://github.com/rails/rails/issues
449
- changelog_uri: https://github.com/rails/rails/blob/v7.1.0/activesupport/CHANGELOG.md
450
- documentation_uri: https://api.rubyonrails.org/v7.1.0/
449
+ changelog_uri: https://github.com/rails/rails/blob/v7.1.2/activesupport/CHANGELOG.md
450
+ documentation_uri: https://api.rubyonrails.org/v7.1.2/
451
451
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
452
- source_code_uri: https://github.com/rails/rails/tree/v7.1.0/activesupport
452
+ source_code_uri: https://github.com/rails/rails/tree/v7.1.2/activesupport
453
453
  rubygems_mfa_required: 'true'
454
454
  post_install_message:
455
455
  rdoc_options: