activesupport 7.0.8.7 → 7.1.0.beta1

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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +722 -314
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_support/actionable_error.rb +3 -1
  6. data/lib/active_support/array_inquirer.rb +2 -0
  7. data/lib/active_support/backtrace_cleaner.rb +25 -5
  8. data/lib/active_support/benchmarkable.rb +1 -0
  9. data/lib/active_support/builder.rb +1 -1
  10. data/lib/active_support/cache/coder.rb +153 -0
  11. data/lib/active_support/cache/entry.rb +128 -0
  12. data/lib/active_support/cache/file_store.rb +36 -9
  13. data/lib/active_support/cache/mem_cache_store.rb +84 -68
  14. data/lib/active_support/cache/memory_store.rb +76 -24
  15. data/lib/active_support/cache/null_store.rb +6 -0
  16. data/lib/active_support/cache/redis_cache_store.rb +126 -131
  17. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  18. data/lib/active_support/cache/strategy/local_cache.rb +20 -8
  19. data/lib/active_support/cache.rb +304 -246
  20. data/lib/active_support/callbacks.rb +38 -18
  21. data/lib/active_support/concern.rb +4 -2
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
  23. data/lib/active_support/concurrency/null_lock.rb +13 -0
  24. data/lib/active_support/configurable.rb +10 -0
  25. data/lib/active_support/core_ext/array/conversions.rb +2 -1
  26. data/lib/active_support/core_ext/array.rb +0 -1
  27. data/lib/active_support/core_ext/class/subclasses.rb +13 -10
  28. data/lib/active_support/core_ext/date/conversions.rb +1 -0
  29. data/lib/active_support/core_ext/date.rb +0 -1
  30. data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
  31. data/lib/active_support/core_ext/date_time/conversions.rb +6 -2
  32. data/lib/active_support/core_ext/date_time.rb +0 -1
  33. data/lib/active_support/core_ext/digest/uuid.rb +1 -10
  34. data/lib/active_support/core_ext/enumerable.rb +3 -75
  35. data/lib/active_support/core_ext/erb/util.rb +196 -0
  36. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  37. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
  38. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
  39. data/lib/active_support/core_ext/module/delegation.rb +40 -11
  40. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  41. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  42. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  43. data/lib/active_support/core_ext/numeric/conversions.rb +2 -0
  44. data/lib/active_support/core_ext/numeric.rb +0 -1
  45. data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
  46. data/lib/active_support/core_ext/object/duplicable.rb +15 -24
  47. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  48. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  49. data/lib/active_support/core_ext/object/json.rb +10 -2
  50. data/lib/active_support/core_ext/object/with.rb +44 -0
  51. data/lib/active_support/core_ext/object/with_options.rb +3 -3
  52. data/lib/active_support/core_ext/object.rb +1 -0
  53. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  54. data/lib/active_support/core_ext/pathname/existence.rb +2 -0
  55. data/lib/active_support/core_ext/pathname.rb +1 -0
  56. data/lib/active_support/core_ext/range/conversions.rb +28 -7
  57. data/lib/active_support/core_ext/range/{overlaps.rb → overlap.rb} +5 -3
  58. data/lib/active_support/core_ext/range.rb +1 -2
  59. data/lib/active_support/core_ext/securerandom.rb +24 -12
  60. data/lib/active_support/core_ext/string/filters.rb +20 -14
  61. data/lib/active_support/core_ext/string/inflections.rb +16 -5
  62. data/lib/active_support/core_ext/string/output_safety.rb +38 -174
  63. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  64. data/lib/active_support/core_ext/time/calculations.rb +18 -2
  65. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  66. data/lib/active_support/core_ext/time/zones.rb +4 -4
  67. data/lib/active_support/core_ext/time.rb +0 -1
  68. data/lib/active_support/current_attributes.rb +15 -6
  69. data/lib/active_support/dependencies/autoload.rb +17 -12
  70. data/lib/active_support/deprecation/behaviors.rb +53 -32
  71. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  72. data/lib/active_support/deprecation/deprecators.rb +104 -0
  73. data/lib/active_support/deprecation/disallowed.rb +3 -5
  74. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  75. data/lib/active_support/deprecation/method_wrappers.rb +6 -23
  76. data/lib/active_support/deprecation/proxy_wrappers.rb +37 -22
  77. data/lib/active_support/deprecation/reporting.rb +35 -21
  78. data/lib/active_support/deprecation.rb +32 -5
  79. data/lib/active_support/deprecator.rb +7 -0
  80. data/lib/active_support/descendants_tracker.rb +104 -132
  81. data/lib/active_support/duration/iso8601_serializer.rb +0 -2
  82. data/lib/active_support/duration.rb +2 -1
  83. data/lib/active_support/encrypted_configuration.rb +30 -9
  84. data/lib/active_support/encrypted_file.rb +8 -3
  85. data/lib/active_support/environment_inquirer.rb +22 -2
  86. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  87. data/lib/active_support/error_reporter.rb +121 -35
  88. data/lib/active_support/execution_wrapper.rb +4 -4
  89. data/lib/active_support/file_update_checker.rb +4 -2
  90. data/lib/active_support/fork_tracker.rb +10 -2
  91. data/lib/active_support/gem_version.rb +4 -4
  92. data/lib/active_support/gzip.rb +2 -0
  93. data/lib/active_support/hash_with_indifferent_access.rb +35 -17
  94. data/lib/active_support/i18n.rb +1 -1
  95. data/lib/active_support/i18n_railtie.rb +20 -13
  96. data/lib/active_support/inflector/inflections.rb +2 -0
  97. data/lib/active_support/inflector/methods.rb +22 -10
  98. data/lib/active_support/inflector/transliterate.rb +3 -1
  99. data/lib/active_support/isolated_execution_state.rb +26 -22
  100. data/lib/active_support/json/decoding.rb +2 -1
  101. data/lib/active_support/json/encoding.rb +25 -43
  102. data/lib/active_support/key_generator.rb +9 -1
  103. data/lib/active_support/lazy_load_hooks.rb +6 -4
  104. data/lib/active_support/locale/en.yml +2 -0
  105. data/lib/active_support/log_subscriber.rb +78 -33
  106. data/lib/active_support/logger.rb +1 -1
  107. data/lib/active_support/logger_thread_safe_level.rb +9 -21
  108. data/lib/active_support/message_encryptor.rb +197 -53
  109. data/lib/active_support/message_encryptors.rb +140 -0
  110. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  111. data/lib/active_support/message_pack/extensions.rb +292 -0
  112. data/lib/active_support/message_pack/serializer.rb +63 -0
  113. data/lib/active_support/message_pack.rb +50 -0
  114. data/lib/active_support/message_verifier.rb +212 -93
  115. data/lib/active_support/message_verifiers.rb +134 -0
  116. data/lib/active_support/messages/codec.rb +65 -0
  117. data/lib/active_support/messages/metadata.rb +111 -45
  118. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  119. data/lib/active_support/messages/rotator.rb +34 -32
  120. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  121. data/lib/active_support/multibyte/chars.rb +2 -0
  122. data/lib/active_support/multibyte/unicode.rb +9 -37
  123. data/lib/active_support/notifications/fanout.rb +239 -81
  124. data/lib/active_support/notifications/instrumenter.rb +71 -14
  125. data/lib/active_support/notifications.rb +1 -1
  126. data/lib/active_support/number_helper/number_converter.rb +2 -2
  127. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  128. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
  129. data/lib/active_support/ordered_hash.rb +3 -3
  130. data/lib/active_support/ordered_options.rb +14 -0
  131. data/lib/active_support/parameter_filter.rb +84 -69
  132. data/lib/active_support/proxy_object.rb +2 -0
  133. data/lib/active_support/railtie.rb +33 -21
  134. data/lib/active_support/reloader.rb +12 -4
  135. data/lib/active_support/rescuable.rb +2 -0
  136. data/lib/active_support/secure_compare_rotator.rb +16 -9
  137. data/lib/active_support/string_inquirer.rb +3 -1
  138. data/lib/active_support/subscriber.rb +9 -27
  139. data/lib/active_support/syntax_error_proxy.rb +49 -0
  140. data/lib/active_support/tagged_logging.rb +60 -24
  141. data/lib/active_support/test_case.rb +153 -6
  142. data/lib/active_support/testing/assertions.rb +25 -9
  143. data/lib/active_support/testing/autorun.rb +0 -2
  144. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  145. data/lib/active_support/testing/deprecation.rb +25 -25
  146. data/lib/active_support/testing/error_reporter_assertions.rb +108 -0
  147. data/lib/active_support/testing/isolation.rb +1 -1
  148. data/lib/active_support/testing/method_call_assertions.rb +21 -8
  149. data/lib/active_support/testing/parallelize_executor.rb +8 -3
  150. data/lib/active_support/testing/stream.rb +1 -1
  151. data/lib/active_support/testing/strict_warnings.rb +38 -0
  152. data/lib/active_support/testing/time_helpers.rb +32 -14
  153. data/lib/active_support/time_with_zone.rb +4 -14
  154. data/lib/active_support/values/time_zone.rb +9 -7
  155. data/lib/active_support/version.rb +1 -1
  156. data/lib/active_support/xml_mini/jdom.rb +3 -10
  157. data/lib/active_support/xml_mini/nokogiri.rb +1 -1
  158. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  159. data/lib/active_support/xml_mini/rexml.rb +1 -1
  160. data/lib/active_support/xml_mini.rb +2 -2
  161. data/lib/active_support.rb +13 -3
  162. metadata +106 -21
  163. data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
  164. data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
  165. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
  166. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
  167. data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
  168. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
  169. data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
  170. data/lib/active_support/core_ext/uri.rb +0 -5
  171. data/lib/active_support/per_thread_registry.rb +0 -65
