activesupport 7.0.8.7 → 7.0.9

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: 1ff51c87fea6c6a84f655165ba9fc10f5a411a7614d372c522054bee0724df3d
4
- data.tar.gz: 9222bf9f5da9f347676e425d8fe66d6a811737cb3db27af1863621e78ac0add5
3
+ metadata.gz: 016e2913f33ba54595a5a6760dc3d228b75dba7a5ea6019bf3633ed1398c3554
4
+ data.tar.gz: 69361bd65952fd588551a2f2b2031cad8aa3b4834a05a08958dc25f7cdd15168
5
5
  SHA512:
6
- metadata.gz: a96190bb17ae2cf6af9f30fb534c08fbc330238bfbf0813d74832cbe49de7cbd5a32f8a24423aa4de9e34446f2f79ca07c881c47c4dd82859a74454ac4c6c244
7
- data.tar.gz: e06a3eacad28328935a57fd24da6cfc63d2b39cd3e5a5e519cc5723835cb5eff70fb9bc8908899f478202e6e4b367ae25ecfeb128aafd98bad0900b9324ba4fc
6
+ metadata.gz: 764c23f42df81f5231a69341f5f3fae03c8da75515f653ad4aadba0766b2047883a88b2566ffb7e1cb2eb53a532eb169083a6e911907e9572a8af36ce31dcc88
7
+ data.tar.gz: 298e9dc61c2c1324d81d5b3a2f6fe5f7565d32fb2c7b697b9dca48cba7dad72b1351b97e0bf42463fa93b6ba234f0ce5e9dcdc6b764c84b0662b6e45b594effc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,64 @@
1
+ ## Rails 7.0.9 (October 28, 2025) ##
2
+
3
+ * Fix `ActiveSupport::Notifications.publish_event` to preserve units.
4
+
5
+ This solves the incorrect reporting of time spent running Active Record
6
+ asynchronous queries (by a factor `1000`).
7
+
8
+ *Jean Boussier*
9
+
10
+ * Fix ActiveSupport::Deprecation to handle blaming generated code
11
+
12
+ *Jean Boussier*, *fatkodima*
13
+
14
+ * Fix `#to_fs(:human_size)` to correctly work with negative numbers.
15
+
16
+ *Earlopain*
17
+
18
+ * Add `bigdecimal` as Active Support dependency that is a bundled gem candidate for Ruby 3.4.
19
+
20
+ `bigdecimal` 3.1.4 or higher version will be installed.
21
+ Ruby 2.7 and 3.0 users who want `bigdecimal` version 2.0.0 or 3.0.0 behavior as a default gem,
22
+ pin the `bigdecimal` version in your application Gemfile.
23
+
24
+ *Koichi ITO*
25
+
26
+ * Ensure `{down,up}case_first` returns non-frozen string.
27
+
28
+ *Jonathan Hefner*
29
+
30
+ * Add `drb`, `mutex_m` and `base64` that are bundled gem candidates for Ruby 3.4
31
+
32
+ *Yasuo Honda*
33
+
34
+ * Fix `delete_matched` for file cache store to work with keys longer than the
35
+ max filename size.
36
+
37
+ *fatkodima* and *Jonathan Hefner*
38
+
39
+ * Fix MemoryStore to prevent race conditions when incrementing or decrementing.
40
+
41
+ *Pierre Jambet*
42
+
43
+ * Fix MemoryStore to preserve entries TTL when incrementing or decrementing
44
+
45
+ This is to be more consistent with how MemCachedStore and RedisCacheStore behaves.
46
+
47
+ *Jean Boussier*
48
+
49
+ * NumberHelper: handle objects responding to_d.
50
+
51
+ *fatkodima*
52
+
53
+ * NumberHelper: handle very large numbers.
54
+
55
+ *Alex Ghiculescu*, *fatkodima*
56
+
57
+ * Fix Range#overlaps? not taking empty ranges into account on Ruby < 3.3
58
+
59
+ *Nobuyoshi Nakada*, *Shouichi Kamiya*, *Hartley McGuire*
60
+
61
+
1
62
  ## Rails 7.0.8.7 (December 10, 2024) ##
