activesupport 7.1.1 → 7.1.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +201 -0
  3. data/lib/active_support/backtrace_cleaner.rb +5 -0
  4. data/lib/active_support/broadcast_logger.rb +23 -14
  5. data/lib/active_support/cache/entry.rb +7 -1
  6. data/lib/active_support/cache/file_store.rb +1 -1
  7. data/lib/active_support/cache/mem_cache_store.rb +16 -8
  8. data/lib/active_support/cache/memory_store.rb +4 -4
  9. data/lib/active_support/cache/redis_cache_store.rb +21 -14
  10. data/lib/active_support/cache/strategy/local_cache.rb +9 -6
  11. data/lib/active_support/cache.rb +36 -8
  12. data/lib/active_support/code_generator.rb +15 -10
  13. data/lib/active_support/core_ext/date/conversions.rb +1 -1
  14. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  15. data/lib/active_support/core_ext/module/delegation.rb +41 -26
  16. data/lib/active_support/core_ext/object/duplicable.rb +24 -15
  17. data/lib/active_support/core_ext/object/json.rb +5 -3
  18. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  19. data/lib/active_support/core_ext/string/indent.rb +1 -1
  20. data/lib/active_support/deprecation/behaviors.rb +18 -16
  21. data/lib/active_support/deprecation/reporting.rb +8 -5
  22. data/lib/active_support/gem_version.rb +1 -1
  23. data/lib/active_support/html_safe_translation.rb +16 -6
  24. data/lib/active_support/inflector/methods.rb +2 -2
  25. data/lib/active_support/log_subscriber.rb +9 -2
  26. data/lib/active_support/messages/codec.rb +1 -1
  27. data/lib/active_support/messages/metadata.rb +1 -1
  28. data/lib/active_support/notifications/fanout.rb +25 -19
  29. data/lib/active_support/notifications/instrumenter.rb +11 -3
  30. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  31. data/lib/active_support/number_helper.rb +379 -318
  32. data/lib/active_support/ordered_options.rb +2 -2
  33. data/lib/active_support/railtie.rb +3 -3
  34. data/lib/active_support/syntax_error_proxy.rb +12 -1
  35. data/lib/active_support/tagged_logging.rb +4 -0
  36. data/lib/active_support/testing/assertions.rb +1 -1
  37. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  38. data/lib/active_support/testing/strict_warnings.rb +1 -0
  39. data/lib/active_support/testing/time_helpers.rb +5 -1
  40. data/lib/active_support/values/time_zone.rb +9 -0
  41. data/lib/active_support.rb +1 -1
  42. metadata +51 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ecb3bcd9549f6429e9a7b2e3e54bd529a71608135834c95ecad50ed60dd8935a
4
- data.tar.gz: 23c6ef54d1fbef9a19494322bae6ed4f4719d1a9f0b60e3f507dc1ccd4cf9adf
3
+ metadata.gz: 1d05cf683e37c600f522c4be11034dfa45e30ef52cea9e41dc0087a624fd4063
4
+ data.tar.gz: 7e7ba21207beeb10e1093ac45cd02a096fda507504e229f8df559b746937f1e9
5
5
  SHA512:
