activesupport 8.0.2.1 → 8.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51325bdd043f3ecb40d38809265f43b19559bea29ca3d72402fb421e25be1653
4
- data.tar.gz: 81ac7af00f2f59da740adb21e77528f653dd0149120dfc0bc163b77eacb500ef
3
+ metadata.gz: b2cdf9736ba5b94841913f95e5e83f01cc82becad9006ed2d75e18f1cc117c43
4
+ data.tar.gz: 7f8f545780e7ecfece1bdd6af006e504d91e89ad019a1020cd43014a296e7def
5
5
  SHA512:
6
- metadata.gz: abf1f60ae3cfa4e2fb533a4583f2c310e43801e783983b7908913243ddd065ae45f44a4eeffe41fec82ab553097ff5c5c8d9c413580b577c3e1181a8255f5737
7
- data.tar.gz: 1effa0559c4730a836774d42fb9b871ebdd95f25161988fa9afd7b30700debbf48d1f705d8434a40b032e2a772bca7ad4e3351f7cb64b48ff912843fcdc43ecf
6
+ metadata.gz: b23ff4bae6aae8f2e154b478c8f36825d0179152a0591591c240ea2b8d6104dcad0b70598a9b42888ab41448a06bc6b69c4ca537c1e5edb386fa81408f25acde
7
+ data.tar.gz: 84fa236c5c86feff0ae2283888ca7266c5f5d9090474b099c2b9142c082e107062bea571d8197f0138a022d7db7fe08e02f3544ade6cd611de68c35b26b90e75
data/CHANGELOG.md CHANGED
@@ -1,9 +1,58 @@
1
- ## Rails 8.0.2.1 (August 13, 2025) ##
1
+ ## Rails 8.0.3 (September 22, 2025) ##
2
2
 
3
- * No changes.
3
+ * `ActiveSupport::FileUpdateChecker` does not depend on `Time.now` to prevent unnecessary reloads with time travel test helpers
4
4
 
5
+ *Jan Grodowski*
5
6
 
6
- ## Rails 8.0.2 (March 12, 2025) ##
7
+ * Fix `ActiveSupport::BroadcastLogger` from executing a block argument for each logger (tagged, info, etc.).
8
+
9
+ *Jared Armstrong*
10
+
11
+ * Make `ActiveSupport::Logger` `#freeze`-friendly.
12
+
13
+ *Joshua Young*
14
+
15
+ * Fix `ActiveSupport::HashWithIndifferentAccess#transform_keys!` removing defaults.
16
+
17
+ *Hartley McGuire*
18
+
19
+ * Fix `ActiveSupport::HashWithIndifferentAccess#tranform_keys!` to handle collisions.
20
+
21
+ If the transformation would result in a key equal to another not yet transformed one,
22
+ it would result in keys being lost.
23
+
24
+ Before:
25
+
26
+ ```ruby
27
+ >> {a: 1, b: 2}.with_indifferent_access.transform_keys!(&:succ)
28
+ => {"c" => 1}
29
+ ```
30
+
31
+ After:
32
+
33
+ ```ruby
34
+ >> {a: 1, b: 2}.with_indifferent_access.transform_keys!(&:succ)
35
+ => {"c" => 1, "d" => 2}
36
+ ```
37
+
38
+ *Jason T Johnson*, *Jean Boussier*
39
+
40
+ * Fix `ActiveSupport::Cache::MemCacheStore#read_multi` to handle network errors.
41
+
42
+ This method specifically wasn't handling network errors like other codepaths.
43
+
44
+ *Alessandro Dal Grande*
45
+
46
+ * Fix configuring `RedisCacheStore` with `raw: true`.
47
+
48
+ *fatkodima*
49
+
50
+ * Fix `Enumerable#sole` for infinite collections.
51
+
52
+ *fatkodima*
53
+
54
+
55
+ ## Rails 8.0.2.1 (August 13, 2025) ##
7
56
 
8
57
  * No changes.
9
58
 
data/README.rdoc CHANGED
@@ -35,6 +35,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
35
35
 
36
36
  * https://github.com/rails/rails/issues
37
37
 
38
- Feature requests should be discussed on the rails-core mailing list here:
38
+ Feature requests should be discussed on the rubyonrails-core forum here:
39
39
 