2
63
 
3
64
  * No changes.
@@ -152,7 +152,7 @@ module ActiveSupport
152
152
 
153
153
  # Translate a file path into a key.
154
154
  def file_path_key(path)
155
- fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
155
+ fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last.delete(File::SEPARATOR)
156
156
  URI.decode_www_form_component(fname, Encoding::UTF_8)
157
157
  end
158
158
 
@@ -190,10 +190,12 @@ module ActiveSupport
190
190
 
191
191
  def modify_value(name, amount, options)
192
192
  options = merged_options(options)
193
+
193
194
  synchronize do
194
- if num = read(name, options)
195
- num = num.to_i + amount
196
- write(name, num, options)
195
+ if entry = read_entry(name, **options)
196
+ num = entry.value.to_i + amount
197
+ entry = Entry.new(num, expires_at: entry.expires_at, version: entry.version)
198
+ write_entry(name, entry)
197
199
  num
198
200
  end
199
201
  end
@@ -84,7 +84,6 @@ class Class
84
84
  # class_attribute :settings, default: {}
85
85
  def class_attribute(*attrs, instance_accessor: true,
86
86
  instance_reader: instance_accessor, instance_writer: instance_accessor, instance_predicate: true, default: nil)
87
-
88
87
  class_methods, methods = [], []
89
88
  attrs.each do |name|
90
89
  unless name.is_a?(Symbol) || name.is_a?(String)
@@ -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:
@@ -225,9 +225,11 @@ class Pathname # :nodoc:
225
225
  end
226
226
  end
227
227
 
228
- class IPAddr # :nodoc:
229
- def as_json(options = nil)
230
- to_s
228
+ unless IPAddr.method_defined?(:as_json, false)
229
+ class IPAddr # :nodoc:
230
+ def as_json(options = nil)
231
+ to_s
232
+ end
231
233
  end
232
234
  end
233
235
 
@@ -68,7 +68,7 @@ class Object
68
68
  # You can access these methods using the class name instead:
69
69
  #
70
70
  # class Phone < ActiveRecord::Base
71
- # enum phone_number_type: { home: 0, office: 1, mobile: 2 }
71
+ # enum :phone_number_type, { home: 0, office: 1, mobile: 2 }
72
72
  #
73
73
  # with_options presence: true do
74
74
  # validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
@@ -5,6 +5,32 @@ class Range
5
5
  # (1..5).overlaps?(4..6) # => true
6
6
  # (1..5).overlaps?(7..9) # => false
7
7
  def overlaps?(other)
8
- other.begin == self.begin || cover?(other.begin) || other.cover?(self.begin)
8
+ raise TypeError unless other.is_a? Range
9
+
10
+ self_begin = self.begin
11
+ other_end = other.end
12
+ other_excl = other.exclude_end?
13
+
14
+ return false if _empty_range?(self_begin, other_end, other_excl)
15
+
16
+ other_begin = other.begin
17
+ self_end = self.end
18
+ self_excl = self.exclude_end?
19
+
20
+ return false if _empty_range?(other_begin, self_end, self_excl)
21
+ return true if self_begin == other_begin
22
+
23
+ return false if _empty_range?(self_begin, self_end, self_excl)
24
+ return false if _empty_range?(other_begin, other_end, other_excl)
25
+
26
+ true
9
27
  end
28
+
29
+ private
30
+ def _empty_range?(b, e, excl)
31
+ return false if b.nil? || e.nil?
32
+
33
+ comp = b <=> e
34
+ comp.nil? || comp > 0 || (comp == 0 && excl)
35
+ end
10
36
  end
@@ -24,7 +24,7 @@ class String
24
24
  #