6
- metadata.gz: abdba26b48e19b8f107f9110e2934f315f8f4fcf87340309f4786faf0998903a21e791d362e106550439088016039c692e00c90b380b5b7602b497244ea30419
7
- data.tar.gz: 8f256ca213eea9aed13061094dca338c5886ba2bd3454036cd39649938399cc41ec74ff09ec28beabc21b9a57713c98e60125b13306e63a56aa908ad269b2316
6
+ metadata.gz: 96c1bc0122ee7fcd400846ada8641591d06ca82c42bb4d35bc87c7c8062826b1aa6eec7d2fe81144947353ff29195fd9ca3b4b307c9d21aa366a0717c974eb40
7
+ data.tar.gz: 668f85b8c485c9bc089781d623d3e6a6c84e8e2c57afb350d35ce346ad629679df837e9c9ea8db31b72a8b05839ab296f5e8ac8289103195ecd27b227ebe696d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,204 @@
1
+ ## Rails 7.1.5 (October 30, 2024) ##
2
+
3
+ * No changes.
4
+
5
+
6
+ ## Rails 7.1.4.2 (October 23, 2024) ##
7
+
8
+ * No changes.
9
+
10
+
11
+ ## Rails 7.1.4.1 (October 15, 2024) ##
12
+
13
+ * No changes.
14
+
15
+
16
+ ## Rails 7.1.4 (August 22, 2024) ##
17
+
18
+ * Improve compatibility for `ActiveSupport::BroadcastLogger`.
19
+
20
+ *Máximo Mussini*
21
+
22
+ * Pass options along to write_entry in handle_expired_entry method.
23
+
24
+ *Graham Cooper*
25
+
26
+ * Fix Active Support configurations deprecations.
27
+
28
+ *fatkodima*
29
+
30
+ * Fix teardown callbacks.
31
+
32
+ *Tristan Starck*
33
+
34
+ * `BacktraceCleaner` silence core internal methods by default.
35
+
36
+ *Jean Boussier*
37
+
38
+ * Fix `delegate_missing_to allow_nil: true` when called with implict self
39
+
40
+ ```ruby
41
+ class Person
42
+ delegate_missing_to :address, allow_nil: true
43
+
44
+ def address
45
+ nil
46
+ end
47
+
48
+ def berliner?
49
+ city == "Berlin"
50
+ end
51
+ end
52
+
53
+ Person.new.city # => nil
54
+ Person.new.berliner? # undefined local variable or method `city' for an instance of Person (NameError)
55
+ ```
56
+
57
+ *Jean Boussier*
58
+
59
+ * Work around a Ruby bug that can cause a VM crash.
60
+
61
+ This would happen if using `TaggerLogger` with a Proc
62
+ formatter on which you called `object_id`.
63
+
64
+ ```
65
+ [BUG] Object ID seen, but not in mapping table: proc
66
+ ```
67
+
68
+ *Jean Boussier*
69
+
70
+ * Fix `ActiveSupport::Notifications.publish_event` to preserve units.
71
+
72
+ This solves the incorrect reporting of time spent running Active Record
73
+ asynchronous queries (by a factor `1000`).
74
+
75
+ *Jean Boussier*
76
+
77
+
78
+ ## Rails 7.1.3.4 (June 04, 2024) ##
79
+
80
+ * No changes.
81
+
82
+
83
+ ## Rails 7.1.3.3 (May 16, 2024) ##
84
+
85
+ * No changes.
86
+
87
+
88
+ ## Rails 7.1.3.2 (February 21, 2024) ##
89
+
90
+ * No changes.
91
+
92
+
93
+ ## Rails 7.1.3.1 (February 21, 2024) ##
94
+
95
+ * No changes.
96
+
97
+
98
+ ## Rails 7.1.3 (January 16, 2024) ##
99
+
100
+ * Handle nil `backtrace_locations` in `ActiveSupport::SyntaxErrorProxy`.
101
+
102
+ *Eugene Kenny*
103
+
104
+ * Fix `ActiveSupport::JSON.encode` to prevent duplicate keys.
105
+
106
+ If the same key exist in both String and Symbol form it could
107
+ lead to the same key being emitted twice.
108
+
109
+ *Manish Sharma*
110
+
111
+ * Fix `ActiveSupport::Cache::Store#read_multi` when using a cache namespace
112
+ and local cache strategy.
113
+
114
+ *Mark Oleson*
115
+
116
+ * Fix `Time.now/DateTime.now/Date.today` to return results in a system timezone after `#travel_to`.
117
+
118
+ There is a bug in the current implementation of #travel_to:
119
+ it remembers a timezone of its argument, and all stubbed methods start
120
+ returning results in that remembered timezone. However, the expected
121
+ behaviour is to return results in a system timezone.
122
+
123
+ *Aleksei Chernenkov*
124
+
125
+ * Fix `:unless_exist` option for `MemoryStore#write` (et al) when using a
126
+ cache namespace.
127
+
128
+ *S. Brent Faulkner*
129
+
130
+ * Fix ActiveSupport::Deprecation to handle blaming generated code.
131
+
132
+ *Jean Boussier*, *fatkodima*
133
+
134
+
135
+ ## Rails 7.1.2 (November 10, 2023) ##
136
+
137
+ * Fix `:expires_in` option for `RedisCacheStore#write_multi`.
138
+
139
+ *fatkodima*
140
+
141
+ * Fix deserialization of non-string "purpose" field in Message serializer
142
+
143
+ *Jacopo Beschi*
144
+
145
+ * Prevent global cache options being overwritten when setting dynamic options
146
+ inside a `ActiveSupport::Cache::Store#fetch` block.
147
+
148
+ *Yasha Krasnou*
149
+
150
+ * Fix missing `require` resulting in `NoMethodError` when running
151
+ `bin/rails secrets:show` or `bin/rails secrets:edit`.
152
+
153
+ *Stephen Ierodiaconou*
154
+
155
+ * Ensure `{down,up}case_first` returns non-frozen string.
156
+
157
+ *Jonathan Hefner*
158
+
159
+ * Fix `#to_fs(:human_size)` to correctly work with negative numbers.
160
+
161
+ *Earlopain*
162
+
163
+ * Fix `BroadcastLogger#dup` so that it duplicates the logger's `broadcasts`.
164
+
165
+ *Andrew Novoselac*
166
+
167
+ * Fix issue where `bootstrap.rb` overwrites the `level` of a `BroadcastLogger`'s `broadcasts`.
168
+
169
+ *Andrew Novoselac*
170
+
171
+ * Fix `ActiveSupport::Cache` to handle outdated Marshal payload from Rails 6.1 format.
172
+
173
+ Active Support's Cache is supposed to treat a Marshal payload that can no longer be
174
+ deserialized as a cache miss. It fail to do so for compressed payload in the Rails 6.1
175
+ legacy format.
176
+
177
+ *Jean Boussier*
178
+
179
+ * Fix `OrderedOptions#dig` for array indexes.
180
+
181
+ *fatkodima*
182
+
183
+ * Fix time travel helpers to work when nested using with separate classes.
184
+
185
+ *fatkodima*
186
+
187
+ * Fix `delete_matched` for file cache store to work with keys longer than the
188
+ max filename size.
189
+
190
+ *fatkodima* and *Jonathan Hefner*
191
+
192
+ * Fix compatibility with the `semantic_logger` gem.
193
+
194
+ The `semantic_logger` gem doesn't behave exactly like stdlib logger in that
195
+ `SemanticLogger#level` returns a Symbol while stdlib `Logger#level` returns an Integer.
196
+
197
+ This caused the various `LogSubscriber` classes in Rails to break when assigned a
198
+ `SemanticLogger` instance.
199
+
200
+ *Jean Boussier*, *ojab*
201
+
1
202
  ## Rails 7.1.1 (October 11, 2023) ##
