activesupport 8.0.4.1 → 8.0.5

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: 4c9cda7e70f0c4b499dc6dadf103e87f3d7e8330879a0ee3a354228711b68fe7
4
- data.tar.gz: d21140f0a85881f2534febd8e9f0bed0820a72c3a278543c4b1b7b7eeb6a2641
3
+ metadata.gz: e4952a5142700edad7b5bf62c0753a93f202873d96a6ad7dd5b231348ec9d917
4
+ data.tar.gz: 7f832339286eeeec1fda1097ee5ad50b6efc3ae44bb1aa483524907dcb3aebd7
5
5
  SHA512:
6
- metadata.gz: 67213e140a1b59108ba6856958603c56472cfa0b6745e5b5479d3ab0b7be050daf8d15b4276ede433d721f2cf853f3ccec4908387a5c67ed4e9657d929f22621
7
- data.tar.gz: 03af70c76a91cf42ca70bb7d7fce52b2c33e7b957bb2c3943399f714bedac5f522628beb95e8f50c8632564653717d7a10f0187cf1f3d33b21cc1ce2ff7446c4
6
+ metadata.gz: 5b4825cf61610686c03d69e648665d4d886e1c755a0f3bc4db14d1bb81331e0c11a79d3c96295185972cb0852e894d66bdc03d2c6787e8c6f3629f4d18e8ce43
7
+ data.tar.gz: 5b7ee77d3c6a4f36a7746d2ce9a2d49d2cc18c9a4e17931626d3dc87c09edb12236180a6ef5d1c7dddcbf7cdafed9d97edb9b9146ed0d56231b286573f41795b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,36 @@
1
+ ## Rails 8.0.5 (March 24, 2026) ##
2
+
3
+ * Fix inflections to better handle overlapping acronyms.
4
+
5
+ ```ruby
6
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
7
+ inflect.acronym "USD"
8
+ inflect.acronym "USDC"
9
+ end
10
+
11
+ "USDC".underscore # => "usdc"
12
+ ```
13
+
14
+ *Said Kaldybaev*
15
+
16
+ * Silence Dalli 4.0+ warning when using `ActiveSupport::Cache::MemCacheStore`.
17
+
18
+ *zzak*
19
+
20
+ * Make `delegate` and `delegate_missing_to` work in BasicObject subclasses.
21
+
22
+ *Rafael Mendonça França*
23
+
24
+ * Fix `ActiveSupport::Inflector.humanize` with international characters.
25
+
26
+ ```ruby
27
+ ActiveSupport::Inflector.humanize("áÉÍÓÚ") # => "Áéíóú"
28
+ ActiveSupport::Inflector.humanize("аБВГДЕ") # => "Абвгде"
29
+ ```
30
+
31
+ *Jose Luis Duran*
32
+
33
+
1
34
  ## Rails 8.0.4.1 (March 23, 2026) ##
2
35
 
3
36
  * Reject scientific notation in NumberConverter
@@ -85,6 +118,26 @@
85
118
 
86
119
  *Jason T Johnson*, *Jean Boussier*
87
120
 
121
+ **Note on overwrite precedence:**
122
+
123
+ This fix also changed the overwrite precedence when distinct keys are transformed into the same key.
124
+ The later key now overwrites the earlier one (matching standard Ruby `Hash` behavior).
125
+ Previously, the earlier key's value was preserved.
126
+
127
+ Before:
128
+
129
+ ```ruby
130
+ >> {XY: 1, xy: 2}.with_indifferent_access.transform_keys!(&:downcase)
131
+ => {"xy"=>1}
132
+ ```
133
+
134
+ After:
135
+
136
+ ```ruby
137
+ >> {XY: 1, xy: 2}.with_indifferent_access.transform_keys!(&:downcase)
138
+ => {"xy"=>2}
139
+ ```
140
+
88
141
  * Fix `ActiveSupport::Cache::MemCacheStore#read_multi` to handle network errors.
89
142
 
90
143
  This method specifically wasn't handling network errors like other codepaths.
@@ -60,7 +60,7 @@ module ActiveSupport
60
60
  pool_options = retrieve_pool_options(options)
61
61
 
62
62
  if pool_options
