activesupport 7.2.2.2 → 7.2.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 +4 -4
- data/CHANGELOG.md +143 -0
- data/README.rdoc +1 -1
- data/lib/active_support/backtrace_cleaner.rb +1 -1
- data/lib/active_support/broadcast_logger.rb +61 -74
- data/lib/active_support/cache/file_store.rb +2 -2
- data/lib/active_support/cache/mem_cache_store.rb +13 -15
- data/lib/active_support/cache/memory_store.rb +5 -5
- data/lib/active_support/cache/null_store.rb +2 -2
- data/lib/active_support/cache/redis_cache_store.rb +1 -1
- data/lib/active_support/cache/strategy/local_cache.rb +56 -20
- data/lib/active_support/cache.rb +3 -3
- data/lib/active_support/callbacks.rb +3 -2
- data/lib/active_support/core_ext/benchmark.rb +1 -0
- data/lib/active_support/core_ext/class/attribute.rb +2 -2
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
- data/lib/active_support/core_ext/enumerable.rb +17 -5
- data/lib/active_support/core_ext/erb/util.rb +2 -2
- data/lib/active_support/core_ext/module/introspection.rb +3 -0
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +1 -0
- data/lib/active_support/core_ext/securerandom.rb +24 -8
- data/lib/active_support/core_ext/string/filters.rb +3 -3
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/time/compatibility.rb +9 -1
- data/lib/active_support/current_attributes.rb +14 -7
- data/lib/active_support/error_reporter.rb +5 -2
- data/lib/active_support/execution_wrapper.rb +1 -1
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +20 -16
- data/lib/active_support/json/decoding.rb +1 -1
- data/lib/active_support/json/encoding.rb +23 -5
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/message_encryptors.rb +2 -2
- data/lib/active_support/message_verifier.rb +9 -0
- data/lib/active_support/message_verifiers.rb +5 -3
- data/lib/active_support/messages/rotator.rb +5 -0
- data/lib/active_support/multibyte/chars.rb +4 -1
- data/lib/active_support/testing/parallelization/server.rb +15 -2
- data/lib/active_support/testing/parallelization/worker.rb +2 -2
- data/lib/active_support/testing/parallelization.rb +12 -1
- data/lib/active_support/xml_mini.rb +2 -0
- metadata +5 -5
- data/lib/active_support/testing/strict_warnings.rb +0 -43
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d12b3bc49972cf65396ee7cb16b370f76853dde525bf3d111e6da8a5936ab441
|
|
4
|
+
data.tar.gz: a97522d2f10dc74dc7d3bc922eb37b3b6c369bd729e26a22e97d250602d6c544
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2a89e3f270073d83270087ad960f1236ee794fb833303b1e97121a1f52b20f07ca79d22c879b474d33d9180b2fbc90359619bd20886a8ab978dabcd7df3df4e2
|
|
7
|
+
data.tar.gz: 0576fb7d4a4bef1d8e9e8a0bbce32f0fbb74ba626e7bbcacabcf50352aaf3c2f6567e4989debe83095164b1f9a16a31ea9c0a9afe55cd4327ffc645f5acbef2d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,146 @@
|
|
|
1
|
+
## Rails 7.2.3 (October 28, 2025) ##
|
|
2
|
+
|
|
3
|
+
* Fix `Enumerable#sole` to return the full tuple instead of just the first element of the tuple.
|
|
4
|
+
|
|
5
|
+
*Olivier Bellone*
|
|
6
|
+
|
|
7
|
+
* Fix parallel tests hanging when worker processes die abruptly.
|
|
8
|
+
|
|
9
|
+
Previously, if a worker process was killed (e.g., OOM killed, `kill -9`) during parallel
|
|
10
|
+
test execution, the test suite would hang forever waiting for the dead worker.
|
|
11
|
+
|
|
12
|
+
*Joshua Young*
|
|
13
|
+
|
|
14
|
+
* `ActiveSupport::FileUpdateChecker` does not depend on `Time.now` to prevent unnecessary reloads with time travel test helpers
|
|
15
|
+
|
|
16
|
+
*Jan Grodowski*
|
|
17
|
+
|
|
18
|
+
* Fix `ActiveSupport::BroadcastLogger` from executing a block argument for each logger (tagged, info, etc.).
|
|
19
|
+
|
|
20
|
+
*Jared Armstrong*
|
|
21
|
+
|
|
22
|
+
* Fix `ActiveSupport::HashWithIndifferentAccess#transform_keys!` removing defaults.
|
|
23
|
+
|
|
24
|
+
*Hartley McGuire*
|
|
25
|
+
|
|
26
|
+
* Fix `ActiveSupport::HashWithIndifferentAccess#tranform_keys!` to handle collisions.
|
|
27
|
+
|
|
28
|
+
If the transformation would result in a key equal to another not yet transformed one,
|
|
29
|
+
it would result in keys being lost.
|
|
30
|
+
|
|
31
|
+
Before:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
>> {a: 1, b: 2}.with_indifferent_access.transform_keys!(&:succ)
|
|
35
|
+
=> {"c" => 1}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
After:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
>> {a: 1, b: 2}.with_indifferent_access.transform_keys!(&:succ)
|
|
42
|
+
=> {"c" => 1, "d" => 2}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
*Jason T Johnson*, *Jean Boussier*
|
|
46
|
+
|
|
47
|
+
* Fix `ActiveSupport::Cache::MemCacheStore#read_multi` to handle network errors.
|
|
48
|
+
|
|
49
|
+
This method specifically wasn't handling network errors like other codepaths.
|
|
50
|
+
|
|
51
|
+
*Alessandro Dal Grande*
|
|
52
|
+
|
|
53
|
+
* Fix Active Support Cache `fetch_multi` when local store is active.
|
|
54
|
+
|
|
55
|
+
`fetch_multi` now properly yield to the provided block for missing entries
|
|
56
|
+
that have been recorded as such in the local store.
|
|
57
|
+
|
|
58
|
+
*Jean Boussier*
|
|
59
|
+
|
|
60
|
+
* Fix execution wrapping to report all exceptions, including `Exception`.
|
|
61
|
+
|
|
62
|
+
If a more serious error like `SystemStackError` or `NoMemoryError` happens,
|
|
63
|
+
the error reporter should be able to report these kinds of exceptions.
|
|
64
|
+
|
|
65
|
+
*Gannon McGibbon*
|
|
66
|
+
|
|
67
|
+
* Fix `RedisCacheStore` and `MemCacheStore` to also handle connection pool related errors.
|
|
68
|
+
|
|
69
|
+
These errors are rescued and reported to `Rails.error`.
|
|
70
|
+
|
|
71
|
+
*Jean Boussier*
|
|
72
|
+
|
|
73
|
+
* Fix `ActiveSupport::Cache#read_multi` to respect version expiry when using local cache.
|
|
74
|
+
|
|
75
|
+
*zzak*
|
|
76
|
+
|
|
77
|
+
* Fix `ActiveSupport::MessageVerifier` and `ActiveSupport::MessageEncryptor` configuration of `on_rotation` callback.
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
verifier.rotate(old_secret).on_rotation { ... }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Now both work as documented.
|
|
84
|
+
|
|
85
|
+
*Jean Boussier*
|
|
86
|
+
|
|
87
|
+
* Fix `ActiveSupport::MessageVerifier` to always be able to verify both URL-safe and URL-unsafe payloads.
|
|
88
|
+
|
|
89
|
+
This is to allow transitioning seemlessly from either configuration without immediately invalidating
|
|
90
|
+
all previously generated signed messages.
|
|
91
|
+
|
|
92
|
+
*Jean Boussier*, *Florent Beaurain*, *Ali Sepehri*
|
|
93
|
+
|
|
94
|
+
* Fix `cache.fetch` to honor the provided expiry when `:race_condition_ttl` is used.
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
cache.fetch("key", expires_in: 1.hour, race_condition_ttl: 5.second) do
|
|
98
|
+
"something"
|
|
99
|
+
end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
In the above example, the final cache entry would have a 10 seconds TTL instead
|
|
103
|
+
of the requested 1 hour.
|
|
104
|
+
|
|
105
|
+
*Dhia*
|
|
106
|
+
|
|
107
|
+
* Better handle procs with splat arguments in `set_callback`.
|
|
108
|
+
|
|
109
|
+
*Radamés Roriz*
|
|
110
|
+
|
|
111
|
+
* Fix `String#mb_chars` to not mutate the receiver.
|
|
112
|
+
|
|
113
|
+
Previously it would call `force_encoding` on the receiver,
|
|
114
|
+
now it dups the receiver first.
|
|
115
|
+
|
|
116
|
+
*Jean Boussier*
|
|
117
|
+
|
|
118
|
+
* Improve `ErrorSubscriber` to also mark error causes as reported.
|
|
119
|
+
|
|
120
|
+
This avoid some cases of errors being reported twice, notably in views because of how
|
|
121
|
+
errors are wrapped in `ActionView::Template::Error`.
|
|
122
|
+
|
|
123
|
+
*Jean Boussier*
|
|
124
|
+
|
|
125
|
+
* Fix `Module#module_parent_name` to return the correct name after the module has been named.
|
|
126
|
+
|
|
127
|
+
When called on an anonymous module, the return value wouldn't change after the module was given a name
|
|
128
|
+
later by being assigned to a constant.
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
mod = Module.new
|
|
132
|
+
mod.module_parent_name # => "Object"
|
|
133
|
+
MyModule::Something = mod
|
|
134
|
+
mod.module_parent_name # => "MyModule"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
*Jean Boussier*
|
|
138
|
+
|
|
139
|
+
* Fix a bug in `ERB::Util.tokenize` that causes incorrect tokenization when ERB tags are preceeded by multibyte characters.
|
|
140
|
+
|
|
141
|
+
*Martin Emde*
|
|
142
|
+
|
|
143
|
+
|
|
1
144
|
## Rails 7.2.2.2 (August 13, 2025) ##
|
|
2
145
|
|
|
3
146
|
* No changes.
|
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
|
|
38
|
+
Feature requests should be discussed on the rubyonrails-core forum here:
|
|
39
39
|
|
|
40
40
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
|
@@ -79,7 +79,7 @@ module ActiveSupport
|
|
|
79
79
|
#
|
|
80
80
|
# # Will turn "/my/rails/root/app/models/person.rb" into "app/models/person.rb"
|
|
81
81
|
# root = "#{Rails.root}/"
|
|
82
|
-
# backtrace_cleaner.add_filter { |line| line.
|
|
82
|
+
# backtrace_cleaner.add_filter { |line| line.delete_prefix(root) }
|
|
83
83
|
def add_filter(&block)
|
|
84
84
|
@filters << block
|
|
85
85
|
end
|
|
@@ -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,131 +104,119 @@ module ActiveSupport
|
|
|
105
104
|
@broadcasts.delete(logger)
|
|
106
105
|
end
|
|
107
106
|
|
|
108
|
-
def level
|
|
109
|
-
@broadcasts.
|
|
110
|
-
|
|
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
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
@formatter = formatter
|
|
149
|
-
end
|
|
113
|
+
def local_level
|
|
114
|
+
loggers = @broadcasts.select { |logger| logger.respond_to?(:local_level) }
|
|
150
115
|
|
|
151
|
-
|
|
152
|
-
|
|
116
|
+
loggers.map do |logger|
|
|
117
|
+
logger.local_level
|
|
118
|
+
end.first
|
|
153
119
|
end
|
|
154
|
-
alias_method :sev_threshold=, :level=
|
|
155
120
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
163
|
-
|
|
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
|
-
#
|
|
167
|
-
# to at least one broadcast.
|
|
139
|
+
# True if the log level allows entries with severity +Logger::DEBUG+ to be written
|
|
140
|
+
# to at least one broadcast. False otherwise.
|
|
168
141
|
def debug?
|
|
169
142
|
@broadcasts.any? { |logger| logger.debug? }
|
|
170
143
|
end
|
|
171
144
|
|
|
172
|
-
# Sets the log level to Logger::DEBUG for the whole broadcast.
|
|
145
|
+
# Sets the log level to +Logger::DEBUG+ for the whole broadcast.
|
|
173
146
|
def debug!
|
|
174
|
-
dispatch
|
|
147
|
+
dispatch(:debug!)
|
|
175
148
|
end
|
|
176
149
|
|
|
177
|
-
#
|
|
178
|
-
# to at least one broadcast.
|
|
150
|
+
# True if the log level allows entries with severity +Logger::INFO+ to be written
|
|
151
|
+
# to at least one broadcast. False otherwise.
|
|
179
152
|
def info?
|
|
180
153
|
@broadcasts.any? { |logger| logger.info? }
|
|
181
154
|
end
|
|
182
155
|
|
|
183
|
-
# Sets the log level to Logger::INFO for the whole broadcast.
|
|
156
|
+
# Sets the log level to +Logger::INFO+ for the whole broadcast.
|
|
184
157
|
def info!
|
|
185
|
-
dispatch
|
|
158
|
+
dispatch(:info!)
|
|
186
159
|
end
|
|
187
160
|
|
|
188
|
-
#
|
|
189
|
-
# to at least one broadcast.
|
|
161
|
+
# True if the log level allows entries with severity +Logger::WARN+ to be written
|
|
162
|
+
# to at least one broadcast. False otherwise.
|
|
190
163
|
def warn?
|
|
191
164
|
@broadcasts.any? { |logger| logger.warn? }
|
|
192
165
|
end
|
|
193
166
|
|
|
194
|
-
# Sets the log level to Logger::WARN for the whole broadcast.
|
|
167
|
+
# Sets the log level to +Logger::WARN+ for the whole broadcast.
|
|
195
168
|
def warn!
|
|
196
|
-
dispatch
|
|
169
|
+
dispatch(:warn!)
|
|
197
170
|
end
|
|
198
171
|
|
|
199
|
-
#
|
|
200
|
-
# to at least one broadcast.
|
|
172
|
+
# True if the log level allows entries with severity +Logger::ERROR+ to be written
|
|
173
|
+
# to at least one broadcast. False otherwise.
|
|
201
174
|
def error?
|
|
202
175
|
@broadcasts.any? { |logger| logger.error? }
|
|
203
176
|
end
|
|
204
177
|
|
|
205
|
-
# Sets the log level to Logger::ERROR for the whole broadcast.
|
|
178
|
+
# Sets the log level to +Logger::ERROR+ for the whole broadcast.
|
|
206
179
|
def error!
|
|
207
|
-
dispatch
|
|
180
|
+
dispatch(:error!)
|
|
208
181
|
end
|
|
209
182
|
|
|
210
|
-
#
|
|
211
|
-
# to at least one broadcast.
|
|
183
|
+
# True if the log level allows entries with severity +Logger::FATAL+ to be written
|
|
184
|
+
# to at least one broadcast. False otherwise.
|
|
212
185
|
def fatal?
|
|
213
186
|
@broadcasts.any? { |logger| logger.fatal? }
|
|
214
187
|
end
|
|
215
188
|
|
|
216
|
-
# Sets the log level to Logger::FATAL for the whole broadcast.
|
|
189
|
+
# Sets the log level to +Logger::FATAL+ for the whole broadcast.
|
|
217
190
|
def fatal!
|
|
218
|
-
dispatch
|
|
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
|
-
|
|
232
|
-
|
|
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, ...)
|
|
@@ -57,7 +57,7 @@ module ActiveSupport
|
|
|
57
57
|
# cache.write("baz", 5)
|
|
58
58
|
# cache.increment("baz") # => 6
|
|
59
59
|
#
|
|
60
|
-
def increment(name, amount = 1, options
|
|
60
|
+
def increment(name, amount = 1, **options)
|
|
61
61
|
modify_value(name, amount, options)
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -72,7 +72,7 @@ module ActiveSupport
|
|
|
72
72
|
# cache.write("baz", 5)
|
|
73
73
|
# cache.decrement("baz") # => 4
|
|
74
74
|
#
|
|
75
|
-
def decrement(name, amount = 1, options
|
|
75
|
+
def decrement(name, amount = 1, **options)
|
|
76
76
|
modify_value(name, -amount, options)
|
|
77
77
|
end
|
|
78
78
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
218
|
+
values = {}
|
|
222
219
|
|
|
223
|
-
|
|
224
|
-
|
|
220
|
+
raw_values.each do |key, value|
|
|
221
|
+
entry = deserialize_entry(value, raw: options[:raw])
|
|
225
222
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
231
|
+
values
|
|
232
|
+
end
|
|
235
233
|
end
|
|
236
234
|
|
|
237
235
|
# Delete an entry from the cache.
|
|
@@ -276,7 +274,7 @@ module ActiveSupport
|
|
|
276
274
|
|
|
277
275
|
def rescue_error_with(fallback)
|
|
278
276
|
yield
|
|
279
|
-
rescue Dalli::DalliError => error
|
|
277
|
+
rescue Dalli::DalliError, ConnectionPool::Error, ConnectionPool::TimeoutError => error
|
|
280
278
|
logger.error("DalliError (#{error}): #{error.message}") if logger
|
|
281
279
|
ActiveSupport.error_reporter&.report(
|
|
282
280
|
error,
|
|
@@ -146,8 +146,8 @@ module ActiveSupport
|
|
|
146
146
|
# cache.write("baz", 5)
|
|
147
147
|
# cache.increment("baz") # => 6
|
|
148
148
|
#
|
|
149
|
-
def increment(name, amount = 1, options
|
|
150
|
-
modify_value(name, amount, options)
|
|
149
|
+
def increment(name, amount = 1, **options)
|
|
150
|
+
modify_value(name, amount, **options)
|
|
151
151
|
end
|
|
152
152
|
|
|
153
153
|
# Decrement a cached integer value. Returns the updated value.
|
|
@@ -161,8 +161,8 @@ module ActiveSupport
|
|
|
161
161
|
# cache.write("baz", 5)
|
|
162
162
|
# cache.decrement("baz") # => 4
|
|
163
163
|
#
|
|
164
|
-
def decrement(name, amount = 1, options
|
|
165
|
-
modify_value(name, -amount, options)
|
|
164
|
+
def decrement(name, amount = 1, **options)
|
|
165
|
+
modify_value(name, -amount, **options)
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
# Deletes cache entries if the cache key matches a given pattern.
|
|
@@ -234,7 +234,7 @@ module ActiveSupport
|
|
|
234
234
|
|
|
235
235
|
# Modifies the amount of an integer value that is stored in the cache.
|
|
236
236
|
# If the key is not found it is created and set to +amount+.
|
|
237
|
-
def modify_value(name, amount, options)
|
|
237
|
+
def modify_value(name, amount, **options)
|
|
238
238
|
options = merged_options(options)
|
|
239
239
|
key = normalize_key(name, options)
|
|
240
240
|
version = normalize_version(name, options)
|
|
@@ -25,10 +25,10 @@ module ActiveSupport
|
|
|
25
25
|
def cleanup(options = nil)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def increment(name, amount = 1, options
|
|
28
|
+
def increment(name, amount = 1, **options)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def decrement(name, amount = 1, options
|
|
31
|
+
def decrement(name, amount = 1, **options)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def delete_matched(matcher, options = nil)
|
|
@@ -480,7 +480,7 @@ module ActiveSupport
|
|
|
480
480
|
|
|
481
481
|
def failsafe(method, returning: nil)
|
|
482
482
|
yield
|
|
483
|
-
rescue ::Redis::BaseError => error
|
|
483
|
+
rescue ::Redis::BaseError, ConnectionPool::Error, ConnectionPool::TimeoutError => error
|
|
484
484
|
@error_handler&.call(method: method, exception: error, returning: returning)
|
|
485
485
|
returning
|
|
486
486
|
end
|
|
@@ -94,28 +94,54 @@ module ActiveSupport
|
|
|
94
94
|
super
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
def increment(name, amount = 1, options
|
|
97
|
+
def increment(name, amount = 1, **options) # :nodoc:
|
|
98
98
|
return super unless local_cache
|
|
99
99
|
value = bypass_local_cache { super }
|
|
100
|
-
|
|
101
|
-
write_cache_value(name, value, raw: true, **options)
|
|
102
|
-
else
|
|
103
|
-
write_cache_value(name, value, raw: true)
|
|
104
|
-
end
|
|
100
|
+
write_cache_value(name, value, raw: true, **options)
|
|
105
101
|
value
|
|
106
102
|
end
|
|
107
103
|
|
|
108
|
-
def decrement(name, amount = 1, options
|
|
104
|
+
def decrement(name, amount = 1, **options) # :nodoc:
|
|
109
105
|
return super unless local_cache
|
|
110
106
|
value = bypass_local_cache { super }
|
|
111
|
-
|
|
112
|
-
write_cache_value(name, value, raw: true, **options)
|
|
113
|
-
else
|
|
114
|
-
write_cache_value(name, value, raw: true)
|
|
115
|
-
end
|
|
107
|
+
write_cache_value(name, value, raw: true, **options)
|
|
116
108
|
value
|
|
117
109
|
end
|
|
118
110
|
|
|
111
|
+
def fetch_multi(*names, &block) # :nodoc:
|
|
112
|
+
return super if local_cache.nil? || names.empty?
|
|
113
|
+
|
|
114
|
+
options = names.extract_options!
|
|
115
|
+
options = merged_options(options)
|
|
116
|
+
|
|
117
|
+
keys_to_names = names.index_by { |name| normalize_key(name, options) }
|
|
118
|
+
|
|
119
|
+
local_entries = local_cache.read_multi_entries(keys_to_names.keys)
|
|
120
|
+
results = local_entries.each_with_object({}) do |(key, value), result|
|
|
121
|
+
# If we recorded a miss in the local cache, `#fetch_multi` will forward
|
|
122
|
+
# that key to the real store, and the entry will be replaced
|
|
123
|
+
# local_cache.delete_entry(key)
|
|
124
|
+
next if value.nil?
|
|
125
|
+
|
|
126
|
+
entry = deserialize_entry(value, **options)
|
|
127
|
+
|
|
128
|
+
normalized_key = keys_to_names[key]
|
|
129
|
+
if entry.nil?
|
|
130
|
+
result[normalized_key] = nil
|
|
131
|
+
elsif entry.expired? || entry.mismatched?(normalize_version(normalized_key, options))
|
|
132
|
+
local_cache.delete_entry(key)
|
|
133
|
+
else
|
|
134
|
+
result[normalized_key] = entry.value
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
if results.size < names.size
|
|
139
|
+
results.merge!(super(*(names - results.keys), options, &block))
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
results
|
|
143
|
+
end
|
|
144
|
+
|
|
119
145
|
private
|
|
120
146
|
def read_serialized_entry(key, raw: false, **options)
|
|
121
147
|
if cache = local_cache
|
|
@@ -137,17 +163,27 @@ module ActiveSupport
|
|
|
137
163
|
keys_to_names = names.index_by { |name| normalize_key(name, options) }
|
|
138
164
|
|
|
139
165
|
local_entries = local_cache.read_multi_entries(keys_to_names.keys)
|
|
140
|
-
|
|
141
|
-
local_entries.
|
|
142
|
-
|
|
166
|
+
|
|
167
|
+
results = local_entries.each_with_object({}) do |(key, value), result|
|
|
168
|
+
next if value.nil? # recorded cache miss
|
|
169
|
+
|
|
170
|
+
entry = deserialize_entry(value, **options)
|
|
171
|
+
|
|
172
|
+
normalized_key = keys_to_names[key]
|
|
173
|
+
if entry.nil?
|
|
174
|
+
result[normalized_key] = nil
|
|
175
|
+
elsif entry.expired? || entry.mismatched?(normalize_version(normalized_key, options))
|
|
176
|
+
local_cache.delete_entry(key)
|
|
177
|
+
else
|
|
178
|
+
result[normalized_key] = entry.value
|
|
179
|
+
end
|
|
143
180
|
end
|
|
144
|
-
missed_names = names - local_entries.keys
|
|
145
181
|
|
|
146
|
-
if
|
|
147
|
-
|
|
148
|
-
else
|
|
149
|
-
local_entries
|
|
182
|
+
if results.size < names.size
|
|
183
|
+
results.merge!(super(names - results.keys, **options))
|
|
150
184
|
end
|
|
185
|
+
|
|
186
|
+
results
|
|
151
187
|
end
|
|
152
188
|
|
|
153
189
|
def write_serialized_entry(key, payload, **)
|
data/lib/active_support/cache.rb
CHANGED
|
@@ -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.
|
|
@@ -386,7 +387,7 @@ module ActiveSupport
|
|
|
386
387
|
# process can try to generate a new value after the extended time window
|
|
387
388
|
# has elapsed.
|
|
388
389
|
#
|
|
389
|
-
# # Set all values to expire after one
|
|
390
|
+
# # Set all values to expire after one second.
|
|
390
391
|
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1)
|
|
391
392
|
#
|
|
392
393
|
# cache.write("foo", "original value")
|
|
@@ -1030,8 +1031,7 @@ module ActiveSupport
|
|
|
1030
1031
|
# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
|
|
1031
1032
|
# for a brief period while the entry is being recalculated.
|
|
1032
1033
|
entry.expires_at = Time.now.to_f + race_ttl
|
|
1033
|
-
options
|
|
1034
|
-
write_entry(key, entry, **options)
|
|
1034
|
+
write_entry(key, entry, **options, expires_in: race_ttl * 2)
|
|
1035
1035
|
else
|
|
1036
1036
|
delete_entry(key, **options)
|
|
1037
1037
|
end
|
|
@@ -499,9 +499,10 @@ module ActiveSupport
|
|
|
499
499
|
when Conditionals::Value
|
|
500
500
|
ProcCall.new(filter)
|
|
501
501
|
when ::Proc
|
|
502
|
-
|
|
502
|
+
case filter.arity
|
|
503
|
+
when 2
|
|
503
504
|
InstanceExec2.new(filter)
|
|
504
|
-
|
|
505
|
+
when 1, -2
|
|
505
506
|
InstanceExec1.new(filter)
|
|
506
507
|
else
|
|
507
508
|
InstanceExec0.new(filter)
|
|
@@ -83,8 +83,8 @@ class Class
|
|
|
83
83
|
#
|
|
84
84
|
# class_attribute :settings, default: {}
|
|
85
85
|
def class_attribute(*attrs, instance_accessor: true,
|
|
86
|
-
instance_reader: instance_accessor, instance_writer: instance_accessor, instance_predicate: true, default: nil
|
|
87
|
-
|
|
86
|
+
instance_reader: instance_accessor, instance_writer: instance_accessor, instance_predicate: true, default: nil
|
|
87
|
+
)
|
|
88
88
|
class_methods, methods = [], []
|
|
89
89
|
attrs.each do |name|
|
|
90
90
|
unless name.is_a?(Symbol) || name.is_a?(String)
|