2
203
 
3
204
  * Add support for keyword arguments when delegating calls to custom loggers from `ActiveSupport::BroadcastLogger`.
@@ -33,6 +33,7 @@ module ActiveSupport
33
33
  class BacktraceCleaner
34
34
  def initialize
35
35
  @filters, @silencers = [], []
36
+ add_core_silencer
36
37
  add_gem_filter
37
38
  add_gem_silencer
38
39
  add_stdlib_silencer
@@ -116,6 +117,10 @@ module ActiveSupport
116
117
  add_filter { |line| line.sub(gems_regexp, gems_result) }
117
118
  end
118
119
 
120
+ def add_core_silencer
121
+ add_silencer { |line| line.include?("<internal:") }
122
+ end
123
+
119
124
  def add_gem_silencer
120
125
  add_silencer { |line| FORMATTED_GEMS_PATTERN.match?(line) }
121
126
  end
@@ -113,33 +113,33 @@ module ActiveSupport
113
113
  dispatch { |logger| logger.<<(message) }
114
114
  end
115
115
 
116
- def add(*args, &block)
117
- dispatch { |logger| logger.add(*args, &block) }
116
+ def add(...)
117
+ dispatch { |logger| logger.add(...) }
118
118
  end
119
119
  alias_method :log, :add
120
120
 
121
- def debug(*args, &block)
122
- dispatch { |logger| logger.debug(*args, &block) }
121
+ def debug(...)
122
+ dispatch { |logger| logger.debug(...) }
123
123
  end
124
124
 
125
- def info(*args, &block)
126
- dispatch { |logger| logger.info(*args, &block) }
125
+ def info(...)
126
+ dispatch { |logger| logger.info(...) }
127
127
  end
128
128
 
