activesupport 7.1.3.4 → 7.2.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +110 -1099
  3. data/lib/active_support/array_inquirer.rb +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +15 -3
  5. data/lib/active_support/broadcast_logger.rb +5 -4
  6. data/lib/active_support/cache/file_store.rb +15 -10
  7. data/lib/active_support/cache/mem_cache_store.rb +16 -74
  8. data/lib/active_support/cache/memory_store.rb +2 -1
  9. data/lib/active_support/cache/redis_cache_store.rb +16 -13
  10. data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
  11. data/lib/active_support/cache.rb +61 -68
  12. data/lib/active_support/callbacks.rb +74 -113
  13. data/lib/active_support/core_ext/array/conversions.rb +0 -2
  14. data/lib/active_support/core_ext/class/subclasses.rb +15 -35
  15. data/lib/active_support/core_ext/date/blank.rb +4 -0
  16. data/lib/active_support/core_ext/date/conversions.rb +0 -2
  17. data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
  18. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  19. data/lib/active_support/core_ext/date_time/conversions.rb +0 -4
  20. data/lib/active_support/core_ext/digest/uuid.rb +6 -0
  21. data/lib/active_support/core_ext/erb/util.rb +5 -0
  22. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  23. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  24. data/lib/active_support/core_ext/module/delegation.rb +20 -148
  25. data/lib/active_support/core_ext/module/deprecation.rb +1 -4
  26. data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
  27. data/lib/active_support/core_ext/object/blank.rb +45 -1
  28. data/lib/active_support/core_ext/object/duplicable.rb +24 -15
  29. data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
  30. data/lib/active_support/core_ext/object/json.rb +1 -1
  31. data/lib/active_support/core_ext/object/with.rb +5 -3
  32. data/lib/active_support/core_ext/pathname/blank.rb +4 -0
  33. data/lib/active_support/core_ext/range/overlap.rb +1 -1
  34. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  35. data/lib/active_support/core_ext/string/filters.rb +1 -1
  36. data/lib/active_support/core_ext/string/output_safety.rb +0 -7
  37. data/lib/active_support/core_ext/time/calculations.rb +12 -27
  38. data/lib/active_support/core_ext/time/conversions.rb +0 -2
  39. data/lib/active_support/core_ext.rb +0 -1
  40. data/lib/active_support/current_attributes.rb +33 -40
  41. data/lib/active_support/delegation.rb +188 -0
  42. data/lib/active_support/dependencies/autoload.rb +0 -12
  43. data/lib/active_support/deprecation/constant_accessor.rb +1 -3
  44. data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
  45. data/lib/active_support/deprecation/reporting.rb +7 -2
  46. data/lib/active_support/deprecation.rb +8 -5
  47. data/lib/active_support/descendants_tracker.rb +9 -87
  48. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  49. data/lib/active_support/duration/iso8601_serializer.rb +1 -2
  50. data/lib/active_support/duration.rb +11 -6
  51. data/lib/active_support/error_reporter.rb +41 -3
  52. data/lib/active_support/evented_file_update_checker.rb +0 -1
  53. data/lib/active_support/execution_wrapper.rb +0 -1
  54. data/lib/active_support/file_update_checker.rb +1 -1
  55. data/lib/active_support/fork_tracker.rb +2 -38
  56. data/lib/active_support/gem_version.rb +3 -3
  57. data/lib/active_support/hash_with_indifferent_access.rb +6 -8
  58. data/lib/active_support/html_safe_translation.rb +7 -4
  59. data/lib/active_support/json/encoding.rb +1 -1
  60. data/lib/active_support/log_subscriber.rb +1 -12
  61. data/lib/active_support/logger.rb +15 -2
  62. data/lib/active_support/message_pack/extensions.rb +15 -2
  63. data/lib/active_support/messages/codec.rb +1 -1
  64. data/lib/active_support/multibyte/chars.rb +2 -2
  65. data/lib/active_support/notifications/fanout.rb +4 -7
  66. data/lib/active_support/notifications/instrumenter.rb +32 -21
  67. data/lib/active_support/notifications.rb +28 -27
  68. data/lib/active_support/number_helper/number_converter.rb +2 -2
  69. data/lib/active_support/option_merger.rb +2 -2
  70. data/lib/active_support/ordered_options.rb +53 -15
  71. data/lib/active_support/proxy_object.rb +8 -5
  72. data/lib/active_support/railtie.rb +4 -11
  73. data/lib/active_support/string_inquirer.rb +1 -1
  74. data/lib/active_support/tagged_logging.rb +4 -0
  75. data/lib/active_support/test_case.rb +3 -1
  76. data/lib/active_support/testing/assertions.rb +4 -4
  77. data/lib/active_support/testing/constant_stubbing.rb +30 -8
  78. data/lib/active_support/testing/deprecation.rb +5 -12
  79. data/lib/active_support/testing/isolation.rb +18 -8
  80. data/lib/active_support/testing/method_call_assertions.rb +2 -16
  81. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  82. data/lib/active_support/testing/strict_warnings.rb +5 -4
  83. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  84. data/lib/active_support/time_with_zone.rb +6 -2
  85. data/lib/active_support/values/time_zone.rb +10 -1
  86. data/lib/active_support/xml_mini.rb +11 -2
  87. data/lib/active_support.rb +3 -2
  88. metadata +17 -25
  89. data/lib/active_support/deprecation/instance_delegator.rb +0 -65
  90. data/lib/active_support/ruby_features.rb +0 -7