@@ -3,16 +3,20 @@
3
3
  begin
4
4
  require "dalli"
5
5
  rescue LoadError => e
6
- $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
6
+ warn "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
7
7
  raise e
8
8
  end
9
9
 
10
+ require "connection_pool"
10
11
  require "delegate"
11
12
  require "active_support/core_ext/enumerable"
12
13
  require "active_support/core_ext/array/extract_options"
14
+ require "active_support/core_ext/numeric/time"
13
15
 
14
16
  module ActiveSupport
15
17
  module Cache
18
+ # = Memcached \Cache \Store
19
+ #
16
20
  # A cache store implementation which stores data in Memcached:
17
21
  # https://memcached.org
18
22
  #
@@ -26,6 +30,10 @@ module ActiveSupport
26
30
  # MemCacheStore implements the Strategy::LocalCache strategy which implements
27
31
  # an in-memory cache inside of a block.
28
32
  class MemCacheStore < Store
33
+ # These options represent behavior overridden by this implementation and should
34
+ # not be allowed to get down to the Dalli client
35
+ OVERRIDDEN_OPTIONS = UNIVERSAL_OPTIONS
36
+
29
37
  # Advertise cache versioning support.
30
38
  def self.supports_cache_versioning?
31
39
  true
@@ -73,6 +81,7 @@ module ActiveSupport
73
81
  end