25
25
  # The second argument, +indent_string+, specifies which indent string to
26
26
  # use. The default is +nil+, which tells the method to make a guess by
27
- # peeking at the first indented line, and fallback to a space if there is
27
+ # peeking at the first indented line, and fall back to a space if there is
28
28
  # none.
29
29
  #
30
30
  # " foo".indent(2) # => " foo"
@@ -53,7 +53,7 @@ module ActiveSupport
53
53
  # [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
54
54
  # [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
55
55
  # [+log+] Log all deprecation warnings to +Rails.logger+.
56
- # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
56
+ # [+notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
57
57
  # [+silence+] Do nothing. On Rails, set <tt>config.active_support.report_deprecations = false</tt> to disable all behaviors.
58
58
  #
59
59
  # Setting behaviors only affects deprecations that happen after boot time.
@@ -80,7 +80,7 @@ module ActiveSupport
80
80
  # [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
81
81
  # [+stderr+] Log all deprecation warnings to <tt>$stderr</tt>.
82
82
  # [+log+] Log all deprecation warnings to +Rails.logger+.
83
- # [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
83
+ # [+notify+] Use ActiveSupport::Notifications to notify +deprecation.rails+.
84
84
  # [+silence+] Do nothing.
85
85
  #
86
86
  # Setting behaviors only affects deprecations that happen after boot time.
@@ -128,7 +128,9 @@ module ActiveSupport
128
128
  return _extract_callstack(callstack) if callstack.first.is_a? String
129
129
 
130
130
  offending_line = callstack.find { |frame|
131
- frame.absolute_path && !ignored_callstack(frame.absolute_path)
131
+ # Code generated with `eval` doesn't have an `absolute_path`, e.g. templates.
132
+ path = frame.absolute_path || frame.path
133
+ path && !ignored_callstack?(path)
132
134
  } || callstack.first
133
135
 
134
136
  [offending_line.path, offending_line.lineno, offending_line.label]
@@ -136,7 +138,7 @@ module ActiveSupport
136
138
 
137
139
  def _extract_callstack(callstack)
138
140
  warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
139
- offending_line = callstack.find { |line| !ignored_callstack(line) } || callstack.first
141
+ offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
140
142
 
141
143
  if offending_line
142
144
  if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
@@ -147,10 +149,11 @@ module ActiveSupport
147
149
  end
148
150
  end
149
151
 
150
- RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
152
+ RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
153
+ LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
151
154
 
152
- def ignored_callstack(path)
153
- path.start_with?(RAILS_GEM_ROOT) || path.start_with?(RbConfig::CONFIG["rubylibdir"])
155
+ def ignored_callstack?(path)
156
+ path.start_with?(RAILS_GEM_ROOT, LIB_DIR)
154
157
  end
155
158
  end
156
159
  end
@@ -9,8 +9,8 @@ module ActiveSupport
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 0
12
- TINY = 8
13
- PRE = "7"
12
+ TINY = 9
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -161,7 +161,7 @@ module ActiveSupport
161
161
  # upcase_first('w') # => "W"
162
162
  # upcase_first('') # => ""
163
163
  def upcase_first(string)
164
- string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
164
+ string.length > 0 ? string[0].upcase.concat(string[1..-1]) : +""
165
165
  end
166
166
 
167
167
  # Capitalizes all the words and replaces some characters in the string to
@@ -4,6 +4,7 @@ require "active_support/concern"
4
4
  require "active_support/core_ext/module/attribute_accessors"
5
5
  require "concurrent"
6
6
  require "fiber"
7
+ require "logger"
7
8
 
8
9
  module ActiveSupport
9
10
  module LoggerThreadSafeLevel # :nodoc:
@@ -56,7 +56,7 @@ module ActiveSupport
56
56
  end
57
57
 
58
58
  class Event
59
- attr_reader :name, :time, :end, :transaction_id, :children
59
+ attr_reader :name, :transaction_id, :children
60
60
  attr_accessor :payload