63
- ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
63
+ ConnectionPool.new(**pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
64
64
  else
65
65
  Dalli::Client.new(addresses, options)
66
66
  end
@@ -90,6 +90,9 @@ module ActiveSupport
90
90
  # The value "compress: false" prevents duplicate compression within Dalli.
91
91
  @mem_cache_options[:compress] = false
92
92
  (OVERRIDDEN_OPTIONS - %i(compress)).each { |name| @mem_cache_options.delete(name) }
93
+ # Set the default serializer for Dalli to prevent warning about
94
+ # inheriting the default serializer.
95
+ @mem_cache_options[:serializer] = Marshal
93
96
  @data = self.class.build_mem_cache(*(addresses + [@mem_cache_options]))
94
97
  end
95
98
 
@@ -150,7 +150,7 @@ module ActiveSupport
150
150
  universal_options = redis_options.extract!(*UNIVERSAL_OPTIONS)
151
151
 
152
152
  if pool_options = self.class.send(:retrieve_pool_options, redis_options)
153
- @redis = ::ConnectionPool.new(pool_options) { self.class.build_redis(**redis_options) }
153
+ @redis = ::ConnectionPool.new(**pool_options) { self.class.build_redis(**redis_options) }
154
154
  else
155
155
  @redis = self.class.build_redis(**redis_options)
156
156
  end
@@ -4,7 +4,7 @@
4
4
  require "json"
5
5
  require "bigdecimal"
6
6
  require "ipaddr"
7
- require "uri/generic"
7
+ require "uri"
8
8
  require "pathname"
9
9
  require "active_support/core_ext/big_decimal/conversions" # for #to_s
10
10
  require "active_support/core_ext/hash/except"
@@ -248,7 +248,7 @@ class String
248
248
  # optional parameter +capitalize+ to false.
249
249
  # By default, this parameter is true.
250
250
  #
251
- # The trailing '_id' can be kept and capitalized by setting the
251
+ # The trailing '_id' can be kept by setting the
252
252
  # optional parameter +keep_id_suffix+ to true.
253
253
  # By default, this parameter is false.
254
254
  #
@@ -134,11 +134,11 @@ module ActiveSupport
134
134
  "def #{method_name}(#{definition})" <<
135
135
  " _ = #{receiver}" <<
136
136
  " _.#{method}(#{definition})" <<
137
- "rescue NoMethodError => e" <<
137
+ "rescue ::NoMethodError => e" <<
138
138
  " if _.nil? && e.name == :#{method}" <<
139
- " raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
139
+ " ::Kernel.raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
140
140
  " else" <<
141
- " raise" <<
141
+ " ::Kernel.raise" <<
142
142
  " end" <<
143
143
  "end"
144
144
  end
@@ -151,49 +151,32 @@ module ActiveSupport
151
151
  target = target.to_s
152
152
  target = "self.#{target}" if RESERVED_METHOD_NAMES.include?(target) || target == "__target"
153
153
 
154
- if allow_nil
155
- owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
156
- def respond_to_missing?(name, include_private = false)
157
- # It may look like an oversight, but we deliberately do not pass
158
- # +include_private+, because they do not get delegated.
159
-
160
- return false if name == :marshal_dump || name == :_dump
161
- #{target}.respond_to?(name) || super
162
- end
163
-
164
- def method_missing(method, ...)
165
- __target = #{target}
166
- if __target.nil? && !nil.respond_to?(method)
167
- nil
168
- elsif __target.respond_to?(method)
169
- __target.public_send(method, ...)
170
- else
171
- super
172
- end
173
- end
174
- RUBY
154
+ nil_behavior = if allow_nil
155
+ "nil"
175
156
  else
176
- owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
177
- def respond_to_missing?(name, include_private = false)
178
- # It may look like an oversight, but we deliberately do not pass
179
- # +include_private+, because they do not get delegated.
157
+ "::Kernel.raise ::ActiveSupport::DelegationError.nil_target(method, :'#{target}')"
158
+ end
180
159
 
181
- return false if name == :marshal_dump || name == :_dump
182
- #{target}.respond_to?(name) || super
183
- end
160
+ owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
161
+ def respond_to_missing?(name, include_private = false)
162
+ # It may look like an oversight, but we deliberately do not pass
163
+ # +include_private+, because they do not get delegated.
184
164
 
185
- def method_missing(method, ...)
186
- __target = #{target}
187
- if __target.nil? && !nil.respond_to?(method)
188
- raise ::ActiveSupport::DelegationError.nil_target(method, :'#{target}')
189
- elsif __target.respond_to?(method)
190
- __target.public_send(method, ...)
191
- else
192
- super
193
- end
165
+ return false if name == :marshal_dump || name == :_dump
166
+ #{target}.respond_to?(name) || super
167
+ end
168
+
169
+ def method_missing(method, ...)
170
+ __target = #{target}
171
+ if __target.nil? && !nil.respond_to?(method)
172
+ #{nil_behavior}
173
+ elsif __target.respond_to?(method)
174
+ __target.public_send(method, ...)
175
+ else
176
+ super
194
177
  end
195
- RUBY
196
- end
178
+ end
179
+ RUBY
197
180
  end
198
181
  end
199
182
  end
@@ -9,8 +9,8 @@ module ActiveSupport
9
9
  module VERSION
10
10
  MAJOR = 8
11
11
  MINOR = 0
12
- TINY = 4
13
- PRE = "1"
12
+ TINY = 5
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -248,7 +248,8 @@ module ActiveSupport
248
248
 
249
249
  private
250
250
  def define_acronym_regex_patterns
251
- @acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/
251
+ sorted_acronyms = @acronyms.empty? ? [] : @acronyms.values.sort_by { |a| -a.length }
252
+ @acronym_regex = sorted_acronyms.empty? ? /(?=a)b/ : /#{sorted_acronyms.join("|")}/
252
253
  @acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
253
254
  @acronyms_underscore_regex = /(?:(?<=([A-Za-z\d]))|\b)(#{@acronym_regex})(?=\b|[^a-z])/
254
255
  end
@@ -119,7 +119,7 @@ module ActiveSupport
119
119
  # The capitalization of the first word can be turned off by setting the
120
120
  # +:capitalize+ option to false (default is true).
121
121
  #
122
- # The trailing '_id' can be kept and capitalized by setting the
122
+ # The trailing '_id' can be kept by setting the
123
123
  # optional parameter +keep_id_suffix+ to true (default is false).
124
124
  #
125
125
  # humanize('employee_salary') # => "Employee salary"
@@ -143,13 +143,13 @@ module ActiveSupport
143
143
  result.delete_suffix!(" id")
144
144
  end
145
145
 
146
- result.gsub!(/([a-z\d]+)/i) do |match|
146
+ result.gsub!(/([[[:alpha:]]\d]+)/i) do |match|
147
147
  match.downcase!
148
148
  inflections.acronyms[match] || match
149
149
  end
150
150
 
151
151
  if capitalize
152
- result.sub!(/\A\w/) do |match|
152
+ result.sub!(/\A[[:alpha:]]/) do |match|
153
153
  match.upcase!
154
154
  match
155
155
  end
@@ -56,11 +56,13 @@ module ActiveSupport
56
56
  scope.current
57
57
  end
58
58
 
59
- def share_with(other)
59
+ def share_with(other, except: [])
60
60
  # Action Controller streaming spawns a new thread and copy thread locals.
61
61
  # We do the same here for backward compatibility, but this is very much a hack
62
62
  # and streaming should be rethought.
63
- context.active_support_execution_state = other.active_support_execution_state.dup
63
+ state = other.active_support_execution_state.dup
64
+ Array(except).each { |key| state.delete(key) }
65
+ context.active_support_execution_state = state
64
66
  end
65
67
 
66
68
  private
@@ -4,7 +4,7 @@ require "bigdecimal"
4
4
  require "date"
5
5
  require "ipaddr"
6
6
  require "pathname"
7
- require "uri/generic"
7
+ require "uri"
8
8
  require "msgpack/bigint"
9
9
  require "active_support/hash_with_indifferent_access"
10
10
  require "active_support/time"
@@ -45,6 +45,12 @@ module ActiveSupport
45
45
  ActiveSupport.test_order ||= :random
46
46
  end
47
47
 
48
+ if Minitest.respond_to? :run_order # MT6 API change
49
+ def run_order # :nodoc:
50
+ test_order
51
+ end
52
+ end
53
+
48
54
  # Parallelizes the test suite.
49
55
  #
50
56
  # Takes a +workers+ argument that controls how many times the process
@@ -120,7 +120,8 @@ module ActiveSupport
120
120
  actual = exp.call
121
121
  rich_message = -> do
122
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}"
123
+ error = "`#{code_string}` didn't change by #{diff}, but by #{actual - before_value}."
124
+ error = "#{error}\n#{diff before_value + diff, actual}" if Minitest::VERSION > "6"
124
125
  error = "#{message}.\n#{error}" if message
125
126
  error
126
127
  end
@@ -212,7 +213,7 @@ module ActiveSupport
212
213
  rich_message = -> do
213
214
  code_string = expression.respond_to?(:call) ? _callable_to_source_string(expression) : expression
214
215
  error = "`#{code_string}` didn't change"
215
- error = "#{error}. It was already #{to.inspect}" if before == to
216
+ error = "#{error}. It was already #{to.inspect}." if before == to
216
217
  error = "#{message}.\n#{error}" if message
217
218
  error
218
219
  end
@@ -268,8 +269,9 @@ module ActiveSupport
268
269
 
269
270
  rich_message = -> do
270
271
  code_string = expression.respond_to?(:call) ? _callable_to_source_string(expression) : expression
271
- error = "`#{code_string}` changed"
272
+ error = "`#{code_string}` changed."
272
273
  error = "#{message}.\n#{error}" if message
274
+ error = "#{error}\n#{diff before, after}" if Minitest::VERSION > "6"
273
275
  error
274
276
  end
275
277
 
@@ -318,6 +320,9 @@ module ActiveSupport
318
320
  lines[0] = lines[0].byteslice(location[1]...)
319
321
  source = lines.join.strip
320
322
 
323
+ # Strip stabby lambda from Ruby 4.1+
324
+ source = source.sub(/^->\s*/, "")
325
+
321
326
  # We ignore procs defined with do/end as they are likely multi-line anyway.
322
327
  if source.start_with?("{")
323
328
  source.delete_suffix!("}")
@@ -2,4 +2,9 @@
2
2
 
3
3
  require "minitest"
4
4
 
5
+ # This respond_to check handles tests running sub-processes in an
6
+ # unbundled environment, which triggers MT5 usage. This conditional may
7
+ # be removable after the version bump, though it currently safeguards
8
+ # against issues in environments with multiple versions installed.
9
+ Minitest.load :rails if Minitest.respond_to? :load
5
10
  Minitest.autorun
@@ -47,7 +47,11 @@ module ActiveSupport
47
47
  set_process_title("#{klass}##{method}")
48
48
 
49
49
  result = klass.with_info_handler reporter do
50
- Minitest.run_one_method(klass, method)
50
+ if Minitest.respond_to? :run_one_method
51
+ Minitest.run_one_method klass, method
52
+ else
53
+ klass.new(method).run
54
+ end
51
55
  end
52
56
 
53
57
  safe_record(reporter, result)
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.4.1
4
+ version: 8.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
@@ -90,9 +90,6 @@ dependencies:
90
90
  - - ">="
91
91
  - !ruby/object:Gem::Version
92
92
  version: '5.1'
93
- - - "<"
94
- - !ruby/object:Gem::Version
95
- version: '6'
96
93
  type: :runtime
97
94
  prerelease: false
98
95
  version_requirements: !ruby/object:Gem::Requirement
@@ -100,9 +97,6 @@ dependencies:
100
97
  - - ">="
101
98
  - !ruby/object:Gem::Version
102
99
  version: '5.1'
103
- - - "<"
104
- - !ruby/object:Gem::Version
105
- version: '6'
106
100
  - !ruby/object:Gem::Dependency
107
101
  name: base64
108
102
  requirement: !ruby/object:Gem::Requirement
@@ -500,10 +494,10 @@ licenses:
500
494
  - MIT
501
495
  metadata:
502
496
  bug_tracker_uri: https://github.com/rails/rails/issues
503
- changelog_uri: https://github.com/rails/rails/blob/v8.0.4.1/activesupport/CHANGELOG.md
504
- documentation_uri: https://api.rubyonrails.org/v8.0.4.1/
497
+ changelog_uri: https://github.com/rails/rails/blob/v8.0.5/activesupport/CHANGELOG.md
498
+ documentation_uri: https://api.rubyonrails.org/v8.0.5/
505
499
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
506
- source_code_uri: https://github.com/rails/rails/tree/v8.0.4.1/activesupport
500
+ source_code_uri: https://github.com/rails/rails/tree/v8.0.5/activesupport
507
501
  rubygems_mfa_required: 'true'
508
502
  rdoc_options:
509
503
  - "--encoding"