74
82
  prepend DupLocalCache
75
83
 
84
+ KEY_MAX_SIZE = 250
76
85
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
77
86
 
78
87
  # Creates a new Dalli::Client instance with specified addresses and options.
@@ -90,11 +99,10 @@ module ActiveSupport
90
99
  addresses = nil if addresses.compact.empty?
91
100
  pool_options = retrieve_pool_options(options)
92
101
 
93
- if pool_options.empty?
94
- Dalli::Client.new(addresses, options)
95
- else
96
- ensure_connection_pool_added!
102
+ if pool_options
97
103
  ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
104
+ else
105
+ Dalli::Client.new(addresses, options)
98
106
  end
99
107
  end
100
108
 
@@ -106,6 +114,7 @@ module ActiveSupport
106
114
  #
107
115
  # If no addresses are provided, but <tt>ENV['MEMCACHE_SERVERS']</tt> is defined, it will be used instead. Otherwise,
108
116
  # 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.
109
118
  def initialize(*addresses)
110
119
  addresses = addresses.flatten
111
120
  options = addresses.extract_options!
@@ -115,19 +124,28 @@ module ActiveSupport
115
124
  super(options)
116
125
 
117
126
  unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
118
- raise ArgumentError, "First argument must be an empty array, an array of hosts or a Dalli::Client instance."
127
+ raise ArgumentError, "First argument must be an empty array, address, or array of addresses."
119
128
  end
120
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
121
134
  @data = addresses.first
122
135
  else
123
- mem_cache_options = options.dup
136
+ @mem_cache_options = options.dup
124
137
  # The value "compress: false" prevents duplicate compression within Dalli.