61
61
 
62
62
  def initialize(name, start, ending, transaction_id, payload)
@@ -72,7 +72,15 @@ module ActiveSupport
72
72
  @allocation_count_finish = 0
73
73
  end
74
74
 
75
- def record
75
+ def time
76
+ @time / 1000.0 if @time
77
+ end
78
+
79
+ def end
80
+ @end / 1000.0 if @end
81
+ end
82
+
83
+ def record # :nodoc:
76
84
  start!
77
85
  begin
78
86
  yield payload if block_given?
@@ -99,20 +107,20 @@ module ActiveSupport
99
107
  @allocation_count_finish = now_allocations
100
108
  end
101
109
 
102
- # Returns the CPU time (in milliseconds) passed since the call to
103
- # +start!+ and the call to +finish!+
110
+ # Returns the CPU time (in milliseconds) passed between the call to
111
+ # #start! and the call to #finish!.
104
112
  def cpu_time
105
113
  @cpu_time_finish - @cpu_time_start
106
114
  end
107
115
 
108
- # Returns the idle time time (in milliseconds) passed since the call to
109
- # +start!+ and the call to +finish!+
116
+ # Returns the idle time time (in milliseconds) passed between the call to
117
+ # #start! and the call to #finish!.
110
118
  def idle_time
111
119
  duration - cpu_time
112
120
  end
113
121
 
114
- # Returns the number of allocations made since the call to +start!+ and
115
- # the call to +finish!+
122
+ # Returns the number of allocations made between the call to #start! and
123
+ # the call to #finish!.
116
124
  def allocations
117
125
  @allocation_count_finish - @allocation_count_start
118
126
  end
@@ -130,7 +138,7 @@ module ActiveSupport
130
138
  #
131
139
  # @event.duration # => 1000.138
132
140
  def duration
133
- self.end - time
141
+ @end - @time
134
142
  end
135
143
 
136
144
  def <<(event)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "bigdecimal"
4
+ require "bigdecimal/util"
3
5
  require "active_support/core_ext/big_decimal/conversions"
4
6
  require "active_support/core_ext/object/blank"
5
7
  require "active_support/core_ext/hash/keys"
@@ -128,7 +130,7 @@ module ActiveSupport
128
130
  def execute
129
131
  if !number
130
132
  nil
131
- elsif validate_float? && !valid_float?
133
+ elsif validate_float? && !valid_bigdecimal
132
134
  number
133
135
  else
134
136
  convert
@@ -173,8 +175,15 @@ module ActiveSupport
173
175
  key.split(".").reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
174
176
  end
175
177
 
176
- def valid_float?
177
- Float(number, exception: false)
178
+ def valid_bigdecimal
179
+ case number
180
+ when Float, Rational
181
+ number.to_d(0)
182
+ when String
183
+ BigDecimal(number, exception: false)
184
+ else
185
+ number.to_d rescue nil
186
+ end
178
187
  end
179
188
  end
180
189
  end
@@ -10,13 +10,13 @@ module ActiveSupport
10
10
  def convert
11
11
  format = options[:format]
12
12
 
13
- number_f = valid_float?
14
- if number_f
15
- if number_f.negative?
16
- number_f = number_f.abs
17
- format = options[:negative_format] if (number_f * 10**options[:precision]) >= 0.5
13
+ number_d = valid_bigdecimal
14
+ if number_d
15
+ if number_d.negative?
16
+ number_d = number_d.abs
17
+ format = options[:negative_format] if (number_d * 10**options[:precision]) >= 0.5
18
18
  end
19
- number_s = NumberToRoundedConverter.convert(number_f, options)
19
+ number_s = NumberToRoundedConverter.convert(number_d, options)
20
20
  else
21
21
  number_s = number.to_s.strip
22
22
  format = options[:negative_format] if number_s.sub!(/^-/, "")
@@ -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