@@ -39,7 +39,7 @@ module ActiveSupport
39
39
  name.end_with?("?") || super
40
40
  end
41
41
 
42
- def method_missing(name, *args)
42
+ def method_missing(name, ...)
43
43
  if name.end_with?("?")
44
44
  any?(name[0..-2])
45
45
  else
@@ -17,7 +17,8 @@ module ActiveSupport
17
17
  # can focus on the rest.
18
18
  #
19
19
  # bc = ActiveSupport::BacktraceCleaner.new
20
- # bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
20
+ # root = "#{Rails.root}/"
21
+ # bc.add_filter { |line| line.start_with?(root) ? line.from(root.size) : line } # strip the Rails.root prefix
21
22
  # bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems
22
23
  # bc.clean(exception.backtrace) # perform the cleanup
23
24
  #
@@ -33,6 +34,7 @@ module ActiveSupport
33
34
  class BacktraceCleaner
34
35
  def initialize
35
36
  @filters, @silencers = [], []
37
+ add_core_silencer
36
38
  add_gem_filter
37
39
  add_gem_silencer
38
40
  add_stdlib_silencer
@@ -75,8 +77,9 @@ module ActiveSupport
75
77
  # Adds a filter from the block provided. Each line in the backtrace will be
76
78
  # mapped against this filter.
77
79
  #
78
- # # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb"
79
- # backtrace_cleaner.add_filter { |line| line.gsub(Rails.root.to_s, '') }
80
+ # # Will turn "/my/rails/root/app/models/person.rb" into "app/models/person.rb"
81
+ # root = "#{Rails.root}/"
82
+ # backtrace_cleaner.add_filter { |line| line.start_with?(root) ? line.from(root.size) : line }
80
83
  def add_filter(&block)
81
84
  @filters << block
82
85
  end
@@ -107,6 +110,11 @@ module ActiveSupport
107
110
  private
108
111
  FORMATTED_GEMS_PATTERN = /\A[^\/]+ \([\w.]+\) /
109
112
 
113
+ def initialize_copy(_other)
114
+ @filters = @filters.dup
115
+ @silencers = @silencers.dup
116
+ end
117
+
110
118
  def add_gem_filter
111
119
  gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
112
120
  return if gems_paths.empty?
@@ -116,6 +124,10 @@ module ActiveSupport
116
124
  add_filter { |line| line.sub(gems_regexp, gems_result) }
117
125
  end
118
126
 
127
+ def add_core_silencer
128
+ add_silencer { |line| line.include?("<internal:") }
129
+ end
130
+
119
131
  def add_gem_silencer