40
40
  * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -76,7 +76,6 @@ module ActiveSupport
76
76
 
77
77
  # Returns all the logger that are part of this broadcast.
78
78
  attr_reader :broadcasts
79
- attr_reader :formatter
80
79
  attr_accessor :progname
81
80
 
82
81
  def initialize(*loggers)
@@ -105,62 +104,36 @@ module ActiveSupport
105
104
  @broadcasts.delete(logger)
106
105
  end
107
106
 
108
- def level
109
- @broadcasts.map(&:level).min
110
- end
111
-
112
- def <<(message)
113
- dispatch { |logger| logger.<<(message) }
114
- end
115
-
116
- def add(...)
117
- dispatch { |logger| logger.add(...) }
118
- end
119
- alias_method :log, :add
120
-
121
- def debug(...)
122
- dispatch { |logger| logger.debug(...) }
123
- end
124
-
125
- def info(...)
126
- dispatch { |logger| logger.info(...) }
127
- end
128
-
129
- def warn(...)
130
- dispatch { |logger| logger.warn(...) }
131
- end
132
-
133
- def error(...)
134
- dispatch { |logger| logger.error(...) }
135
- end
136
-
137
- def fatal(...)
138
- dispatch { |logger| logger.fatal(...) }
139
- end
140
-
141
- def unknown(...)
142
- dispatch { |logger| logger.unknown(...) }
107
+ def local_level=(level)
108
+ @broadcasts.each do |logger|
109
+ logger.local_level = level if logger.respond_to?(:local_level=)
110
+ end
143
111
  end
144
112
 
145
- def formatter=(formatter)
146
- dispatch { |logger| logger.formatter = formatter }
147
-
148
- @formatter = formatter
149
- end
113
+ def local_level
114
+ loggers = @broadcasts.select { |logger| logger.respond_to?(:local_level) }
150
115
 
151
- def level=(level)
152
- dispatch { |logger| logger.level = level }
116
+ loggers.map do |logger|
117
+ logger.local_level
118
+ end.first
153
119
  end
154
- alias_method :sev_threshold=, :level=
155
120
 