129
- def warn(*args, &block)
130
- dispatch { |logger| logger.warn(*args, &block) }
129
+ def warn(...)
130
+ dispatch { |logger| logger.warn(...) }
131
131
  end
132
132
 
133
- def error(*args, &block)
134
- dispatch { |logger| logger.error(*args, &block) }
133
+ def error(...)
134
+ dispatch { |logger| logger.error(...) }
135
135
  end
136
136
 
137
- def fatal(*args, &block)
138
- dispatch { |logger| logger.fatal(*args, &block) }
137
+ def fatal(...)
138
+ dispatch { |logger| logger.fatal(...) }
139
139
  end
140
140
 
141
- def unknown(*args, &block)
142
- dispatch { |logger| logger.unknown(*args, &block) }
141
+ def unknown(...)
142
+ dispatch { |logger| logger.unknown(...) }
143
143
  end
144
144
 
145
145
  def formatter=(formatter)
@@ -218,9 +218,18 @@ module ActiveSupport
218
218
  dispatch { |logger| logger.fatal! }
219
219
  end
220
220
 
221
+ def initialize_copy(other)
222
+ @broadcasts = []
223
+ @progname = other.progname.dup
224
+ @formatter = other.formatter.dup
225
+
226
+ broadcast_to(*other.broadcasts.map(&:dup))
227
+ end
228
+
221
229
  private
222
230
  def dispatch(&block)
223
231
  @broadcasts.each { |logger| block.call(logger) }
232
+ true
224
233
  end
225
234
 
226
235
  def method_missing(name, *args, **kwargs, &block)
@@ -121,7 +121,13 @@ module ActiveSupport
121
121
 
122
122
  private
123
123
  def uncompress(value)
124
- Marshal.load(Zlib::Inflate.inflate(value))
124
+ marshal_load(Zlib::Inflate.inflate(value))
125
+ end
126
+
127
+ def marshal_load(payload)
128
+ Marshal.load(payload)
129
+ rescue ArgumentError => error
130
+ raise Cache::DeserializationError, error.message
125
131
  end
126
132
  end
127
133
  end
@@ -176,7 +176,7 @@ module ActiveSupport
176
176
 
177
177
  # Translate a file path into a key.
178
178
  def file_path_key(path)
179
- fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
179
+ fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last.delete(File::SEPARATOR)
180
180
  URI.decode_www_form_component(fname, Encoding::UTF_8)
181
181
  end
182
182
 
@@ -24,11 +24,11 @@ module ActiveSupport
24
24
  #
25
25
  # Special features:
26
26
  # - Clustering and load balancing. One can specify multiple memcached servers,
27
- # and MemCacheStore will load balance between all available servers. If a
28
- # server goes down, then MemCacheStore will ignore it until it comes back up.
27
+ # and +MemCacheStore+ will load balance between all available servers. If a
28
+ # server goes down, then +MemCacheStore+ will ignore it until it comes back up.
29
29
  #
30
- # MemCacheStore implements the Strategy::LocalCache strategy which implements
31
- # an in-memory cache inside of a block.
30
+ # +MemCacheStore+ implements the Strategy::LocalCache strategy which
31
+ # implements an in-memory cache inside of a block.
32
32
  class MemCacheStore < Store
33
33
  # These options represent behavior overridden by this implementation and should
34
34
  # not be allowed to get down to the Dalli client
@@ -106,14 +106,14 @@ module ActiveSupport
106
106
  end
107
107
  end
108
108
 
109
- # Creates a new MemCacheStore object, with the given memcached server
109
+ # Creates a new +MemCacheStore+ object, with the given memcached server
110
110
  # addresses. Each address is either a host name, or a host-with-port string
111
111
  # in the form of "host_name:port". For example:
112
112
  #
113
113
  # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
114
114
  #
115
115
  # If no addresses are provided, but <tt>ENV['MEMCACHE_SERVERS']</tt> is defined, it will be used instead. Otherwise,
116
- # MemCacheStore will connect to localhost:11211 (the default memcached port).
116
+ # +MemCacheStore+ will connect to localhost:11211 (the default memcached port).
117
117
  # Passing a +Dalli::Client+ instance is deprecated and will be removed. Please pass an address instead.
118
118
  def initialize(*addresses)
119
119
  addresses = addresses.flatten