120
132
  add_silencer { |line| FORMATTED_GEMS_PATTERN.match?(line) }
121
133
  end
@@ -229,17 +229,18 @@ module ActiveSupport
229
229
  private
230
230
  def dispatch(&block)
231
231
  @broadcasts.each { |logger| block.call(logger) }
232
+ true
232
233
  end
233
234
 
234
- def method_missing(name, *args, **kwargs, &block)
235
+ def method_missing(name, ...)
235
236
  loggers = @broadcasts.select { |logger| logger.respond_to?(name) }
236
237
 
237
238
  if loggers.none?
238
- super(name, *args, **kwargs, &block)
239
+ super
239
240
  elsif loggers.one?
240
- loggers.first.send(name, *args, **kwargs, &block)
241
+ loggers.first.send(name, ...)
241
242
  else
242
- loggers.map { |logger| logger.send(name, *args, **kwargs, &block) }
243
+ loggers.map { |logger| logger.send(name, ...) }
243
244
  end
244
245
  end
245
246
 
@@ -78,8 +78,9 @@ module ActiveSupport
78
78
 
79
79
  def delete_matched(matcher, options = nil)
80
80
  options = merged_options(options)
81
+ matcher = key_matcher(matcher, options)
82
+
81
83
  instrument(:delete_matched, matcher.inspect) do
82
- matcher = key_matcher(matcher, options)
83
84
  search_dir(cache_path) do |path|
84
85
  key = file_path_key(path)
85
86
  delete_entry(path, **options) if key.match(matcher)
@@ -209,18 +210,22 @@ module ActiveSupport
209
210
  # Modifies the amount of an integer value that is stored in the cache.
210
211
  # If the key is not found it is created and set to +amount+.
211
212
  def modify_value(name, amount, options)
212
- file_name = normalize_key(name, options)
213
+ options = merged_options(options)
214
+ key = normalize_key(name, options)
215
+ version = normalize_version(name, options)
216
+ amount = Integer(amount)
213
217
 
214
- lock_file(file_name) do
215
- options = merged_options(options)
218
+ lock_file(key) do
219
+ entry = read_entry(key, **options)
216
220
 
217
- if num = read(name, options)
218
- num = num.to_i + amount
219
- write(name, num, options)
220
- num
221
- else
222
- write(name, Integer(amount), options)
221
+ if !entry || entry.expired? || entry.mismatched?(version)
222
+ write(name, amount, options)
223
223
  amount
224
+ else
225
+ num = entry.value.to_i + amount
226
+ entry = Entry.new(num, expires_at: entry.expires_at, version: entry.version)
227
+ write_entry(key, entry)
228
+ num
224
229
  end
225
230
  end
226
231
  end
@@ -41,46 +41,6 @@ module ActiveSupport
41
41
 
42
42
  prepend Strategy::LocalCache
43
43
 
44
- module DupLocalCache
45
- class DupLocalStore < DelegateClass(Strategy::LocalCache::LocalStore)
46
- def write_entry(_key, entry)
47
- if entry.is_a?(Entry)
48
- entry.dup_value!
49
- end
50
- super
51
- end
52
-
53
- def fetch_entry(key)
54
- entry = super do
55
- new_entry = yield
56
- if entry.is_a?(Entry)
57
- new_entry.dup_value!
58
- end
59
- new_entry
60
- end
61
- entry = entry.dup
62
-
63
- if entry.is_a?(Entry)
64
- entry.dup_value!
65
- end
66
-
67
- entry
68
- end
69
- end
70
-
71
- private
72
- def local_cache
73
- if ActiveSupport::Cache.format_version == 6.1
74
- if local_cache = super
75
- DupLocalStore.new(local_cache)
76
- end
77
- else
78
- super
79
- end
80
- end
81
- end
82
- prepend DupLocalCache
83
-
84
44
  KEY_MAX_SIZE = 250
85
45
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
86
46
 
@@ -114,7 +74,6 @@ module ActiveSupport
114
74
  #
115
75
  # If no addresses are provided, but <tt>ENV['MEMCACHE_SERVERS']</tt> is defined, it will be used instead. Otherwise,