156
- def local_level=(level)
157
- dispatch do |logger|
158
- logger.local_level = level if logger.respond_to?(:local_level=)
159
- end
121
+ LOGGER_METHODS = %w[
122
+ << log add debug info warn error fatal unknown
123
+ level= sev_threshold= close
124
+ formatter formatter=
125
+ ] # :nodoc:
126
+ LOGGER_METHODS.each do |method|
127
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
128
+ def #{method}(...)
129
+ dispatch(:#{method}, ...)
130
+ end
131
+ RUBY
160
132
  end
161
133
 
162
- def close
163
- dispatch { |logger| logger.close }
134
+ # Returns the lowest level of all the loggers in the broadcast.
135
+ def level
136
+ @broadcasts.map(&:level).min
164
137
  end
165
138
 
166
139
  # True if the log level allows entries with severity +Logger::DEBUG+ to be written
@@ -171,7 +144,7 @@ module ActiveSupport
171
144
 
172
145
  # Sets the log level to +Logger::DEBUG+ for the whole broadcast.
173
146
  def debug!
174
- dispatch { |logger| logger.debug! }
147
+ dispatch(:debug!)
175
148
  end
176
149
 
177
150
  # True if the log level allows entries with severity +Logger::INFO+ to be written
@@ -182,7 +155,7 @@ module ActiveSupport
182
155
 
183
156
  # Sets the log level to +Logger::INFO+ for the whole broadcast.
184
157
  def info!
185
- dispatch { |logger| logger.info! }
158
+ dispatch(:info!)
186
159
  end
187
160
 
188
161
  # True if the log level allows entries with severity +Logger::WARN+ to be written
@@ -193,7 +166,7 @@ module ActiveSupport
193
166
 
194
167
  # Sets the log level to +Logger::WARN+ for the whole broadcast.
195
168
  def warn!
196
- dispatch { |logger| logger.warn! }
169
+ dispatch(:warn!)
197
170
  end
198
171
 
199
172
  # True if the log level allows entries with severity +Logger::ERROR+ to be written
@@ -204,7 +177,7 @@ module ActiveSupport
204
177
 
205
178
  # Sets the log level to +Logger::ERROR+ for the whole broadcast.
206
179
  def error!
207
- dispatch { |logger| logger.error! }
180
+ dispatch(:error!)
208
181
  end
209
182
 
210
183
  # True if the log level allows entries with severity +Logger::FATAL+ to be written
@@ -215,21 +188,35 @@ module ActiveSupport
215
188
 
216
189
  # Sets the log level to +Logger::FATAL+ for the whole broadcast.
217
190
  def fatal!
218
- dispatch { |logger| logger.fatal! }
191
+ dispatch(:fatal!)
219
192
  end
220
193
 
221
194
  def initialize_copy(other)
222
195
  @broadcasts = []
223
196
  @progname = other.progname.dup
224
- @formatter = other.formatter.dup
225
197
 
226
198
  broadcast_to(*other.broadcasts.map(&:dup))
227
199
  end
228
200
 
229
201
  private
230
- def dispatch(&block)
231
- @broadcasts.each { |logger| block.call(logger) }
232
- true
202
+ def dispatch(method, *args, **kwargs, &block)
203
+ if block_given?
204
+ # Maintain semantics that the first logger yields the block
205
+ # as normal, but subsequent loggers won't re-execute the block.
206
+ # Instead, the initial result is immediately returned.
207
+ called, result = false, nil
208
+ block = proc { |*args, **kwargs|
209
+ if called then result
210
+ else
211
+ called = true
212
+ result = yield(*args, **kwargs)
213
+ end
214
+ }
215
+ end
216
+
217
+ @broadcasts.map { |logger|
218
+ logger.send(method, *args, **kwargs, &block)
219
+ }.first
233
220
  end
234
221
 
235
222
  def method_missing(name, ...)
@@ -212,26 +212,24 @@ module ActiveSupport
212
212
  def read_multi_entries(names, **options)
213
213
  keys_to_names = names.index_by { |name| normalize_key(name, options) }
214
214
 
215
- raw_values = begin
216
- @data.with { |c| c.get_multi(keys_to_names.keys) }
217
- rescue Dalli::UnmarshalError
218
- {}
219
- end
215
+ rescue_error_with({}) do
216
+ raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
220
217
 
221
- values = {}
218
+ values = {}
222
219
 
223
- raw_values.each do |key, value|
224
- entry = deserialize_entry(value, raw: options[:raw])
220
+ raw_values.each do |key, value|
221
+ entry = deserialize_entry(value, raw: options[:raw])
225
222
 
226
- unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
227
- begin
228
- values[keys_to_names[key]] = entry.value
229
- rescue DeserializationError
223
+ unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
224
+ begin
225
+ values[keys_to_names[key]] = entry.value
226
+ rescue DeserializationError
227
+ end
230
228
  end
231
229
  end
232
- end
233
230
 
234
- values
231
+ values
232
+ end
235
233
  end
236
234
 
237
235
  # Delete an entry from the cache.
@@ -35,6 +35,7 @@ module ActiveSupport
35
35
  :race_condition_ttl,
36
36
  :serializer,
37
37
  :skip_nil,
38
+ :raw,
38
39
  ]
39
40
 
40
41
  # Mapping of canonical option names to aliases that a store will recognize.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "benchmark"
4
+ return if Benchmark.respond_to?(:ms)
4
5
 
5
6
  class << Benchmark
6
7
  def ms(&block) # :nodoc
@@ -11,7 +11,8 @@ class DateTime
11
11
  #
12
12
  # This method is aliased to <tt>to_formatted_s</tt>.
13
13
  #
14
- # === Examples
14
+ # ==== Examples
15
+ #
15
16
  # datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
16
17
  #
17
18
  # datetime.to_fs(:db) # => "2007-12-04 00:00:00"
@@ -23,7 +24,8 @@ class DateTime
23
24
  # datetime.to_fs(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
24
25
  # datetime.to_fs(:iso8601) # => "2007-12-04T00:00:00+00:00"
25
26
  #
26
- # == Adding your own datetime formats to to_fs
27
+ # ==== Adding your own datetime formats to +to_fs+
28
+ #
27
29
  # DateTime formats are shared with Time. You can add your own to the
28
30
  # Time::DATE_FORMATS hash. Use the format name as the hash key and