@@ -270,14 +270,22 @@ module ActiveSupport
270
270
  def read_multi_entries(names, **options)
271
271
  keys_to_names = names.index_by { |name| normalize_key(name, options) }
272
272
 
273
- raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
273
+ raw_values = begin
274
+ @data.with { |c| c.get_multi(keys_to_names.keys) }
275
+ rescue Dalli::UnmarshalError
276
+ {}
277
+ end
278
+
274
279
  values = {}
275
280
 
276
281
  raw_values.each do |key, value|
277
282
  entry = deserialize_entry(value, raw: options[:raw])
278
283
 
279
284
  unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
280
- values[keys_to_names[key]] = entry.value
285
+ begin
286
+ values[keys_to_names[key]] = entry.value
287
+ rescue DeserializationError
288
+ end
281
289
  end
282
290
  end
283
291
 
@@ -18,13 +18,13 @@ module ActiveSupport
18
18
  # a cleanup will occur which tries to prune the cache down to three quarters
19
19
  # of the maximum size by removing the least recently used entries.
20
20
  #
21
- # Unlike other Cache store implementations, MemoryStore does not compress
22
- # values by default. MemoryStore does not benefit from compression as much
21
+ # Unlike other Cache store implementations, +MemoryStore+ does not compress
22
+ # values by default. +MemoryStore+ does not benefit from compression as much
23
23
  # as other Store implementations, as it does not send data over a network.
24
24
  # However, when compression is enabled, it still pays the full cost of
25
25
  # compression in terms of cpu use.
26
26
  #
27
- # MemoryStore is thread-safe.
27
+ # +MemoryStore+ is thread-safe.
28
28
  class MemoryStore < Store
29
29
  module DupCoder # :nodoc:
30
30
  extend self
@@ -209,7 +209,7 @@ module ActiveSupport
209
209
  def write_entry(key, entry, **options)
210
210
  payload = serialize_entry(entry, **options)
211
211
  synchronize do
212
- return false if options[:unless_exist] && exist?(key)
212
+ return false if options[:unless_exist] && exist?(key, namespace: nil)
213
213
 
214
214
  old_payload = @data[key]
215
215
  if old_payload
@@ -19,22 +19,23 @@ module ActiveSupport
19
19
  module Cache
20
20
  # = Redis \Cache \Store
21
21
  #
22
- # Deployment note: Take care to use a *dedicated Redis cache* rather
23
- # than pointing this at your existing Redis server. It won't cope well
24
- # with mixed usage patterns and it won't expire cache entries by default.
22
+ # Deployment note: Take care to use a <b>dedicated Redis cache</b> rather
23
+ # than pointing this at a persistent Redis server (for example, one used as
24
+ # an Active Job queue). Redis won't cope well with mixed usage patterns and it
25
+ # won't expire cache entries by default.
25
26
  #
26
27
  # Redis cache server setup guide: https://redis.io/topics/lru-cache
27
28
  #
28
- # * Supports vanilla Redis, hiredis, and Redis::Distributed.
29
- # * Supports Memcached-like sharding across Redises with Redis::Distributed.
29
+ # * Supports vanilla Redis, hiredis, and +Redis::Distributed+.
30
+ # * Supports Memcached-like sharding across Redises with +Redis::Distributed+.
30
31
  # * Fault tolerant. If the Redis server is unavailable, no exceptions are
31
32
  # raised. Cache fetches are all misses and writes are dropped.
32
33
  # * Local cache. Hot in-memory primary cache within block/middleware scope.
33
- # * +read_multi+ and +write_multi+ support for Redis mget/mset. Use Redis::Distributed
34
- # 4.0.1+ for distributed mget support.
34
+ # * +read_multi+ and +write_multi+ support for Redis mget/mset. Use
35
+ # +Redis::Distributed+ 4.0.1+ for distributed mget support.
35
36
  # * +delete_matched+ support for Redis KEYS globs.
36
37
  class RedisCacheStore < Store
37
- # Keys are truncated with the ActiveSupport digest if they exceed 1kB
38
+ # Keys are truncated with the Active Support digest if they exceed 1kB
38
39
  MAX_KEY_BYTESIZE = 1024
39
40
 