116
76
  # +MemCacheStore+ will connect to localhost:11211 (the default memcached port).
117
- # Passing a +Dalli::Client+ instance is deprecated and will be removed. Please pass an address instead.
118
77
  def initialize(*addresses)
119
78
  addresses = addresses.flatten
120
79
  options = addresses.extract_options!
@@ -126,19 +85,12 @@ module ActiveSupport
126
85
  unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
127
86
  raise ArgumentError, "First argument must be an empty array, address, or array of addresses."
128
87
  end
129
- if addresses.first.is_a?(Dalli::Client)
130
- ActiveSupport.deprecator.warn(<<~MSG)
131
- Initializing MemCacheStore with a Dalli::Client is deprecated and will be removed in Rails 7.2.
132
- Use memcached server addresses instead.
133
- MSG
134
- @data = addresses.first
135
- else
136
- @mem_cache_options = options.dup
137
- # The value "compress: false" prevents duplicate compression within Dalli.
138
- @mem_cache_options[:compress] = false
139
- (OVERRIDDEN_OPTIONS - %i(compress)).each { |name| @mem_cache_options.delete(name) }
140
- @data = self.class.build_mem_cache(*(addresses + [@mem_cache_options]))
141
- end
88
+
89
+ @mem_cache_options = options.dup
90
+ # The value "compress: false" prevents duplicate compression within Dalli.
91
+ @mem_cache_options[:compress] = false
92
+ (OVERRIDDEN_OPTIONS - %i(compress)).each { |name| @mem_cache_options.delete(name) }
93
+ @data = self.class.build_mem_cache(*(addresses + [@mem_cache_options]))
142
94
  end
143
95
 
144
96
  def inspect
@@ -179,9 +131,11 @@ module ActiveSupport
179
131
  # <tt>raw: true</tt>, will fail and return +nil+.
180
132
  def increment(name, amount = 1, options = nil)
181
133
  options = merged_options(options)
182
- instrument(:increment, name, amount: amount) do
134
+ key = normalize_key(name, options)
135
+
136
+ instrument(:increment, key, amount: amount) do
183
137
  rescue_error_with nil do
184
- @data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in], amount) }
138
+ @data.with { |c| c.incr(key, amount, options[:expires_in], amount) }
185
139
  end
186
140
  end
187
141
  end
@@ -203,9 +157,11 @@ module ActiveSupport
203
157
  # <tt>raw: true</tt>, will fail and return +nil+.
204
158
  def decrement(name, amount = 1, options = nil)
205
159
  options = merged_options(options)
206
- instrument(:decrement, name, amount: amount) do
160
+ key = normalize_key(name, options)
161
+
162
+ instrument(:decrement, key, amount: amount) do
207
163
  rescue_error_with nil do
208
- @data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in], 0) }
164
+ @data.with { |c| c.decr(key, amount, options[:expires_in], 0) }
209
165
  end
210
166
  end
211
167
  end
@@ -222,20 +178,6 @@ module ActiveSupport
222
178
  end
223
179
 
224
180
  private
225
- def default_serializer
226
- if Cache.format_version == 6.1
227
- ActiveSupport.deprecator.warn <<~EOM
228
- Support for `config.active_support.cache_format_version = 6.1` has been deprecated and will be removed in Rails 7.2.
229
-
230
- Check the Rails upgrade guide at https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#new-activesupport-cache-serialization-format
231
- for more information on how to upgrade.
232
- EOM
233
- Cache::SerializerWithFallback[:passthrough]
234
- else
235
- super
236
- end
237
- end
238
-
239
181
  # Read an entry from the cache.
240
182
  def read_entry(key, **options)
241
183
  deserialize_entry(read_serialized_entry(key, **options), **options)
@@ -259,10 +201,10 @@ module ActiveSupport
259
201
  # Set the memcache expire a few minutes in the future to support race condition ttls on read
260
202
  expires_in += 5.minutes
261
203
  end
262
- rescue_error_with false do
204
+ rescue_error_with nil do
263
205
  # Don't pass compress option to Dalli since we are already dealing with compression.