125
- mem_cache_options[:compress] = false
126
- (UNIVERSAL_OPTIONS - %i(compress)).each { |name| mem_cache_options.delete(name) }
127
- @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
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]))
128
141
  end
129
142
  end
130
143
 
144
+ def inspect
145
+ instance = @data || @mem_cache_options
146
+ "#<#{self.class} options=#{options.inspect} mem_cache=#{instance.inspect}>"
147
+ end
148
+
131
149
  ##
132
150
  # :method: write
133
151
  # :call-seq: write(name, value, options = nil)
@@ -144,28 +162,50 @@ module ActiveSupport
144
162
  # * <tt>unless_exist: true</tt> - Prevents overwriting an existing cache
145
163
  # entry.
146
164
 
147
- # Increment a cached value. This method uses the memcached incr atomic
148
- # operator and can only be used on values written with the +:raw+ option.
149
- # Calling it on a value not stored with +:raw+ will initialize that value
150
- # to zero.
165
+ # Increment a cached integer value using the memcached incr atomic operator.
166
+ # Returns the updated value.
167
+ #
168
+ # If the key is unset or has expired, it will be set to +amount+:
169
+ #
170
+ # cache.increment("foo") # => 1
171
+ # cache.increment("bar", 100) # => 100
172
+ #
173
+ # To set a specific value, call #write passing <tt>raw: true</tt>:
174
+ #
175
+ # cache.write("baz", 5, raw: true)
176
+ # cache.increment("baz") # => 6
177
+ #
178
+ # Incrementing a non-numeric value, or a value written without
179
+ # <tt>raw: true</tt>, will fail and return +nil+.
151
180
  def increment(name, amount = 1, options = nil)
152
181
  options = merged_options(options)
153
182
  instrument(:increment, name, amount: amount) do
154
183
  rescue_error_with nil do
155
- @data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in]) }
184
+ @data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in], amount) }
156
185
  end
157
186
  end
158
187
  end
159
188
 
160
- # Decrement a cached value. This method uses the memcached decr atomic
161
- # operator and can only be used on values written with the +:raw+ option.
162
- # Calling it on a value not stored with +:raw+ will initialize that value
163
- # to zero.
189
+ # Decrement a cached integer value using the memcached decr atomic operator.
190
+ # Returns the updated value.
191
+ #
192
+ # If the key is unset or has expired, it will be set to 0. Memcached
193
+ # does not support negative counters.
194
+ #
195
+ # cache.decrement("foo") # => 0
196
+ #
197
+ # To set a specific value, call #write passing <tt>raw: true</tt>:
198
+ #
199
+ # cache.write("baz", 5, raw: true)
200
+ # cache.decrement("baz") # => 4
201
+ #
202
+ # Decrementing a non-numeric value, or a value written without
203
+ # <tt>raw: true</tt>, will fail and return +nil+.
164
204
  def decrement(name, amount = 1, options = nil)
165
205
  options = merged_options(options)
166
206
  instrument(:decrement, name, amount: amount) do
167
207
  rescue_error_with nil do
168
- @data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in]) }
208
+ @data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in], 0) }
169
209
  end
170
210
  end
171
211
  end
@@ -182,54 +222,20 @@ module ActiveSupport
182
222
  end
183
223
 
184
224
  private
185
- module Coders # :nodoc:
186
- class << self
187
- def [](version)
188
- case version
189
- when 6.1
190
- Rails61Coder
191
- when 7.0
192
- Rails70Coder
193
- else
194
- raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}"
195
- end
196
- end
197
- end
198
-
199
- module Loader
200
- def load(payload)
201
- if payload.is_a?(Entry)
202
- payload
203
- else
204
- Cache::Coders::Loader.load(payload)
205
- end
206
- end
207
- end
208
-
209
- module Rails61Coder
210
- include Loader
211
- extend self
212
-
213
- def dump(entry)
214
- entry
215
- end
216
-
217
- def dump_compressed(entry, threshold)
218
- entry.compressed(threshold)
219
- end
220
- end
221
-
222
- module Rails70Coder
223
- include Cache::Coders::Rails70Coder
224
- include Loader
225
- extend self
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
226
236
  end
227
237
  end
228
238
 
