activesupport 8.1.3.1 → 8.1.4
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 +4 -4
- data/CHANGELOG.md +211 -0
- data/lib/active_support/cache/file_store.rb +1 -0
- data/lib/active_support/cache/memory_store.rb +1 -1
- data/lib/active_support/cache/redis_cache_store.rb +9 -1
- data/lib/active_support/cache/strategy/local_cache.rb +1 -1
- data/lib/active_support/cache.rb +1 -1
- data/lib/active_support/configurable.rb +3 -3
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -0
- data/lib/active_support/core_ext/enumerable.rb +2 -2
- data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +2 -14
- data/lib/active_support/core_ext/range/sole.rb +2 -0
- data/lib/active_support/core_ext/string/filters.rb +2 -0
- data/lib/active_support/core_ext/time/calculations.rb +4 -0
- data/lib/active_support/duration.rb +6 -6
- data/lib/active_support/event_reporter.rb +2 -2
- data/lib/active_support/execution_context.rb +1 -1
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +2 -1
- data/lib/active_support/inflector/methods.rb +1 -1
- data/lib/active_support/inflector/transliterate.rb +12 -2
- data/lib/active_support/json/decoding.rb +6 -2
- data/lib/active_support/json/encoding.rb +13 -3
- data/lib/active_support/message_pack/extensions.rb +11 -2
- data/lib/active_support/messages/serializer_with_fallback.rb +1 -1
- data/lib/active_support/notifications.rb +2 -2
- data/lib/active_support/number_helper/number_converter.rb +4 -1
- data/lib/active_support/number_helper/number_to_currency_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -4
- data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -7
- data/lib/active_support/number_helper/rounding_helper.rb +1 -1
- data/lib/active_support/ordered_options.rb +2 -2
- data/lib/active_support/security_utils.rb +4 -5
- data/lib/active_support/structured_event_subscriber.rb +1 -1
- data/lib/active_support/syntax_error_proxy.rb +2 -1
- data/lib/active_support/test_case.rb +1 -1
- data/lib/active_support/testing/parallelization.rb +4 -0
- data/lib/active_support/values/time_zone.rb +3 -3
- data/lib/active_support/xml_mini.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 273220875f318471f4762274c8b1f360159e9e89b008fd5b4b79ea0931e0974b
|
|
4
|
+
data.tar.gz: e7a6e2364c58e00ef9993314e3182207b0c2b39a07d4e30470b3485438eb9dc6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3562450b3e30a8426934851940e2f344c781ba56a1048e70c80b90da735a4dec4e571aaeb892313ab5ab546ff52ce7e0423d3012d090ffd2e3713c9e4e8b08db
|
|
7
|
+
data.tar.gz: 403060699c2a65347f2c9f5fb1238c898f04f7675e4102ad43bae2da4e64d55c17b33c9c15d1484a4e565fc34efd0d3150a5e4725ee26e1e567b46c0b55dabc9
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,214 @@
|
|
|
1
|
+
## Rails 8.1.4 (September 24, 2026) ##
|
|
2
|
+
|
|
3
|
+
* Fix the debug error page rendering for `SyntaxError`s with multi-line messages.
|
|
4
|
+
|
|
5
|
+
*Marco Roth*
|
|
6
|
+
|
|
7
|
+
* Make `ActiveSupport::JSON.decode` compatible with the upcoming `json` 3.0 gem.
|
|
8
|
+
|
|
9
|
+
*Earlopain*
|
|
10
|
+
|
|
11
|
+
* Fix `number_to_human_size` crashing for sizes above a terabyte by supporting
|
|
12
|
+
petabyte, exabyte, and zettabyte storage units.
|
|
13
|
+
|
|
14
|
+
*Kenta Ishizaki*
|
|
15
|
+
|
|
16
|
+
* Fix `Range#sole` raising `NoMethodError` when the enumerable core extension
|
|
17
|
+
isn't already loaded.
|
|
18
|
+
|
|
19
|
+
*Kenta Ishizaki*
|
|
20
|
+
|
|
21
|
+
* Fix `ActiveSupport::Cache::FileStore` raising `NameError` for `FileUtils`
|
|
22
|
+
when `fileutils` isn't already loaded.
|
|
23
|
+
|
|
24
|
+
*Kenta Ishizaki*
|
|
25
|
+
|
|
26
|
+
* Fix `Enumerable#in_order_of` with `filter: true` dropping `nil` elements
|
|
27
|
+
explicitly named in the series.
|
|
28
|
+
|
|
29
|
+
*Kenta Ishizaki*
|
|
30
|
+
|
|
31
|
+
* Keep `HashWithIndifferentAccess#filter` returning a `HashWithIndifferentAccess`
|
|
32
|
+
instead of a plain `Hash`.
|
|
33
|
+
|
|
34
|
+
*Kenta Ishizaki*
|
|
35
|
+
|
|
36
|
+
* Fix `number_to_human` and `number_to_human_size` crashing when `:precision` is `nil`.
|
|
37
|
+
|
|
38
|
+
*Kenta Ishizaki*
|
|
39
|
+
|
|
40
|
+
* Fix `ActiveSupport::InheritableOptions#to_h` to recursively flatten nested
|
|
41
|
+
`InheritableOptions` parents.
|
|
42
|
+
|
|
43
|
+
*Andrew Novoselac*
|
|
44
|
+
|
|
45
|
+
* Fix `ActiveSupport::StructuredEventSubscriber.debug_only` leaking debug-only
|
|
46
|
+
methods across subscriber subclasses.
|
|
47
|
+
|
|
48
|
+
*Kenta Ishizaki*
|
|
49
|
+
|
|
50
|
+
* Fix `ActiveSupport::Inflector#transliterate` mutating the caller's string.
|
|
51
|
+
|
|
52
|
+
*Kenta Ishizaki*
|
|
53
|
+
|
|
54
|
+
* Fix `Hash.from_xml` raising `Date::Error` on `type="date"` values surrounded
|
|
55
|
+
by whitespace.
|
|
56
|
+
|
|
57
|
+
*Kenta Ishizaki*
|
|
58
|
+
|
|
59
|
+
* Fix `Time#advance` and `DateTime#advance` mutating the options hash passed
|
|
60
|
+
by the caller.
|
|
61
|
+
|
|
62
|
+
*Kenta Ishizaki*
|
|
63
|
+
|
|
64
|
+
* Fix `ActiveSupport::Cache::MemoryStore#cleanup` raising `NoMethodError` when
|
|
65
|
+
used with a non-`DupCoder` serializer.
|
|
66
|
+
|
|
67
|
+
*Kenta Ishizaki*
|
|
68
|
+
|
|
69
|
+
* Fix `String#truncate` with `:separator` misbehaving when the `:omission` is
|
|
70
|
+
longer than the target length.
|
|
71
|
+
|
|
72
|
+
*Kenta Ishizaki*
|
|
73
|
+
|
|
74
|
+
* Fix `ActiveSupport::Cache::Store#delete_multi` mutating the names array passed
|
|
75
|
+
by the caller.
|
|
76
|
+
|
|
77
|
+
*Kenta Ishizaki*
|
|
78
|
+
|
|
79
|
+
* Fix `number_to_currency` crashing on a negative number when `:precision` is `nil`.
|
|
80
|
+
|
|
81
|
+
*Kenta Ishizaki*
|
|
82
|
+
|
|
83
|
+
* Fix `TimeZone#strptime` with `%s` dropping the sub-second fraction of the timestamp.
|
|
84
|
+
|
|
85
|
+
*Kenta Ishizaki*
|
|
86
|
+
|
|
87
|
+
* Fix `HashWithIndifferentAccess.new` dropping a falsy (`false` or `0`) default
|
|
88
|
+
value from the source hash.
|
|
89
|
+
|
|
90
|
+
*Kenta Ishizaki*
|
|
91
|
+
|
|
92
|
+
* Fix `Range#include?` and `Range#===` raising on exclusive non-integer sub-ranges.
|
|
93
|
+
|
|
94
|
+
*Kenta Ishizaki*
|
|
95
|
+
|
|
96
|
+
* Fix `number_to_delimited` corrupting numbers that begin with a `+` or `-` sign.
|
|
97
|
+
|
|
98
|
+
*Kenta Ishizaki*
|
|
99
|
+
|
|
100
|
+
* Fix `ActiveSupport::InheritableOptions#==` raising `NoMethodError` when compared
|
|
101
|
+
with a non-Hash object.
|
|
102
|
+
|
|
103
|
+
*Kenta Ishizaki*
|
|
104
|
+
|
|
105
|
+
* Fix `Enumerable#in_order_of` with `filter: false` dropping elements whose keyed
|
|
106
|
+
value is `nil`.
|
|
107
|
+
|
|
108
|
+
*Hammad Khan*
|
|
109
|
+
|
|
110
|
+
* Stop the DRb service when shutting down parallel test workers, preventing it
|
|
111
|
+
from lingering after the test run.
|
|
112
|
+
|
|
113
|
+
*Shuta Mugikura*
|
|
114
|
+
|
|
115
|
+
* Preserve the encoding of `ActiveSupport::SafeBuffer` values round-tripped through
|
|
116
|
+
`ActiveSupport::MessagePack`.
|
|
117
|
+
|
|
118
|
+
*Rafael Mendonça França*
|
|
119
|
+
|
|
120
|
+
* Fix `number_to_phone` without an area code stripping a leading delimiter when the
|
|
121
|
+
number itself coincidentally starts with the delimiter string.
|
|
122
|
+
|
|
123
|
+
*Tahsin Hasan*
|
|
124
|
+
|
|
125
|
+
* Update `ActiveSupport::TimeZone` mappings to use the current IANA identifiers
|
|
126
|
+
`Europe/Kyiv` (was `Europe/Kiev`) and `Asia/Yangon` (was `Asia/Rangoon`).
|
|
127
|
+
|
|
128
|
+
*tsymbalenkovlad*
|
|
129
|
+
|
|
130
|
+
* Use the faster string-based delimiter logic by default in `number_to_delimited`,
|
|
131
|
+
instead of the regular expression fallback.
|
|
132
|
+
|
|
133
|
+
*Shinichi Maeshima*
|
|
134
|
+
|
|
135
|
+
* Preserve the requested key order in `ActiveSupport::Cache::Store#fetch_multi`
|
|
136
|
+
when a local cache is active.
|
|
137
|
+
|
|
138
|
+
Previously, if some keys were served from the local cache and others from the
|
|
139
|
+
underlying store, `fetch_multi` returned the local cache hits first instead of
|
|
140
|
+
following the order of the requested keys.
|
|
141
|
+
|
|
142
|
+
*Mueez Afzal*
|
|
143
|
+
|
|
144
|
+
* Fix `String#parameterize` raising `TypeError` when `separator` is `nil`.
|
|
145
|
+
|
|
146
|
+
`parameterize` already treats a `nil` separator the same as an empty one when
|
|
147
|
+
squeezing and trimming separators, but raised `TypeError` before reaching that
|
|
148
|
+
point. A `nil` separator now behaves like `""`, removing the unwanted characters.
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
"Donald E. Knuth".parameterize(separator: nil) # => "donaldeknuth"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
*Hammad Khan*
|
|
155
|
+
|
|
156
|
+
* Add `RedisClient::Error` to `ActiveSupport::Cache::RedisCacheStore`'s failsafe rescue list.
|
|
157
|
+
|
|
158
|
+
The redis-rb gem normally translates `RedisClient::*` errors into `Redis::*` errors but in
|
|
159
|
+
some rare cases, such as when using sentinels, `RedisClient::*` errors may slip through.
|
|
160
|
+
|
|
161
|
+
*David Arrunategui*
|
|
162
|
+
|
|
163
|
+
* Fix `ActiveSupport::Duration#in_minutes`, `#in_hours`, `#in_days`,
|
|
164
|
+
`#in_weeks`, `#in_months`, and `#in_years` truncating sub-second precision.
|
|
165
|
+
|
|
166
|
+
These methods divided the duration's integer second count (`in_seconds`,
|
|
167
|
+
aliased to `to_i`) instead of its exact value, so any fractional second was
|
|
168
|
+
silently dropped before the conversion.
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
# Before
|
|
172
|
+
90.5.seconds.in_minutes # => 1.5
|
|
173
|
+
|
|
174
|
+
# After
|
|
175
|
+
90.5.seconds.in_minutes # => 1.5083333333333333
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
*Kenta Ishizaki*
|
|
179
|
+
|
|
180
|
+
* Fix JSON encoding of non-String Hash keys.
|
|
181
|
+
|
|
182
|
+
The old encoder would simply call `to_s` on them, the newer encoder
|
|
183
|
+
would incorrectly call `as_json` instead.
|
|
184
|
+
|
|
185
|
+
In the case of `Time`, `DateTime` and `TimeWithZone` this would result
|
|
186
|
+
in different serialization of time keys: `"2009-01-01T12:30:00.000Z"` (arguably better)
|
|
187
|
+
instead of `"2009-01-01 12:30:00 UTC"` (how it used to be).
|
|
188
|
+
|
|
189
|
+
*Kenta Ishizaki*
|
|
190
|
+
|
|
191
|
+
* Fix `number_to_phone` dropping only the first character of a
|
|
192
|
+
multi-character `:delimiter` when no area code is present.
|
|
193
|
+
|
|
194
|
+
The leading delimiter produced by an empty first capture group was
|
|
195
|
+
stripped with `slice!(0, 1)`, which assumed a single-character
|
|
196
|
+
delimiter. Multi-character (and multibyte) delimiters now work:
|
|
197
|
+
|
|
198
|
+
number_to_phone(5551234, delimiter: " - ") # => "555 - 1234"
|
|
199
|
+
# was "- 555 - 1234"
|
|
200
|
+
|
|
201
|
+
*Kenta Ishizaki*
|
|
202
|
+
|
|
203
|
+
* Fix `titleize` inflector to consider Unicode characters
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
"ćasim đipa".titleize # => "Ćasim Đipa"
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
*Eldin Guzin*
|
|
210
|
+
|
|
211
|
+
|
|
1
212
|
## Rails 8.1.3.1 (July 29, 2026) ##
|
|
2
213
|
|
|
3
214
|
* No changes.
|
|
@@ -105,7 +105,7 @@ module ActiveSupport
|
|
|
105
105
|
_instrument(:cleanup, size: @data.size) do
|
|
106
106
|
keys = synchronize { @data.keys }
|
|
107
107
|
keys.each do |key|
|
|
108
|
-
entry = @data[key]
|
|
108
|
+
entry = deserialize_entry(@data[key])
|
|
109
109
|
delete_entry(key, **options) if entry && entry.expired?
|
|
110
110
|
end
|
|
111
111
|
end
|
|
@@ -487,9 +487,17 @@ module ActiveSupport
|
|
|
487
487
|
@supports_expire_nx = redis_versions.all? { |v| Gem::Version.new(v) >= Gem::Version.new("7.0.0") }
|
|
488
488
|
end
|
|
489
489
|
|
|
490
|
+
FAILSAFE_ERRORS = [
|
|
491
|
+
::Redis::BaseError,
|
|
492
|
+
ConnectionPool::Error,
|
|
493
|
+
ConnectionPool::TimeoutError,
|
|
494
|
+
(::RedisClient::Error if defined?(::RedisClient::Error)),
|
|
495
|
+
].compact.freeze
|
|
496
|
+
private_constant :FAILSAFE_ERRORS
|
|
497
|
+
|
|
490
498
|
def failsafe(method, returning: nil)
|
|
491
499
|
yield
|
|
492
|
-
rescue
|
|
500
|
+
rescue *FAILSAFE_ERRORS => error
|
|
493
501
|
@error_handler&.call(method: method, exception: error, returning: returning)
|
|
494
502
|
returning
|
|
495
503
|
end
|
data/lib/active_support/cache.rb
CHANGED
|
@@ -700,7 +700,7 @@ module ActiveSupport
|
|
|
700
700
|
return 0 if names.empty?
|
|
701
701
|
|
|
702
702
|
options = merged_options(options)
|
|
703
|
-
names.map
|
|
703
|
+
names = names.map { |key| normalize_key(key, options) }
|
|
704
704
|
|
|
705
705
|
instrument_multi(:delete_multi, names, options) do
|
|
706
706
|
delete_multi_entries(names, **options)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
ActiveSupport.deprecator.warn <<~MSG
|
|
4
4
|
ActiveSupport::Configurable is deprecated without replacement, and will be removed in Rails 8.2.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
The previous behavior can be emulated with `attr_accessor`s, `class_attribute`s, or combinations of `class_attribute` and `InheritableOptions` depending on requirements.
|
|
7
7
|
MSG
|
|
8
8
|
|
|
9
9
|
require "active_support/concern"
|
|
@@ -63,13 +63,13 @@ module ActiveSupport
|
|
|
63
63
|
# include ActiveSupport::Configurable
|
|
64
64
|
# end
|
|
65
65
|
#
|
|
66
|
-
# User.allowed_access # => nil
|
|
66
|
+
# User.config.allowed_access # => nil
|
|
67
67
|
#
|
|
68
68
|
# User.configure do |config|
|
|
69
69
|
# config.allowed_access = true
|
|
70
70
|
# end
|
|
71
71
|
#
|
|
72
|
-
# User.allowed_access # => true
|
|
72
|
+
# User.config.allowed_access # => true
|
|
73
73
|
def configure
|
|
74
74
|
yield config
|
|
75
75
|
end
|
|
@@ -80,6 +80,7 @@ class DateTime
|
|
|
80
80
|
# largest to smallest. This order can affect the result around the end of a
|
|
81
81
|
# month.
|
|
82
82
|
def advance(options)
|
|
83
|
+
options = options.dup
|
|
83
84
|
unless options[:weeks].nil?
|
|
84
85
|
options[:weeks], partial_weeks = options[:weeks].divmod(1)
|
|
85
86
|
options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
|
|
@@ -196,9 +196,9 @@ module Enumerable
|
|
|
196
196
|
# the +filter+ option is set to +false+.
|
|
197
197
|
def in_order_of(key, series, filter: true)
|
|
198
198
|
if filter
|
|
199
|
-
group_by(&key).values_at(*series).flatten(1)
|
|
199
|
+
group_by(&key).values_at(*series).compact.flatten(1)
|
|
200
200
|
else
|
|
201
|
-
sort_by { |v| series.index(v.public_send(key)) || series.size }
|
|
201
|
+
sort_by { |v| series.index(v.public_send(key)) || series.size }
|
|
202
202
|
end
|
|
203
203
|
end
|
|
204
204
|
|
|
@@ -15,13 +15,7 @@ module ActiveSupport
|
|
|
15
15
|
# The given range must be fully bounded, with both start and end.
|
|
16
16
|
def ===(value)
|
|
17
17
|
if value.is_a?(::Range)
|
|
18
|
-
|
|
19
|
-
return false if value.begin && value.end && value.begin.public_send(is_backwards_op, value.end)
|
|
20
|
-
# 1...10 includes 1..9 but it does not include 1..10.
|
|
21
|
-
# 1..10 includes 1...11 but it does not include 1...12.
|
|
22
|
-
operator = exclude_end? && !value.exclude_end? ? :< : :<=
|
|
23
|
-
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
|
|
24
|
-
super(value.first) && (self.end.nil? || value_max.public_send(operator, last))
|
|
18
|
+
cover?(value)
|
|
25
19
|
else
|
|
26
20
|
super
|
|
27
21
|
end
|
|
@@ -40,13 +34,7 @@ module ActiveSupport
|
|
|
40
34
|
# The given range must be fully bounded, with both start and end.
|
|
41
35
|
def include?(value)
|
|
42
36
|
if value.is_a?(::Range)
|
|
43
|
-
|
|
44
|
-
return false if value.begin && value.end && value.begin.public_send(is_backwards_op, value.end)
|
|
45
|
-
# 1...10 includes 1..9 but it does not include 1..10.
|
|
46
|
-
# 1..10 includes 1...11 but it does not include 1...12.
|
|
47
|
-
operator = exclude_end? && !value.exclude_end? ? :< : :<=
|
|
48
|
-
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
|
|
49
|
-
super(value.first) && (self.end.nil? || value_max.public_send(operator, last))
|
|
37
|
+
cover?(value)
|
|
50
38
|
else
|
|
51
39
|
super
|
|
52
40
|
end
|
|
@@ -72,6 +72,8 @@ class String
|
|
|
72
72
|
|
|
73
73
|
omission = options[:omission] || "..."
|
|
74
74
|
length_with_room_for_omission = truncate_to - omission.length
|
|
75
|
+
return omission.dup if length_with_room_for_omission <= 0
|
|
76
|
+
|
|
75
77
|
stop = \
|
|
76
78
|
if options[:separator]
|
|
77
79
|
rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
|
|
@@ -6,6 +6,7 @@ require "active_support/time_with_zone"
|
|
|
6
6
|
require "active_support/core_ext/time/zones"
|
|
7
7
|
require "active_support/core_ext/date_and_time/calculations"
|
|
8
8
|
require "active_support/core_ext/date/calculations"
|
|
9
|
+
require "active_support/core_ext/module/redefine_method"
|
|
9
10
|
require "active_support/core_ext/module/remove_method"
|
|
10
11
|
|
|
11
12
|
class Time
|
|
@@ -59,6 +60,8 @@ class Time
|
|
|
59
60
|
alias_method :at_without_coercion, :at
|
|
60
61
|
alias_method :at, :at_with_coercion
|
|
61
62
|
|
|
63
|
+
silence_redefinition_of_method :rfc3339
|
|
64
|
+
|
|
62
65
|
# Creates a +Time+ instance from an RFC 3339 string.
|
|
63
66
|
#
|
|
64
67
|
# Time.rfc3339('1999-12-31T14:00:00-10:00') # => 2000-01-01 00:00:00 -1000
|
|
@@ -192,6 +195,7 @@ class Time
|
|
|
192
195
|
# largest to smallest. This order can affect the result around the end of a
|
|
193
196
|
# month.
|
|
194
197
|
def advance(options)
|
|
198
|
+
options = options.dup
|
|
195
199
|
unless options[:weeks].nil?
|
|
196
200
|
options[:weeks], partial_weeks = options[:weeks].divmod(1)
|
|
197
201
|
options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
|
|
@@ -383,42 +383,42 @@ module ActiveSupport
|
|
|
383
383
|
#
|
|
384
384
|
# 1.day.in_minutes # => 1440.0
|
|
385
385
|
def in_minutes
|
|
386
|
-
|
|
386
|
+
value / SECONDS_PER_MINUTE.to_f
|
|
387
387
|
end
|
|
388
388
|
|
|
389
389
|
# Returns the amount of hours a duration covers as a float
|
|
390
390
|
#
|
|
391
391
|
# 1.day.in_hours # => 24.0
|
|
392
392
|
def in_hours
|
|
393
|
-
|
|
393
|
+
value / SECONDS_PER_HOUR.to_f
|
|
394
394
|
end
|
|
395
395
|
|
|
396
396
|
# Returns the amount of days a duration covers as a float
|
|
397
397
|
#
|
|
398
398
|
# 12.hours.in_days # => 0.5
|
|
399
399
|
def in_days
|
|
400
|
-
|
|
400
|
+
value / SECONDS_PER_DAY.to_f
|
|
401
401
|
end
|
|
402
402
|
|
|
403
403
|
# Returns the amount of weeks a duration covers as a float
|
|
404
404
|
#
|
|
405
405
|
# 2.months.in_weeks # => 8.696
|
|
406
406
|
def in_weeks
|
|
407
|
-
|
|
407
|
+
value / SECONDS_PER_WEEK.to_f
|
|
408
408
|
end
|
|
409
409
|
|
|
410
410
|
# Returns the amount of months a duration covers as a float
|
|
411
411
|
#
|
|
412
412
|
# 9.weeks.in_months # => 2.07
|
|
413
413
|
def in_months
|
|
414
|
-
|
|
414
|
+
value / SECONDS_PER_MONTH.to_f
|
|
415
415
|
end
|
|
416
416
|
|
|
417
417
|
# Returns the amount of years a duration covers as a float
|
|
418
418
|
#
|
|
419
419
|
# 30.days.in_years # => 0.082
|
|
420
420
|
def in_years
|
|
421
|
-
|
|
421
|
+
value / SECONDS_PER_YEAR.to_f
|
|
422
422
|
end
|
|
423
423
|
|
|
424
424
|
# Returns +true+ if +other+ is also a Duration instance, which has the
|
|
@@ -128,7 +128,7 @@ module ActiveSupport
|
|
|
128
128
|
# payload: Hash, Object (The payload of the event, or the event object itself)
|
|
129
129
|
# tags: Hash (The tags of the event)
|
|
130
130
|
# context: Hash (The context of the event)
|
|
131
|
-
# timestamp:
|
|
131
|
+
# timestamp: Integer (The timestamp of the event, in nanoseconds)
|
|
132
132
|
# source_location: Hash (The source location of the event, containing the filepath, lineno, and label)
|
|
133
133
|
#
|
|
134
134
|
# Subscribers are responsible for encoding events to their desired format before emitting them to their
|
|
@@ -300,7 +300,7 @@ module ActiveSupport
|
|
|
300
300
|
# payload: Hash, Object (The payload of the event, or the event object itself)
|
|
301
301
|
# tags: Hash (The tags of the event)
|
|
302
302
|
# context: Hash (The context of the event)
|
|
303
|
-
# timestamp:
|
|
303
|
+
# timestamp: Integer (The timestamp of the event, in nanoseconds)
|
|
304
304
|
# source_location: Hash (The source location of the event, containing the filepath, lineno, and label)
|
|
305
305
|
#
|
|
306
306
|
# An optional filter proc can be provided to only receive a subset of events:
|
|
@@ -75,7 +75,7 @@ module ActiveSupport
|
|
|
75
75
|
update(constructor)
|
|
76
76
|
|
|
77
77
|
hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
|
|
78
|
-
self.default = hash.default
|
|
78
|
+
self.default = hash.default unless hash.default.nil?
|
|
79
79
|
self.default_proc = hash.default_proc if hash.default_proc
|
|
80
80
|
else
|
|
81
81
|
super(constructor)
|
|
@@ -338,6 +338,7 @@ module ActiveSupport
|
|
|
338
338
|
return to_enum(:select) unless block_given?
|
|
339
339
|
dup.tap { |hash| hash.select!(*args, &block) }
|
|
340
340
|
end
|
|
341
|
+
alias_method :filter, :select
|
|
341
342
|
|
|
342
343
|
def reject(*args, &block)
|
|
343
344
|
return to_enum(:reject) unless block_given?
|
|
@@ -190,7 +190,7 @@ module ActiveSupport
|
|
|
190
190
|
# titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
|
|
191
191
|
# titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
|
|
192
192
|
def titleize(word, keep_id_suffix: false)
|
|
193
|
-
humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`()])
|
|
193
|
+
humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`()])\p{Lower}/) do |match|
|
|
194
194
|
match.capitalize
|
|
195
195
|
end
|
|
196
196
|
end
|
|
@@ -66,7 +66,10 @@ module ActiveSupport
|
|
|
66
66
|
raise ArgumentError, "Cannot transliterate strings with #{string.encoding} encoding" unless ALLOWED_ENCODINGS_FOR_TRANSLITERATE.include?(string.encoding)
|
|
67
67
|
|
|
68
68
|
return string.dup if string.ascii_only?
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
# Operate on a copy so the in-place force_encoding/encode! below never
|
|
71
|
+
# mutate the caller's (possibly non-frozen) string.
|
|
72
|
+
string = string.dup
|
|
70
73
|
|
|
71
74
|
input_encoding = string.encoding
|
|
72
75
|
|
|
@@ -105,6 +108,11 @@ module ActiveSupport
|
|
|
105
108
|
# parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
|
|
106
109
|
# parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
|
|
107
110
|
#
|
|
111
|
+
# To remove the unwanted characters instead of replacing them, pass an
|
|
112
|
+
# empty separator. +nil+ is treated the same way.
|
|
113
|
+
#
|
|
114
|
+
# parameterize("Donald E. Knuth", separator: '') # => "donaldeknuth"
|
|
115
|
+
#
|
|
108
116
|
# To preserve the case of the characters in a string, use the +preserve_case+ argument.
|
|
109
117
|
#
|
|
110
118
|
# parameterize("Donald E. Knuth", preserve_case: true) # => "Donald-E-Knuth"
|
|
@@ -121,13 +129,15 @@ module ActiveSupport
|
|
|
121
129
|
# By default, this parameter is set to <tt>nil</tt> and it will use
|
|
122
130
|
# the configured <tt>I18n.locale</tt>.
|
|
123
131
|
def parameterize(string, separator: "-", preserve_case: false, locale: nil)
|
|
132
|
+
separator ||= ""
|
|
133
|
+
|
|
124
134
|
# Replace accented chars with their ASCII equivalents.
|
|
125
135
|
parameterized_string = transliterate(string, locale: locale)
|
|
126
136
|
|
|
127
137
|
# Turn unwanted chars into the separator.
|
|
128
138
|
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
|
|
129
139
|
|
|
130
|
-
unless separator.
|
|
140
|
+
unless separator.empty?
|
|
131
141
|
# No more than one of the separator in a row.
|
|
132
142
|
if separator.length == 1
|
|
133
143
|
parameterized_string.squeeze!(separator)
|
|
@@ -21,8 +21,12 @@ module ActiveSupport
|
|
|
21
21
|
# # => {"team" => "rails", "players" => "36"}
|
|
22
22
|
# ActiveSupport::JSON.decode("2.39")
|
|
23
23
|
# # => 2.39
|
|
24
|
-
def decode(json, options =
|
|
25
|
-
data =
|
|
24
|
+
def decode(json, options = nil)
|
|
25
|
+
data = if options
|
|
26
|
+
::JSON.parse(json, **options)
|
|
27
|
+
else
|
|
28
|
+
::JSON.parse(json)
|
|
29
|
+
end
|
|
26
30
|
|
|
27
31
|
if ActiveSupport.parse_json_times
|
|
28
32
|
convert_dates_from(data)
|
|
@@ -150,10 +150,20 @@ module ActiveSupport
|
|
|
150
150
|
class JSONGemCoderEncoder # :nodoc:
|
|
151
151
|
JSON_NATIVE_TYPES = [Hash, Array, Float, String, Symbol, Integer, NilClass, TrueClass, FalseClass, ::JSON::Fragment].freeze
|
|
152
152
|
CODER = ::JSON::Coder.new do |value, is_key|
|
|
153
|
-
|
|
153
|
+
# Serialize non-String/Symbol keys via #to_s based on the key's own type,
|
|
154
|
+
# mirroring the legacy `jsonify` encoder. (#as_json is intentionally not
|
|
155
|
+
# consulted here: Time#as_json returns an ISO8601 String, yet the key must
|
|
156
|
+
# still be emitted via #to_s for backward compatibility.)
|
|
157
|
+
if is_key
|
|
158
|
+
# Keep compatibility by calling to_s on non-String keys
|
|
159
|
+
if Symbol === value
|
|
160
|
+
next value # Symbol#to_s needlessly allocate a string.
|
|
161
|
+
else
|
|
162
|
+
next value.to_s
|
|
163
|
+
end
|
|
164
|
+
end
|
|
154
165
|
|
|
155
|
-
|
|
156
|
-
next value.to_s if is_key && !(String === json_value)
|
|
166
|
+
json_value = value.as_json
|
|
157
167
|
|
|
158
168
|
# Handle objects returning self from as_json
|
|
159
169
|
if json_value.equal?(value)
|
|
@@ -105,8 +105,9 @@ module ActiveSupport
|
|
|
105
105
|
recursive: true
|
|
106
106
|
|
|
107
107
|
registry.register_type 18, ActiveSupport::SafeBuffer,
|
|
108
|
-
packer: :
|
|
109
|
-
unpacker: :
|
|
108
|
+
packer: method(:write_safe_buffer),
|
|
109
|
+
unpacker: method(:read_safe_buffer),
|
|
110
|
+
recursive: true
|
|
110
111
|
end
|
|
111
112
|
|
|
112
113
|
def install_unregistered_type_error(registry)
|
|
@@ -247,6 +248,14 @@ module ActiveSupport
|
|
|
247
248
|
ActiveSupport::HashWithIndifferentAccess.new(unpacker.read)
|
|
248
249
|
end
|
|
249
250
|
|
|
251
|
+
def write_safe_buffer(buffer, packer)
|
|
252
|
+
packer.write(buffer.to_str)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def read_safe_buffer(unpacker)
|
|
256
|
+
ActiveSupport::SafeBuffer.new(unpacker.read)
|
|
257
|
+
end
|
|
258
|
+
|
|
250
259
|
def raise_unserializable(object, *)
|
|
251
260
|
raise UnserializableObjectError, "Unsupported type #{object.class} for object #{object.inspect}"
|
|
252
261
|
end
|
|
@@ -185,8 +185,8 @@ module ActiveSupport
|
|
|
185
185
|
#
|
|
186
186
|
# subscriber = ActiveSupport::Notifications.subscribe(/render/) { }
|
|
187
187
|
# ActiveSupport::Notifications.unsubscribe('render_template.action_view')
|
|
188
|
-
# subscriber.
|
|
189
|
-
# subscriber.
|
|
188
|
+
# subscriber.subscribed_to?('render_template.action_view') # => false
|
|
189
|
+
# subscriber.subscribed_to?('render_partial.action_view') # => true
|
|
190
190
|
#
|
|
191
191
|
# == Default Queue
|
|
192
192
|
#
|
|
@@ -14,7 +14,7 @@ module ActiveSupport
|
|
|
14
14
|
if number_d
|
|
15
15
|
if number_d.negative?
|
|
16
16
|
number_d = number_d.abs
|
|
17
|
-
format = options[:negative_format] if (number_d * 10**options[:precision]) >= 0.5
|
|
17
|
+
format = options[:negative_format] if options[:precision].nil? || (number_d * 10**options[:precision]) >= 0.5
|
|
18
18
|
end
|
|
19
19
|
number_s = NumberToRoundedConverter.convert(number_d, options)
|
|
20
20
|
else
|
|
@@ -7,8 +7,6 @@ module ActiveSupport
|
|
|
7
7
|
class NumberToDelimitedConverter < NumberConverter # :nodoc:
|
|
8
8
|
self.validate_float = true
|
|
9
9
|
|
|
10
|
-
DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
|
|
11
|
-
|
|
12
10
|
def convert
|
|
13
11
|
parts.join(options[:separator])
|
|
14
12
|
end
|
|
@@ -22,6 +20,7 @@ module ActiveSupport
|
|
|
22
20
|
end
|
|
23
21
|
else
|
|
24
22
|
left_parts = []
|
|
23
|
+
sign = left.slice!(0) if left.start_with?("-", "+")
|
|
25
24
|
offset = left.size % 3
|
|
26
25
|
if offset > 0
|
|
27
26
|
left_parts << left[0, offset]
|
|
@@ -31,14 +30,14 @@ module ActiveSupport
|
|
|
31
30
|
left_parts << left[offset + (i * 3), 3]
|
|
32
31
|
end
|
|
33
32
|
|
|
34
|
-
left = left_parts.join(options[:delimiter])
|
|
33
|
+
left = "#{sign}#{left_parts.join(options[:delimiter])}"
|
|
35
34
|
end
|
|
36
35
|
|
|
37
36
|
[left, right].compact
|
|
38
37
|
end
|
|
39
38
|
|
|
40
39
|
def delimiter_pattern
|
|
41
|
-
options
|
|
40
|
+
options[:delimiter_pattern]
|
|
42
41
|
end
|
|
43
42
|
end
|
|
44
43
|
end
|
|
@@ -30,16 +30,12 @@ module ActiveSupport
|
|
|
30
30
|
|
|
31
31
|
def convert_without_area_code(number)
|
|
32
32
|
default_pattern = /(\d{0,3})(\d{3})(\d{4})$/
|
|
33
|
-
number.gsub!(regexp_pattern(default_pattern)
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
number.gsub!(regexp_pattern(default_pattern)) do
|
|
34
|
+
$1.empty? ? "#{$2}#{delimiter}#{$3}" : "#{$1}#{delimiter}#{$2}#{delimiter}#{$3}"
|
|
35
|
+
end
|
|
36
36
|
number
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def start_with_delimiter?(number)
|
|
40
|
-
delimiter.present? && number.start_with?(delimiter)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
39
|
def delimiter
|
|
44
40
|
opts[:delimiter] || "-"
|
|
45
41
|
end
|
|
@@ -35,7 +35,7 @@ module ActiveSupport
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def absolute_precision(number)
|
|
38
|
-
if options[:significant] && options[:precision] > 0
|
|
38
|
+
if options[:significant] && options[:precision] && options[:precision] > 0
|
|
39
39
|
options[:precision] - digit_count(convert_to_decimal(number))
|
|
40
40
|
else
|
|
41
41
|
options[:precision]
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveSupport
|
|
4
4
|
module SecurityUtils
|
|
5
|
-
# Constant time string comparison, for fixed length strings.
|
|
6
|
-
#
|
|
7
|
-
# The values compared should be of fixed length, such as strings
|
|
8
|
-
# that have already been processed by HMAC. Raises in case of length mismatch.
|
|
9
|
-
|
|
10
5
|
if defined?(OpenSSL.fixed_length_secure_compare)
|
|
6
|
+
# Constant time string comparison, for fixed length strings.
|
|
7
|
+
#
|
|
8
|
+
# The values compared should be of fixed length, such as strings
|
|
9
|
+
# that have already been processed by HMAC. Raises in case of length mismatch.
|
|
11
10
|
def fixed_length_secure_compare(a, b)
|
|
12
11
|
OpenSSL.fixed_length_secure_compare(a, b)
|
|
13
12
|
end
|
|
@@ -60,7 +60,8 @@ module ActiveSupport
|
|
|
60
60
|
location = __getobj__.backtrace_locations[0]
|
|
61
61
|
["#{location.path}:#{location.lineno}: #{__getobj__}"]
|
|
62
62
|
else
|
|
63
|
-
|
|
63
|
+
# Only message lines with a file location on them are trace entries
|
|
64
|
+
__getobj__.to_s.split("\n").grep(/:\d+/)
|
|
64
65
|
end
|
|
65
66
|
end
|
|
66
67
|
end
|
|
@@ -99,7 +99,7 @@ module ActiveSupport
|
|
|
99
99
|
"Bucharest" => "Europe/Bucharest",
|
|
100
100
|
"Cairo" => "Africa/Cairo",
|
|
101
101
|
"Helsinki" => "Europe/Helsinki",
|
|
102
|
-
"Kyiv" => "Europe/
|
|
102
|
+
"Kyiv" => "Europe/Kyiv",
|
|
103
103
|
"Riga" => "Europe/Riga",
|
|
104
104
|
"Sofia" => "Europe/Sofia",
|
|
105
105
|
"Tallinn" => "Europe/Tallinn",
|
|
@@ -140,7 +140,7 @@ module ActiveSupport
|
|
|
140
140
|
"Almaty" => "Asia/Almaty",
|
|
141
141
|
"Astana" => "Asia/Almaty",
|
|
142
142
|
"Novosibirsk" => "Asia/Novosibirsk",
|
|
143
|
-
"Rangoon" => "Asia/
|
|
143
|
+
"Rangoon" => "Asia/Yangon",
|
|
144
144
|
"Bangkok" => "Asia/Bangkok",
|
|
145
145
|
"Hanoi" => "Asia/Bangkok",
|
|
146
146
|
"Jakarta" => "Asia/Jakarta",
|
|
@@ -594,7 +594,7 @@ module ActiveSupport
|
|
|
594
594
|
return if parts.empty?
|
|
595
595
|
|
|
596
596
|
if parts[:seconds]
|
|
597
|
-
time = Time.at(parts[:seconds])
|
|
597
|
+
time = Time.at(parts[:seconds] + parts.fetch(:sec_fraction, 0))
|
|
598
598
|
else
|
|
599
599
|
time = Time.new(
|
|
600
600
|
parts.fetch(:year, now.year),
|
|
@@ -65,7 +65,7 @@ module ActiveSupport
|
|
|
65
65
|
unless defined?(PARSING)
|
|
66
66
|
PARSING = {
|
|
67
67
|
"symbol" => Proc.new { |symbol| symbol.to_s.to_sym },
|
|
68
|
-
"date" => Proc.new { |date| ::Date.strptime(date, "%Y-%m-%d") },
|
|
68
|
+
"date" => Proc.new { |date| ::Date.strptime(date.to_s.strip, "%Y-%m-%d") },
|
|
69
69
|
"datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
|
|
70
70
|
"duration" => Proc.new { |duration| Duration.parse(duration) },
|
|
71
71
|
"integer" => Proc.new { |integer| integer.to_i },
|
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.1.
|
|
4
|
+
version: 8.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Heinemeier Hansson
|
|
@@ -501,10 +501,10 @@ licenses:
|
|
|
501
501
|
- MIT
|
|
502
502
|
metadata:
|
|
503
503
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
|
504
|
-
changelog_uri: https://github.com/rails/rails/blob/v8.1.
|
|
505
|
-
documentation_uri: https://api.rubyonrails.org/v8.1.
|
|
504
|
+
changelog_uri: https://github.com/rails/rails/blob/v8.1.4/activesupport/CHANGELOG.md
|
|
505
|
+
documentation_uri: https://api.rubyonrails.org/v8.1.4/
|
|
506
506
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
|
507
|
-
source_code_uri: https://github.com/rails/rails/tree/v8.1.
|
|
507
|
+
source_code_uri: https://github.com/rails/rails/tree/v8.1.4/activesupport
|
|
508
508
|
rubygems_mfa_required: 'true'
|
|
509
509
|
rdoc_options:
|
|
510
510
|
- "--encoding"
|
|
@@ -522,7 +522,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
522
522
|
- !ruby/object:Gem::Version
|
|
523
523
|
version: '0'
|
|
524
524
|
requirements: []
|
|
525
|
-
rubygems_version: 4.0.
|
|
525
|
+
rubygems_version: 4.0.20
|
|
526
526
|
specification_version: 4
|
|
527
527
|
summary: A toolkit of support libraries and Ruby core extensions extracted from the
|
|
528
528
|
Rails framework.
|