264
206
  options.delete(:compress)
265
- @data.with { |c| c.send(method, key, payload, expires_in, **options) }
207
+ @data.with { |c| !!c.send(method, key, payload, expires_in, **options) }
266
208
  end
267
209
  end
268
210
 
@@ -168,8 +168,9 @@ module ActiveSupport
168
168
  # Deletes cache entries if the cache key matches a given pattern.
169
169
  def delete_matched(matcher, options = nil)
170
170
  options = merged_options(options)
171
+ matcher = key_matcher(matcher, options)
172
+
171
173
  instrument(:delete_matched, matcher.inspect) do
172
- matcher = key_matcher(matcher, options)
173
174
  keys = synchronize { @data.keys }
174
175
  keys.each do |key|
175
176
  delete_entry(key, **options) if key.match(matcher)
@@ -196,12 +196,13 @@ module ActiveSupport
196
196
  #
197
197
  # Failsafe: Raises errors.
198
198
  def delete_matched(matcher, options = nil)
199
- instrument :delete_matched, matcher do
200
- unless String === matcher
201
- raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}"
202
- end
199
+ unless String === matcher
200
+ raise ArgumentError, "Only Redis glob strings are supported: #{matcher.inspect}"
201
+ end
202
+ pattern = namespace_key(matcher, options)
203
+
204
+ instrument :delete_matched, pattern do
203
205
  redis.then do |c|
204
- pattern = namespace_key(matcher, options)
205
206
  cursor = "0"
206
207
  # Fetch keys in batches using SCAN to avoid blocking the Redis server.
207
208
  nodes = c.respond_to?(:nodes) ? c.nodes : [c]
@@ -234,10 +235,11 @@ module ActiveSupport
234
235
  #
235
236
  # Failsafe: Raises errors.
236
237
  def increment(name, amount = 1, options = nil)
237
- instrument :increment, name, amount: amount do
238
+ options = merged_options(options)
239
+ key = normalize_key(name, options)
240
+
241
+ instrument :increment, key, amount: amount do
238
242
  failsafe :increment do
239
- options = merged_options(options)
240
- key = normalize_key(name, options)
241
243
  change_counter(key, amount, options)
242
244
  end
243
245
  end
@@ -260,10 +262,11 @@ module ActiveSupport
260
262
  #
261
263
  # Failsafe: Raises errors.
262
264
  def decrement(name, amount = 1, options = nil)
263
- instrument :decrement, name, amount: amount do
265
+ options = merged_options(options)
266
+ key = normalize_key(name, options)
267
+
268
+ instrument :decrement, key, amount: amount do
264
269
  failsafe :decrement do
265
- options = merged_options(options)
266
- key = normalize_key(name, options)
267
270
  change_counter(key, -amount, options)
268
271
  end
269
272
  end
@@ -369,8 +372,8 @@ module ActiveSupport
369
372
  if pipeline
370
373
  pipeline.set(key, payload, **modifiers)
371
374
  else
372
- failsafe :write_entry, returning: false do
373
- redis.then { |c| c.set key, payload, **modifiers }
375
+ failsafe :write_entry, returning: nil do
376
+ redis.then { |c| !!c.set(key, payload, **modifiers) }
374
377
  end
375
378
  end
376
379
  end
@@ -63,28 +63,6 @@ module ActiveSupport
63
63
  end
64
64
  end
65
65
 
66
- module Marshal61WithFallback
67
- include SerializerWithFallback
68
- extend self
69
-
70
- MARSHAL_SIGNATURE = "\x04\x08".b.freeze
71
-
72
- def dump(entry)
73
- Marshal.dump(entry)
74
- end
75
-
76
- def dump_compressed(entry, threshold)
77
- Marshal.dump(entry.compressed(threshold))
78
- end
79
-
80
- alias_method :_load, :marshal_load
81
- public :_load
82
-
83
- def dumped?(dumped)
84
- dumped.start_with?(MARSHAL_SIGNATURE)
85
- end
86
- end
87
-
88
66
  module Marshal70WithFallback