229
- def default_coder
230
- Coders[Cache.format_version]
231
- end
232
-
233
239
  # Read an entry from the cache.
234
240
  def read_entry(key, **options)
235
241
  deserialize_entry(read_serialized_entry(key, **options), **options)
@@ -270,7 +276,7 @@ module ActiveSupport
270
276
  raw_values.each do |key, value|
271
277
  entry = deserialize_entry(value, raw: options[:raw])
272
278
 
273
- unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
279
+ unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
274
280
  values[keys_to_names[key]] = entry.value
275
281
  end
276
282
  end
@@ -299,7 +305,13 @@ module ActiveSupport
299
305
  if key
300
306
  key = key.dup.force_encoding(Encoding::ASCII_8BIT)
301
307
  key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
302
- key = "#{key[0, 212]}:hash:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
308
+
309
+ if key.size > KEY_MAX_SIZE
310
+ key_separator = ":hash:"
311
+ key_hash = ActiveSupport::Digest.hexdigest(key)
312
+ key_trim_size = KEY_MAX_SIZE - key_separator.size - key_hash.size
313
+ key = "#{key[0, key_trim_size]}#{key_separator}#{key_hash}"
314
+ end
303
315
  end
304
316
  key
305
317
  end
@@ -315,8 +327,12 @@ module ActiveSupport
315
327
  def rescue_error_with(fallback)
316
328
  yield
317
329
  rescue Dalli::DalliError => error
318
- ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
319
330
  logger.error("DalliError (#{error}): #{error.message}") if logger
331
+ ActiveSupport.error_reporter&.report(
332
+ error,
333
+ severity: :warning,
334
+ source: "mem_cache_store.active_support",
335
+ )
320
336
  fallback
321
337
  end
322
338
  end
@@ -4,10 +4,12 @@ require "monitor"
4
4
 
5
5
  module ActiveSupport
6
6
  module Cache
7
+ # = Memory \Cache \Store
8
+ #
7
9
  # A cache store implementation which stores everything into memory in the
8
- # same process. If you're running multiple Ruby on Rails server processes
10
+ # same process. If you're running multiple Ruby on \Rails server processes
9
11
  # (which is the case if you're using Phusion Passenger or puma clustered mode),
10
- # then this means that Rails server process instances won't be able
12
+ # then this means that \Rails server process instances won't be able
11
13
  # to share cache data with each other and this may not be the most
12
14
  # appropriate cache in that scenario.
13
15
  #
@@ -28,25 +30,49 @@ module ActiveSupport
28
30
  extend self
29
31
 
30
32
  def dump(entry)
31
- entry.dup_value! unless entry.compressed?
32
- entry
33
+ if entry.value && entry.value != true && !entry.value.is_a?(Numeric)
34
+ Cache::Entry.new(dump_value(entry.value), expires_at: entry.expires_at, version: entry.version)
35
+ else
36
+ entry
37
+ end
33
38
  end
34
39
 
35
40
  def dump_compressed(entry, threshold)
36
- entry = entry.compressed(threshold)
37
- entry.dup_value! unless entry.compressed?
38
- entry
41
+ compressed_entry = entry.compressed(threshold)
42
+ compressed_entry.compressed? ? compressed_entry : dump(entry)
39
43
  end
40
44
 
41
45
  def load(entry)
42
- entry = entry.dup
43
- entry.dup_value!
44
- entry
46
+ if !entry.compressed? && entry.value.is_a?(String)
47
+ Cache::Entry.new(load_value(entry.value), expires_at: entry.expires_at, version: entry.version)
48
+ else
49
+ entry
50
+ end
45
51
  end
52
+
53
+ private
54
+ MARSHAL_SIGNATURE = "\x04\x08".b.freeze
55
+
56
+ def dump_value(value)
57
+ if value.is_a?(String) && !value.start_with?(MARSHAL_SIGNATURE)
58
+ value.dup
59
+ else
60
+ Marshal.dump(value)
61
+ end
62
+ end
63
+
64
+ def load_value(string)
65
+ if string.start_with?(MARSHAL_SIGNATURE)
66
+ Marshal.load(string)
67
+ else
68
+ string.dup
69
+ end
70
+ end
46
71
  end
47
72
 
48
73
  def initialize(options = nil)
49
74
  options ||= {}