29
31
  # either a strftime string or Proc instance that takes a time or
@@ -209,10 +209,22 @@ module Enumerable
209
209
  # Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
210
210
  # { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
211
211
  def sole
212
- case count
213
- when 1 then return first # rubocop:disable Style/RedundantReturn
214
- when 0 then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "no item found"
215
- when 2.. then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "multiple items found"
212
+ result = nil
213
+ found = false
214
+
215
+ each do |element|
216
+ if found
217
+ raise SoleItemExpectedError, "multiple items found"
218
+ end
219
+
220
+ result = element
221
+ found = true
222
+ end
223
+
224
+ if found
225
+ result
226
+ else
227
+ raise SoleItemExpectedError, "no item found"
216
228
  end
217
229
  end
218
230
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "cgi"
3
+ require "cgi/escape"
4
+ require "cgi/util" if RUBY_VERSION < "3.5"
4
5
 
5
6
  class Object
6
7
  # Alias of <tt>to_s</tt>.
@@ -145,14 +145,14 @@ class NilClass
145
145
  #
146
146
  # With +try+
147
147
  # @person.try(:children).try(:first).try(:name)
148
- def try(*)
148
+ def try(*, &)
149
149
  nil
150
150
  end
151
151
 
152
152
  # Calling +try!+ on +nil+ always returns +nil+.
153
153
  #
154
154
  # nil.try!(:name) # => nil
155
- def try!(*)
155
+ def try!(*, &)
156
156
  nil
157
157
  end
158
158
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Range
4
- # Compare two ranges and see if they overlap each other
5
- # (1..5).overlap?(4..6) # => true
6
- # (1..5).overlap?(7..9) # => false
7
4
  unless Range.method_defined?(:overlap?) # Ruby 3.3+
5
+ # Compare two ranges and see if they overlap each other
6
+ # (1..5).overlap?(4..6) # => true
7
+ # (1..5).overlap?(7..9) # => false
8
8
  def overlap?(other)
9
9
  raise TypeError unless other.is_a? Range
10
10
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Range
4
+ # Returns the sole item in the range. If there are no items, or more
5
+ # than one item, raises Enumerable::SoleItemExpectedError.
6
+ #
7
+ # (1..1).sole # => 1
8
+ # (2..1).sole # => Enumerable::SoleItemExpectedError: no item found
9
+ # (..1).sole # => Enumerable::SoleItemExpectedError: infinite range cannot represent a sole item
10
+ def sole
11
+ if self.begin.nil? || self.end.nil?
12
+ raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "infinite range '#{inspect}' cannot represent a sole item"
13
+ end
14
+
15
+ super
16
+ end
17
+ end
@@ -4,3 +4,4 @@ require "active_support/core_ext/range/conversions"
4
4
  require "active_support/core_ext/range/compare_range"
5
5
  require "active_support/core_ext/range/overlap"
6
6
  require "active_support/core_ext/range/each"
7
+ require "active_support/core_ext/range/sole"
@@ -88,11 +88,11 @@ class String
88
88
  # characters.
89
89
  #
90
90
  # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".size
91
- # => 20
91
+ # # => 20
92
92
  # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".bytesize
93
- # => 80
93
+ # # => 80
94
94
  # >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".truncate_bytes(20)
95
- # => "🔪🔪🔪🔪…"
95
+ # # => "🔪🔪🔪🔪…"
96
96
  #
97
97
  # The truncated text ends with the <tt>:omission</tt> string, defaulting
98
98
  # to "…", for a total length not exceeding <tt>truncate_to</tt>.
@@ -12,12 +12,12 @@ class String
12
12
  # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
13
13
  #
14
14
  # >> "lj".mb_chars.upcase.to_s
15
- # => "LJ"
15
+ # # => "LJ"
16
16
  #
17
17
  # NOTE: Ruby 2.4 and later support native Unicode case mappings:
18
18
  #
19
19
  # >> "lj".upcase
20
- # => "LJ"
20
+ # # => "LJ"
21
21
  #
22
22
  # == \Method chaining
23
23
  #
@@ -108,15 +108,18 @@ module ActiveSupport
108
108
  # ==== Options
109
109
  #
110
110
  # * <tt>:default</tt> - The default value for the attributes. If the value