89
67
  include SerializerWithFallback
90
68
  extend self
@@ -165,7 +143,6 @@ module ActiveSupport
165
143
 
166
144
  SERIALIZERS = {
167
145
  passthrough: PassthroughWithFallback,
168
- marshal_6_1: Marshal61WithFallback,
169
146
  marshal_7_0: Marshal70WithFallback,
170
147
  marshal_7_1: Marshal71WithFallback,
171
148
  message_pack: MessagePackWithFallback,
@@ -52,7 +52,7 @@ module ActiveSupport
52
52
  autoload :LocalCache, "active_support/cache/strategy/local_cache"
53
53
  end
54
54
 
55
- @format_version = 6.1
55
+ @format_version = 7.0
56
56
 
57
57
  class << self
58
58
  attr_accessor :format_version
@@ -86,13 +86,7 @@ module ActiveSupport
86
86
  case store
87
87
  when Symbol
88
88
  options = parameters.extract_options!
89
- # clean this up once Ruby 2.7 support is dropped
90
- # see https://github.com/rails/rails/pull/41522#discussion_r581186602
91
- if options.empty?
92
- retrieve_store_class(store).new(*parameters)
93
- else
94
- retrieve_store_class(store).new(*parameters, **options)
95
- end
89
+ retrieve_store_class(store).new(*parameters, **options)
96
90
  when Array
97
91
  lookup_store(*store)
98
92
  when nil
@@ -166,7 +160,7 @@ module ActiveSupport
166
160
  # cache = ActiveSupport::Cache::MemoryStore.new
167
161
  #
168
162
  # cache.read('city') # => nil
169
- # cache.write('city', "Duckburgh")
163
+ # cache.write('city', "Duckburgh") # => true
170
164
  # cache.read('city') # => "Duckburgh"
171
165
  #
172
166
  # cache.write('not serializable', Proc.new {}) # => TypeError
@@ -206,24 +200,6 @@ module ActiveSupport
206
200
  def retrieve_pool_options(options)
207
201
  if options.key?(:pool)
208
202
  pool_options = options.delete(:pool)
209
- elsif options.key?(:pool_size) || options.key?(:pool_timeout)
210
- pool_options = {}
211
-
212
- if options.key?(:pool_size)
213
- ActiveSupport.deprecator.warn(<<~MSG)
214
- Using :pool_size is deprecated and will be removed in Rails 7.2.
215
- Use `pool: { size: #{options[:pool_size].inspect} }` instead.
216
- MSG
217
- pool_options[:size] = options.delete(:pool_size)
218
- end
219
-
220
- if options.key?(:pool_timeout)
221
- ActiveSupport.deprecator.warn(<<~MSG)
222
- Using :pool_timeout is deprecated and will be removed in Rails 7.2.
223
- Use `pool: { timeout: #{options[:pool_timeout].inspect} }` instead.
224
- MSG
225
- pool_options[:timeout] = options.delete(:pool_timeout)
226
- end
227
203
  else
228
204
  pool_options = true
229
205
  end
@@ -344,7 +320,7 @@ module ActiveSupport
344
320
 
345
321
  # Silences the logger within a block.
346
322
  def mute
347
- previous_silence, @silence = defined?(@silence) && @silence, true
323
+ previous_silence, @silence = @silence, true
348
324
  yield
349
325
  ensure
350
326
  @silence = previous_silence
@@ -411,31 +387,47 @@ module ActiveSupport
411
387
  # has elapsed.
412
388
  #
413
389
  # # Set all values to expire after one minute.
414
- # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
390
+ # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1)
415
391
  #
416
- # cache.write('foo', 'original value')
392
+ # cache.write("foo", "original value")
417
393
  # val_1 = nil
418
394
  # val_2 = nil
419
- # sleep 60
395
+ # p cache.read("foo") # => "original value"
420
396
  #
421
- # Thread.new do
422
- # val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
397
+ # sleep 1 # wait until the cache expires
398
+ #
399
+ # t1 = Thread.new do
400
+ # # fetch does the following:
401
+ # # 1. gets an recent expired entry
402
+ # # 2. extends the expiry by 2 seconds (race_condition_ttl)
403
+ # # 3. regenerates the new value
404
+ # val_1 = cache.fetch("foo", race_condition_ttl: 2) do
423
405
  # sleep 1
424
- # 'new value 1'
406
+ # "new value 1"
425
407
  # end
426
408
  # end
427
409
  #
428
- # Thread.new do
429
- # val_2 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
430
- # 'new value 2'
431
- # end
410
+ # # Wait until t1 extends the expiry of the entry
411
+ # # but before generating the new value
412
+ # sleep 0.1
413
+ #
414
+ # val_2 = cache.fetch("foo", race_condition_ttl: 2) do
415
+ # # This block won't be executed because t1 extended the expiry
416
+ # "new value 2"
432
417
  # end
433
418
  #
434
- # cache.fetch('foo') # => "original value"
435
- # sleep 10 # First thread extended the life of cache by another 10 seconds
436
- # cache.fetch('foo') # => "new value 1"
437
- # val_1 # => "new value 1"
438
- # val_2 # => "original value"
419
+ # t1.join
420
+ #
421
+ # p val_1 # => "new value 1"
422
+ # p val_2 # => "oritinal value"
423
+ # p cache.fetch("foo") # => "new value 1"
424
+ #
425
+ # # The entry requires 3 seconds to expire (expires_in + race_condition_ttl)
426
+ # # We have waited 2 seconds already (sleep(1) + t1.join) thus we need to wait 1
427
+ # # more second to see the entry expire.
428
+ # sleep 1
429
+ #
430
+ # p cache.fetch("foo") # => nil
439
431
  #
440
432
  # ==== Dynamic Options
441
433
  #
@@ -456,7 +448,7 @@ module ActiveSupport
456
448
 
457
449
  entry = nil
458
450
  unless options[:force]
459
- instrument(:read, name, options) do |payload|
451
+ instrument(:read, key, options) do |payload|
460
452
  cached_entry = read_entry(key, **options, event: payload)
461
453
  entry = handle_expired_entry(cached_entry, key, options)
462
454
  if entry
@@ -478,7 +470,7 @@ module ActiveSupport
478
470
  if entry
479
471
  get_entry_value(entry, name, options)
480
472
  else
481
- save_block_result_to_cache(name, options, &block)
473
+ save_block_result_to_cache(name, key, options, &block)
482
474
  end
483
475
  elsif options && options[:force]
484
476
  raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
@@ -508,7 +500,7 @@ module ActiveSupport
508
500
  key = normalize_key(name, options)
509
501
  version = normalize_version(name, options)
510
502
 
511
- instrument(:read, name, options) do |payload|
503
+ instrument(:read, key, options) do |payload|
512
504
  entry = read_entry(key, **options, event: payload)
513
505
 
514
506
  if entry
@@ -605,14 +597,14 @@ module ActiveSupport
605
597
  options = names.extract_options!
606
598
  options = merged_options(options)
607
599
 
608
- instrument_multi :read_multi, names, options do |payload|
600
+ writes = {}
601
+ ordered = instrument_multi :read_multi, names, options do |payload|
609
602
  if options[:force]
610
603
  reads = {}
611
604
  else
612
605
  reads = read_multi_entries(names, **options)
613
606
  end
614
607
 
615
- writes = {}
616
608
  ordered = names.index_with do |name|
617
609
  reads.fetch(name) { writes[name] = yield(name) }
618
610
  end
@@ -621,15 +613,20 @@ module ActiveSupport
621
613
  payload[:hits] = reads.keys
622
614
  payload[:super_operation] = :fetch_multi
623
615
 
624
- write_multi(writes, options)
625
-
626
616
  ordered
627
617
  end
618
+
619
+ write_multi(writes, options)
620
+
621
+ ordered
628
622
  end
629
623
 
630
624
  # Writes the value to the cache with the key. The value must be supported
631
625
  # by the +coder+'s +dump+ and +load+ methods.
632
626
  #
627
+ # Returns +true+ if the write succeeded, +nil+ if there was an error talking
628
+ # to the cache backend, or +false+ if the write failed for another reason.
629
+ #
633
630
  # By default, cache entries larger than 1kB are compressed. Compression
634
631
  # allows more data to be stored in the same memory footprint, leading to
635
632
  # fewer cache evictions and higher hit rates.
@@ -662,10 +659,11 @@ module ActiveSupport
662
659
  # Other options will be handled by the specific cache store implementation.
663
660
  def write(name, value, options = nil)
664
661
  options = merged_options(options)
662
+ key = normalize_key(name, options)
665
663
 
666
- instrument(:write, name, options) do
664
+ instrument(:write, key, options) do
667
665
  entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
668
- write_entry(normalize_key(name, options), entry, **options)
666
+ write_entry(key, entry, **options)
669
667
  end
670
668
  end
671
669
 
@@ -675,9 +673,10 @@ module ActiveSupport
675
673
  # Options are passed to the underlying cache implementation.
676
674
  def delete(name, options = nil)
677
675
  options = merged_options(options)
676
+ key = normalize_key(name, options)
678
677
 
679
- instrument(:delete, name) do
680
- delete_entry(normalize_key(name, options), **options)
678
+ instrument(:delete, key) do
679
+ delete_entry(key, **options)
681
680
  end
682
681
  end
683
682
 
@@ -701,9 +700,10 @@ module ActiveSupport
701
700
  # Options are passed to the underlying cache implementation.
702
701
  def exist?(name, options = nil)
703
702
  options = merged_options(options)
703
+ key = normalize_key(name, options)
704
704
 
705
- instrument(:exist?, name) do |payload|
706
- entry = read_entry(normalize_key(name, options), **options, event: payload)
705
+ instrument(:exist?, key) do |payload|
706
+ entry = read_entry(key, **options, event: payload)
707
707
  (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
708
708
  end
709
709
  end
@@ -761,14 +761,6 @@ module ActiveSupport
761
761
  private
762
762
  def default_serializer
763
763
  case Cache.format_version
764
- when 6.1
765
- ActiveSupport.deprecator.warn <<~EOM
766
- Support for `config.active_support.cache_format_version = 6.1` has been deprecated and will be removed in Rails 7.2.
767
-
768
- Check the Rails upgrade guide at https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#new-activesupport-cache-serialization-format
769
- for more information on how to upgrade.
770
- EOM
771
- Cache::SerializerWithFallback[:marshal_6_1]
772
764
  when 7.0
773
765
  Cache::SerializerWithFallback[:marshal_7_0]
774
766
  when 7.1
@@ -1016,7 +1008,7 @@ module ActiveSupport
1016
1008
  if multi
1017
1009
  ": #{payload[:key].size} key(s) specified"
1018
1010
  elsif payload[:key]
1019
- ": #{normalize_key(payload[:key], options)}"
1011
+ ": #{payload[:key]}"
1020
1012
  end
1021
1013
 
1022
1014
  debug_options = " (#{options.inspect})" unless options.blank?
@@ -1038,7 +1030,8 @@ module ActiveSupport
1038
1030
  # When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
1039
1031
  # for a brief period while the entry is being recalculated.
1040
1032
  entry.expires_at = Time.now.to_f + race_ttl
1041
- write_entry(key, entry, expires_in: race_ttl * 2)
1033
+ options[:expires_in] = race_ttl * 2
1034
+ write_entry(key, entry, **options)
1042
1035
  else
1043
1036
  delete_entry(key, **options)
1044
1037
  end
@@ -1052,10 +1045,10 @@ module ActiveSupport
1052
1045
  entry.value
1053
1046
  end
1054
1047
 
1055
- def save_block_result_to_cache(name, options)
1048
+ def save_block_result_to_cache(name, key, options)
1056
1049
  options = options.dup
1057
1050
 
1058
- result = instrument(:generate, name, options) do
1051
+ result = instrument(:generate, key, options) do
1059
1052
  yield(name, WriteOptions.new(options))
1060
1053
  end
1061
1054