40
41
  DEFAULT_REDIS_OPTIONS = {
@@ -110,8 +111,11 @@ module ActiveSupport
110
111
 
111
112
  # Creates a new Redis cache store.
112
113
  #
113
- # Handles four options: :redis block, :redis instance, single :url
114
- # string, and multiple :url strings.
114
+ # There are four ways to provide the Redis client used by the cache: the
115
+ # +:redis+ param can be a Redis instance or a block that returns a Redis
116
+ # instance, or the +:url+ param can be a string or an array of strings
117
+ # which will be used to create a Redis instance or a +Redis::Distributed+
118
+ # instance.
115
119
  #
116
120
  # Option Class Result
117
121
  # :redis Proc -> options[:redis].call
@@ -134,7 +138,7 @@ module ActiveSupport
134
138
  #
135
139
  # Race condition TTL is not set by default. This can be used to avoid
136
140
  # "thundering herd" cache writes when hot cache entries are expired.
137
- # See <tt>ActiveSupport::Cache::Store#fetch</tt> for more.
141
+ # See ActiveSupport::Cache::Store#fetch for more.
138
142
  #
139
143
  # Setting <tt>skip_nil: true</tt> will not cache nil results:
140
144
  #
@@ -242,7 +246,7 @@ module ActiveSupport
242
246
  # Decrement a cached integer value using the Redis decrby atomic operator.
243
247
  # Returns the updated value.
244
248
  #
245
- # If the key is unset or has expired, it will be set to -amount:
249
+ # If the key is unset or has expired, it will be set to +-amount+:
246
250
  #
247
251
  # cache.decrement("foo") # => -1
248
252
  #
@@ -332,7 +336,10 @@ module ActiveSupport
332
336
  if value
333
337
  entry = deserialize_entry(value, raw: raw)
334
338
  unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(name, options))
335
- results[name] = entry.value
339
+ begin
340
+ results[name] = entry.value
341
+ rescue DeserializationError
342
+ end
336
343
  end
337
344
  end
338
345
  end
@@ -383,7 +390,7 @@ module ActiveSupport
383
390
  end
384
391
 
385
392
  # Nonstandard store provider API to write multiple values at once.
386
- def write_multi_entries(entries, expires_in: nil, race_condition_ttl: nil, **options)
393
+ def write_multi_entries(entries, **options)
387
394
  return if entries.empty?
388
395
 
389
396
  failsafe :write_multi_entries do
@@ -131,17 +131,20 @@ module ActiveSupport
131
131
  end
132
132
  end
133
133
 
134
- def read_multi_entries(keys, **options)
134
+ def read_multi_entries(names, **options)
135
135
  return super unless local_cache
136
136
 
137
- local_entries = local_cache.read_multi_entries(keys)
137
+ keys_to_names = names.index_by { |name| normalize_key(name, options) }
138
+
139
+ local_entries = local_cache.read_multi_entries(keys_to_names.keys)
140
+ local_entries.transform_keys! { |key| keys_to_names[key] }
138
141
  local_entries.transform_values! do |payload|
139
- deserialize_entry(payload)&.value
142
+ deserialize_entry(payload, **options)&.value
140
143
  end
141
- missed_keys = keys - local_entries.keys
144
+ missed_names = names - local_entries.keys
142
145
 
143
- if missed_keys.any?
144
- local_entries.merge!(super(missed_keys, **options))
146
+ if missed_names.any?
147
+ local_entries.merge!(super(missed_names, **options))
145
148
  else
146
149
  local_entries
147
150
  end
@@ -160,8 +160,8 @@ module ActiveSupport
160
160
  # Some implementations may not support all methods beyond the basic cache
161
161
  # methods of #fetch, #write, #read, #exist?, and #delete.
162
162
  #
163
- # ActiveSupport::Cache::Store can store any Ruby object that is supported by
164
- # its +coder+'s +dump+ and +load+ methods.
163
+ # +ActiveSupport::Cache::Store+ can store any Ruby object that is supported
164
+ # by its +coder+'s +dump+ and +load+ methods.
165
165
  #
166
166
  # cache = ActiveSupport::Cache::MemoryStore.new
167
167
  #
@@ -370,8 +370,8 @@ module ActiveSupport
370
370
  #
371
371
  # ==== Options
372
372
  #