111
- # is a proc or lambda, it will be called whenever an instance is
112
- # constructed. Otherwise, the value will be duplicated with +#dup+.
113
- # Default values are re-assigned when the attributes are reset.
111
+ # is a proc or lambda, it will be called whenever an instance is
112
+ # constructed. Otherwise, the value will be duplicated with +#dup+.
113
+ # Default values are re-assigned when the attributes are reset.
114
114
  def attribute(*names, default: NOT_SET)
115
115
  invalid_attribute_names = names.map(&:to_sym) & INVALID_ATTRIBUTE_NAMES
116
116
  if invalid_attribute_names.any?
117
117
  raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}"
118
118
  end
119
119
 
120
+ Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
121
+ Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
122
+
120
123
  ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
121
124
  names.each do |name|
122
125
  owner.define_cached_method(name, namespace: :current_attributes) do |batch|
@@ -134,9 +137,6 @@ module ActiveSupport
134
137
  end
135
138
  end
136
139
 
137
- Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
138
- Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
139
-
140
140
  self.defaults = defaults.merge(names.index_with { default })
141
141
  end
142
142
 
@@ -185,9 +185,16 @@ module ActiveSupport
185
185
 
186
186
  def method_added(name)
187
187
  super
188
+
189
+ # We try to generate instance delegators early to not rely on method_missing.
188
190
  return if name == :initialize
191
+
192
+ # If the added method isn't public, we don't delegate it.
189
193
  return unless public_method_defined?(name)
194
+
195
+ # If we already have a class method by that name, we don't override it.
190
196
  return if singleton_class.method_defined?(name) || singleton_class.private_method_defined?(name)
197
+
191
198
  Delegation.generate(singleton_class, [name], to: :instance, as: self, nilable: false)
192
199
  end
193
200
  end
@@ -120,7 +120,7 @@ module ActiveSupport
120
120
  # healthy to consider this edge case because with mtimes in the future
121
121
  # reloading is not triggered.
122
122
  def max_mtime(paths)
123
- time_now = Time.now
123
+ time_now = Time.at(0, Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond), :nanosecond)
124
124
  max_mtime = nil
125
125
 
126
126
  # Time comparisons are performed with #compare_without_coercion because
@@ -9,8 +9,8 @@ module ActiveSupport
9
9
  module VERSION
10
10
  MAJOR = 8
11
11
  MINOR = 0
12
- TINY = 2
13
- PRE = "1"
12
+ TINY = 3
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -262,9 +262,7 @@ module ActiveSupport
262
262
  # hash[:a][:c] # => "c"
263
263
  # dup[:a][:c] # => "c"
264
264
  def dup
265
- self.class.new(self).tap do |new_hash|
266
- set_defaults(new_hash)
267
- end
265
+ copy_defaults(self.class.new(self))
268
266
  end
269
267
 
270
268
  # This method has the same semantics of +update+, except it does not
@@ -338,21 +336,26 @@ module ActiveSupport
338
336
  NOT_GIVEN = Object.new # :nodoc:
339
337
 
340
338
  def transform_keys(hash = NOT_GIVEN, &block)
341
- return to_enum(:transform_keys) if NOT_GIVEN.equal?(hash) && !block_given?
342
- dup.tap { |h| h.transform_keys!(hash, &block) }
339
+ if NOT_GIVEN.equal?(hash)
340
+ if block_given?
341
+ self.class.new(super(&block))
342
+ else
343
+ to_enum(:transform_keys)
344
+ end
345
+ else
346
+ self.class.new(super)
347
+ end
343
348
  end
344
349
 
345
350
  def transform_keys!(hash = NOT_GIVEN, &block)
346
- return to_enum(:transform_keys!) if NOT_GIVEN.equal?(hash) && !block_given?
347
-
348
- if hash.nil?
349
- super
350
- elsif NOT_GIVEN.equal?(hash)
351
- keys.each { |key| self[yield(key)] = delete(key) }
352
- elsif block_given?
353
- keys.each { |key| self[hash[key] || yield(key)] = delete(key) }
351
+ if NOT_GIVEN.equal?(hash)
352
+ if block_given?
353
+ replace(copy_defaults(transform_keys(&block)))
354
+ else
355
+ return to_enum(:transform_keys!)
356
+ end
354
357
  else