75
+ options[:coder] = DupCoder unless options.key?(:coder) || options.key?(:serializer)
50
76
  # Disable compression by default.
51
77
  options[:compress] ||= false
52
78
  super(options)
@@ -74,7 +100,7 @@ module ActiveSupport
74
100
  # Preemptively iterates through all stored keys and removes the ones which have expired.
75
101
  def cleanup(options = nil)
76
102
  options = merged_options(options)
77
- instrument(:cleanup, size: @data.size) do
103
+ _instrument(:cleanup, size: @data.size) do
78
104
  keys = synchronize { @data.keys }
79
105
  keys.each do |key|
80
106
  entry = @data[key]
@@ -108,12 +134,33 @@ module ActiveSupport
108
134
  @pruning
109
135
  end
110
136
 
111
- # Increment an integer value in the cache.
137
+ # Increment a cached integer value. Returns the updated value.
138
+ #
139
+ # If the key is unset, it will be set to +amount+:
140
+ #
141
+ # cache.increment("foo") # => 1
142
+ # cache.increment("bar", 100) # => 100
143
+ #
144
+ # To set a specific value, call #write:
145
+ #
146
+ # cache.write("baz", 5)
147
+ # cache.increment("baz") # => 6
148
+ #
112
149
  def increment(name, amount = 1, options = nil)
113
150
  modify_value(name, amount, options)
114
151
  end
115
152
 
116
- # Decrement an integer value in the cache.
153
+ # Decrement a cached integer value. Returns the updated value.
154
+ #
155
+ # If the key is unset or has expired, it will be set to +-amount+.
156
+ #
157
+ # cache.decrement("foo") # => -1
158
+ #
159
+ # To set a specific value, call #write:
160
+ #
161
+ # cache.write("baz", 5)
162
+ # cache.decrement("baz") # => 4
163
+ #
117
164
  def decrement(name, amount = 1, options = nil)
118
165
  modify_value(name, -amount, options)
119
166
  end
@@ -143,10 +190,6 @@ module ActiveSupport
143
190
  private
144
191
  PER_ENTRY_OVERHEAD = 240
145
192
 
146
- def default_coder
147
- DupCoder
148
- end
149
-
150
193
  def cached_size(key, payload)
151
194
  key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
152
195
  end
@@ -166,7 +209,7 @@ module ActiveSupport
166
209
  def write_entry(key, entry, **options)
167
210
  payload = serialize_entry(entry, **options)
168
211
  synchronize do
169
- return false if options[:unless_exist] && @data.key?(key)
212
+ return false if options[:unless_exist] && exist?(key)
170
213
 
171
214
  old_payload = @data[key]
172
215
  if old_payload
@@ -188,14 +231,23 @@ module ActiveSupport
188
231
  end
189
232
  end
190
233
 
234
+ # Modifies the amount of an integer value that is stored in the cache.
235
+ # If the key is not found it is created and set to +amount+.
191
236
  def modify_value(name, amount, options)
192
237
  options = merged_options(options)
193
- synchronize do
194
- if num = read(name, options)
195
- num = num.to_i + amount
196
- write(name, num, options)
197
- num
198
- end
238
+ key = normalize_key(name, options)
239
+ version = normalize_version(name, options)
240
+
241
+ entry = read_entry(key, **options)
242
+
243
+ if !entry || entry.expired? || entry.mismatched?(version)
244
+ write(name, Integer(amount), options)
245
+ amount
246
+ else
247
+ num = entry.value.to_i + amount
248
+ entry = Entry.new(num, expires_at: entry.expires_at, version: entry.version)
249
+ write_entry(key, entry)
250
+ num
199
251
  end
200
252
  end
201
253
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ActiveSupport
4
4
  module Cache
5
+ # = Null \Cache \Store
6
+ #
5
7
  # A cache store implementation which doesn't actually store anything. Useful in
6
8
  # development and test environments where you don't want caching turned on but
7
9
  # need to go through the caching interface.
@@ -32,6 +34,10 @@ module ActiveSupport
32
34
  def delete_matched(matcher, options = nil)
33
35
  end
34
36
 
37
+ def inspect # :nodoc:
38
+ "#<#{self.class.name} options=#{@options.inspect}>"
39
+ end
40
+
35
41
  private
36
42
  def read_entry(key, **s)
37
43
  deserialize_entry(read_serialized_entry(key))