373
- # Internally, +fetch+ calls #read_entry, and calls #write_entry on a cache
374
- # miss. Thus, +fetch+ supports the same options as #read and #write.
373
+ # Internally, +fetch+ calls +read_entry+, and calls +write_entry+ on a
374
+ # cache miss. Thus, +fetch+ supports the same options as #read and #write.
375
375
  # Additionally, +fetch+ supports the following options:
376
376
  #
377
377
  # * <tt>force: true</tt> - Forces a cache "miss," meaning we treat the
@@ -459,7 +459,17 @@ module ActiveSupport
459
459
  instrument(:read, name, options) do |payload|
460
460
  cached_entry = read_entry(key, **options, event: payload)
461
461
  entry = handle_expired_entry(cached_entry, key, options)
462
- entry = nil if entry && entry.mismatched?(normalize_version(name, options))
462
+ if entry
463
+ if entry.mismatched?(normalize_version(name, options))
464
+ entry = nil
465
+ else
466
+ begin
467
+ entry.value
468
+ rescue DeserializationError
469
+ entry = nil
470
+ end
471
+ end
472
+ end
463
473
  payload[:super_operation] = :fetch if payload
464
474
  payload[:hit] = !!entry if payload
465
475
  end
@@ -511,7 +521,12 @@ module ActiveSupport
511
521
  nil
512
522
  else
513
523
  payload[:hit] = true if payload
514
- entry.value
524
+ begin
525
+ entry.value
526
+ rescue DeserializationError
527
+ payload[:hit] = false
528
+ nil
529
+ end
515
530
  end
516
531
  else
517
532
  payload[:hit] = false if payload
@@ -803,7 +818,7 @@ module ActiveSupport
803
818
  end
804
819
  end
805
820
 
806
- def deserialize_entry(payload)
821
+ def deserialize_entry(payload, **)
807
822
  payload.nil? ? nil : @coder.load(payload)
808
823
  rescue DeserializationError
809
824
  nil
@@ -1023,7 +1038,8 @@ module ActiveSupport
1023
1038
  # When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
1024
1039
  # for a brief period while the entry is being recalculated.
1025
1040
  entry.expires_at = Time.now.to_f + race_ttl
1026
- write_entry(key, entry, expires_in: race_ttl * 2)
1041
+ options[:expires_in] = race_ttl * 2
1042
+ write_entry(key, entry, **options)
1027
1043
  else
1028
1044
  delete_entry(key, **options)
1029
1045
  end
@@ -1038,6 +1054,8 @@ module ActiveSupport
1038
1054
  end
1039
1055
 
1040
1056
  def save_block_result_to_cache(name, options)
1057
+ options = options.dup
1058
+
1041
1059
  result = instrument(:generate, name, options) do
1042
1060
  yield(name, WriteOptions.new(options))
1043
1061
  end
@@ -1047,6 +1065,10 @@ module ActiveSupport
1047
1065
  end
1048
1066
  end
1049
1067
 
1068
+ # Enables the dynamic configuration of Cache entry options while ensuring
1069
+ # that conflicting options are not both set. When a block is given to
1070
+ # ActiveSupport::Cache::Store#fetch, the second argument will be an
1071
+ # instance of +WriteOptions+.
1050
1072
  class WriteOptions
1051
1073
  def initialize(options) # :nodoc:
1052
1074
  @options = options
@@ -1064,6 +1086,9 @@ module ActiveSupport
1064
1086
  @options[:expires_in]
1065
1087
  end
1066
1088
 
1089
+ # Sets the Cache entry's +expires_in+ value. If an +expires_at+ option was
1090
+ # previously set, this will unset it since +expires_in+ and +expires_at+
1091
+ # cannot both be set.
1067
1092
  def expires_in=(expires_in)
1068
1093
  @options.delete(:expires_at)
1069
1094
  @options[:expires_in] = expires_in
@@ -1073,6 +1098,9 @@ module ActiveSupport
1073
1098
  @options[:expires_at]
1074
1099
  end
1075
1100
 
1101
+ # Sets the Cache entry's +expires_at+ value. If an +expires_in+ option was
1102
+ # previously set, this will unset it since +expires_at+ and +expires_in+
1103
+ # cannot both be set.
1076
1104
  def expires_at=(expires_at)
1077
1105
  @options.delete(:expires_in)
1078
1106
  @options[:expires_at] = expires_at