355
- keys.each { |key| self[hash[key] || key] = delete(key) }
358
+ replace(copy_defaults(transform_keys(hash, &block)))
356
359
  end
357
360
 
358
361
  self
@@ -376,8 +379,7 @@ module ActiveSupport
376
379
  def to_hash
377
380
  copy = Hash[self]
378
381
  copy.transform_values! { |v| convert_value_to_hash(v) }
379
- set_defaults(copy)
380
- copy
382
+ copy_defaults(copy)
381
383
  end
382
384
 
383
385
  def to_proc
@@ -413,12 +415,13 @@ module ActiveSupport
413
415
  end
414
416
 
415
417
 
416
- def set_defaults(target)
418
+ def copy_defaults(target)
417
419
  if default_proc
418
420
  target.default_proc = default_proc.dup
419
421
  else
420
422
  target.default = default
421
423
  end
424
+ target
422
425
  end
423
426
 
424
427
  def update_with_single_argument(other_hash, block)
@@ -14,11 +14,13 @@ module ActiveSupport
14
14
  DATETIME_REGEX = /\A(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)\z/
15
15
 
16
16
  class << self
17
- # Parses a JSON string (JavaScript Object Notation) into a hash.
17
+ # Parses a JSON string (JavaScript Object Notation) into a Ruby object.
18
18
  # See http://www.json.org for more info.
19
19
  #
20
20
  # ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
21
- # => {"team" => "rails", "players" => "36"}
21
+ # # => {"team" => "rails", "players" => "36"}
22
+ # ActiveSupport::JSON.decode("2.39")
23
+ # # => 2.39
22
24
  def decode(json)
23
25
  data = ::JSON.parse(json, quirks_mode: true)
24
26
 
@@ -53,7 +53,7 @@ module ActiveSupport
53
53
  # loaded. If the component has already loaded, the block is executed
54
54
  # immediately.
55
55
  #
56
- # Options:
56
+ # ==== Options
57
57
  #
58
58
  # * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+.
59
59
  # * <tt>:run_once</tt> - Given +block+ will run only once.
@@ -7,6 +7,11 @@ module ActiveSupport
7
7
  module LoggerThreadSafeLevel # :nodoc:
8
8
  extend ActiveSupport::Concern
9
9
 
10
+ def initialize(...)
11
+ super
12
+ @local_level_key = :"logger_thread_safe_level_#{object_id}"
13
+ end
14
+
10
15
  def local_level
11
16
  IsolatedExecutionState[local_level_key]
12
17
  end
@@ -40,8 +45,6 @@ module ActiveSupport
40
45
  end
41
46
 
42
47
  private
43
- def local_level_key
44
- @local_level_key ||= :"logger_thread_safe_level_#{object_id}"
45
- end
48
+ attr_reader :local_level_key
46
49
  end
47
50
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activesupport
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.2.1
4
+ version: 8.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -329,6 +329,7 @@ files:
329
329
  - lib/active_support/core_ext/range/conversions.rb
330
330
  - lib/active_support/core_ext/range/each.rb
331
331
  - lib/active_support/core_ext/range/overlap.rb
332
+ - lib/active_support/core_ext/range/sole.rb
332
333
  - lib/active_support/core_ext/regexp.rb
333
334
  - lib/active_support/core_ext/securerandom.rb
334
335
  - lib/active_support/core_ext/string.rb
@@ -493,10 +494,10 @@ licenses:
493
494
  - MIT
494
495
  metadata:
495
496
  bug_tracker_uri: https://github.com/rails/rails/issues
496
- changelog_uri: https://github.com/rails/rails/blob/v8.0.2.1/activesupport/CHANGELOG.md
497
- documentation_uri: https://api.rubyonrails.org/v8.0.2.1/
497
+ changelog_uri: https://github.com/rails/rails/blob/v8.0.3/activesupport/CHANGELOG.md
498
+ documentation_uri: https://api.rubyonrails.org/v8.0.3/
498
499
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
499
- source_code_uri: https://github.com/rails/rails/tree/v8.0.2.1/activesupport
500
+ source_code_uri: https://github.com/rails/rails/tree/v8.0.3/activesupport
500
501
  rubygems_mfa_required: 'true'
501
502
  rdoc_options:
502
503
  - "--encoding"