activesupport 7.0.8.7 → 7.2.2.1
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 -459
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_support/actionable_error.rb +3 -1
- data/lib/active_support/array_inquirer.rb +3 -1
- data/lib/active_support/backtrace_cleaner.rb +39 -7
- data/lib/active_support/benchmarkable.rb +1 -0
- data/lib/active_support/broadcast_logger.rb +251 -0
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +49 -17
- data/lib/active_support/cache/mem_cache_store.rb +94 -128
- data/lib/active_support/cache/memory_store.rb +80 -25
- data/lib/active_support/cache/null_store.rb +6 -0
- data/lib/active_support/cache/redis_cache_store.rb +165 -152
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +29 -14
- data/lib/active_support/cache.rb +363 -291
- data/lib/active_support/callbacks.rb +118 -134
- data/lib/active_support/code_generator.rb +15 -10
- data/lib/active_support/concern.rb +4 -2
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/configurable.rb +10 -0
- data/lib/active_support/core_ext/array/conversions.rb +1 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/class/subclasses.rb +17 -34
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/conversions.rb +1 -2
- data/lib/active_support/core_ext/date.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +10 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -2
- data/lib/active_support/core_ext/date_time.rb +0 -1
- data/lib/active_support/core_ext/digest/uuid.rb +7 -10
- data/lib/active_support/core_ext/enumerable.rb +3 -75
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +34 -16
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -119
- data/lib/active_support/core_ext/module/deprecation.rb +12 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +5 -3
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
- data/lib/active_support/core_ext/object/json.rb +17 -7
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +4 -4
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +2 -0
- data/lib/active_support/core_ext/pathname.rb +1 -0
- data/lib/active_support/core_ext/range/conversions.rb +28 -7
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +1 -5
- data/lib/active_support/core_ext/string/conversions.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +16 -5
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +34 -177
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +36 -30
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +1 -3
- data/lib/active_support/core_ext/time/zones.rb +4 -4
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +53 -46
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +9 -16
- data/lib/active_support/deprecation/behaviors.rb +65 -42
- data/lib/active_support/deprecation/constant_accessor.rb +47 -25
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +3 -5
- data/lib/active_support/deprecation/method_wrappers.rb +6 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
- data/lib/active_support/deprecation/reporting.rb +49 -27
- data/lib/active_support/deprecation.rb +39 -9
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +66 -172
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -4
- data/lib/active_support/duration.rb +13 -7
- data/lib/active_support/encrypted_configuration.rb +30 -9
- data/lib/active_support/encrypted_file.rb +9 -4
- data/lib/active_support/environment_inquirer.rb +22 -2
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +160 -36
- data/lib/active_support/evented_file_update_checker.rb +0 -1
- data/lib/active_support/execution_wrapper.rb +4 -5
- data/lib/active_support/file_update_checker.rb +5 -3
- data/lib/active_support/fork_tracker.rb +4 -32
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +41 -25
- data/lib/active_support/html_safe_translation.rb +19 -6
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +20 -13
- data/lib/active_support/inflector/inflections.rb +2 -0
- data/lib/active_support/inflector/methods.rb +23 -11
- data/lib/active_support/inflector/transliterate.rb +3 -1
- data/lib/active_support/isolated_execution_state.rb +26 -22
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +25 -43
- data/lib/active_support/key_generator.rb +9 -1
- data/lib/active_support/lazy_load_hooks.rb +6 -4
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber.rb +74 -34
- data/lib/active_support/logger.rb +22 -60
- data/lib/active_support/logger_thread_safe_level.rb +10 -32
- data/lib/active_support/message_encryptor.rb +197 -53
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +305 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +220 -89
- data/lib/active_support/message_verifiers.rb +135 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +111 -45
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +4 -2
- data/lib/active_support/multibyte/unicode.rb +9 -37
- data/lib/active_support/notifications/fanout.rb +248 -87
- data/lib/active_support/notifications/instrumenter.rb +93 -25
- data/lib/active_support/notifications.rb +29 -28
- data/lib/active_support/number_helper/number_converter.rb +16 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
- data/lib/active_support/number_helper.rb +379 -318
- data/lib/active_support/option_merger.rb +2 -2
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +67 -15
- data/lib/active_support/parameter_filter.rb +84 -69
- data/lib/active_support/proxy_object.rb +8 -3
- data/lib/active_support/railtie.rb +25 -20
- data/lib/active_support/reloader.rb +12 -4
- data/lib/active_support/rescuable.rb +2 -0
- data/lib/active_support/secure_compare_rotator.rb +16 -9
- data/lib/active_support/string_inquirer.rb +4 -2
- data/lib/active_support/subscriber.rb +10 -27
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +64 -25
- data/lib/active_support/test_case.rb +156 -7
- data/lib/active_support/testing/assertions.rb +28 -12
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/deprecation.rb +20 -27
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +21 -9
- data/lib/active_support/testing/method_call_assertions.rb +7 -8
- data/lib/active_support/testing/parallelization/server.rb +3 -0
- data/lib/active_support/testing/parallelize_executor.rb +8 -3
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/strict_warnings.rb +43 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +38 -16
- data/lib/active_support/time_with_zone.rb +12 -18
- data/lib/active_support/values/time_zone.rb +25 -14
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -10
- data/lib/active_support/xml_mini/nokogiri.rb +1 -1
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +1 -1
- data/lib/active_support/xml_mini.rb +12 -3
- data/lib/active_support.rb +15 -3
- metadata +140 -19
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -40
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -36
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -5
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -73
- data/lib/active_support/core_ext/uri.rb +0 -5
- data/lib/active_support/deprecation/instance_delegator.rb +0 -38
- data/lib/active_support/per_thread_registry.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
data/lib/active_support/cache.rb
CHANGED
@@ -2,14 +2,15 @@
|
|
2
2
|
|
3
3
|
require "zlib"
|
4
4
|
require "active_support/core_ext/array/extract_options"
|
5
|
-
require "active_support/core_ext/array/wrap"
|
6
5
|
require "active_support/core_ext/enumerable"
|
7
6
|
require "active_support/core_ext/module/attribute_accessors"
|
8
7
|
require "active_support/core_ext/numeric/bytes"
|
9
|
-
require "active_support/core_ext/numeric/time"
|
10
8
|
require "active_support/core_ext/object/to_param"
|
11
9
|
require "active_support/core_ext/object/try"
|
12
10
|
require "active_support/core_ext/string/inflections"
|
11
|
+
require_relative "cache/coder"
|
12
|
+
require_relative "cache/entry"
|
13
|
+
require_relative "cache/serializer_with_fallback"
|
13
14
|
|
14
15
|
module ActiveSupport
|
15
16
|
# See ActiveSupport::Cache::Store for documentation.
|
@@ -22,20 +23,36 @@ module ActiveSupport
|
|
22
23
|
|
23
24
|
# These options mean something to all cache implementations. Individual cache
|
24
25
|
# implementations may support additional options.
|
25
|
-
UNIVERSAL_OPTIONS = [
|
26
|
-
|
27
|
-
|
26
|
+
UNIVERSAL_OPTIONS = [
|
27
|
+
:coder,
|
28
|
+
:compress,
|
29
|
+
:compress_threshold,
|
30
|
+
:compressor,
|
31
|
+
:expire_in,
|
32
|
+
:expired_in,
|
33
|
+
:expires_in,
|
34
|
+
:namespace,
|
35
|
+
:race_condition_ttl,
|
36
|
+
:serializer,
|
37
|
+
:skip_nil,
|
38
|
+
]
|
28
39
|
|
29
40
|
# Mapping of canonical option names to aliases that a store will recognize.
|
30
41
|
OPTION_ALIASES = {
|
31
42
|
expires_in: [:expire_in, :expired_in]
|
32
43
|
}.freeze
|
33
44
|
|
45
|
+
DEFAULT_COMPRESS_LIMIT = 1.kilobyte
|
46
|
+
|
47
|
+
# Raised by coders when the cache entry can't be deserialized.
|
48
|
+
# This error is treated as a cache miss.
|
49
|
+
DeserializationError = Class.new(StandardError)
|
50
|
+
|
34
51
|
module Strategy
|
35
52
|
autoload :LocalCache, "active_support/cache/strategy/local_cache"
|
36
53
|
end
|
37
54
|
|
38
|
-
@format_version =
|
55
|
+
@format_version = 7.0
|
39
56
|
|
40
57
|
class << self
|
41
58
|
attr_accessor :format_version
|
@@ -69,13 +86,7 @@ module ActiveSupport
|
|
69
86
|
case store
|
70
87
|
when Symbol
|
71
88
|
options = parameters.extract_options!
|
72
|
-
|
73
|
-
# see https://github.com/rails/rails/pull/41522#discussion_r581186602
|
74
|
-
if options.empty?
|
75
|
-
retrieve_store_class(store).new(*parameters)
|
76
|
-
else
|
77
|
-
retrieve_store_class(store).new(*parameters, **options)
|
78
|
-
end
|
89
|
+
retrieve_store_class(store).new(*parameters, **options)
|
79
90
|
when Array
|
80
91
|
lookup_store(*store)
|
81
92
|
when nil
|
@@ -132,6 +143,8 @@ module ActiveSupport
|
|
132
143
|
end
|
133
144
|
end
|
134
145
|
|
146
|
+
# = Active Support \Cache \Store
|
147
|
+
#
|
135
148
|
# An abstract cache store class. There are multiple cache store
|
136
149
|
# implementations, each having its own additional features. See the classes
|
137
150
|
# under the ActiveSupport::Cache module, e.g.
|
@@ -141,13 +154,13 @@ module ActiveSupport
|
|
141
154
|
# Some implementations may not support all methods beyond the basic cache
|
142
155
|
# methods of #fetch, #write, #read, #exist?, and #delete.
|
143
156
|
#
|
144
|
-
# ActiveSupport::Cache::Store can store any Ruby object that is supported
|
145
|
-
# its +coder+'s +dump+ and +load+ methods.
|
157
|
+
# +ActiveSupport::Cache::Store+ can store any Ruby object that is supported
|
158
|
+
# by its +coder+'s +dump+ and +load+ methods.
|
146
159
|
#
|
147
160
|
# cache = ActiveSupport::Cache::MemoryStore.new
|
148
161
|
#
|
149
162
|
# cache.read('city') # => nil
|
150
|
-
# cache.write('city', "Duckburgh")
|
163
|
+
# cache.write('city', "Duckburgh") # => true
|
151
164
|
# cache.read('city') # => "Duckburgh"
|
152
165
|
#
|
153
166
|
# cache.write('not serializable', Proc.new {}) # => TypeError
|
@@ -174,24 +187,37 @@ module ActiveSupport
|
|
174
187
|
#
|
175
188
|
class Store
|
176
189
|
cattr_accessor :logger, instance_writer: true
|
190
|
+
cattr_accessor :raise_on_invalid_cache_expiration_time, default: false
|
177
191
|
|
178
192
|
attr_reader :silence, :options
|
179
193
|
alias :silence? :silence
|
180
194
|
|
181
195
|
class << self
|
182
196
|
private
|
197
|
+
DEFAULT_POOL_OPTIONS = { size: 5, timeout: 5 }.freeze
|
198
|
+
private_constant :DEFAULT_POOL_OPTIONS
|
199
|
+
|
183
200
|
def retrieve_pool_options(options)
|
184
|
-
|
185
|
-
pool_options
|
186
|
-
|
201
|
+
if options.key?(:pool)
|
202
|
+
pool_options = options.delete(:pool)
|
203
|
+
else
|
204
|
+
pool_options = true
|
187
205
|
end
|
188
|
-
end
|
189
206
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
207
|
+
case pool_options
|
208
|
+
when false, nil
|
209
|
+
return false
|
210
|
+
when true
|
211
|
+
pool_options = DEFAULT_POOL_OPTIONS
|
212
|
+
when Hash
|
213
|
+
pool_options[:size] = Integer(pool_options[:size]) if pool_options.key?(:size)
|
214
|
+
pool_options[:timeout] = Float(pool_options[:timeout]) if pool_options.key?(:timeout)
|
215
|
+
pool_options = DEFAULT_POOL_OPTIONS.merge(pool_options)
|
216
|
+
else
|
217
|
+
raise TypeError, "Invalid :pool argument, expected Hash, got: #{pool_options.inspect}"
|
218
|
+
end
|
219
|
+
|
220
|
+
pool_options unless pool_options.empty?
|
195
221
|
end
|
196
222
|
end
|
197
223
|
|
@@ -199,21 +225,90 @@ module ActiveSupport
|
|
199
225
|
#
|
200
226
|
# ==== Options
|
201
227
|
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
# applications.
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
228
|
+
# [+:namespace+]
|
229
|
+
# Sets the namespace for the cache. This option is especially useful if
|
230
|
+
# your application shares a cache with other applications.
|
231
|
+
#
|
232
|
+
# [+:serializer+]
|
233
|
+
# The serializer for cached values. Must respond to +dump+ and +load+.
|
234
|
+
#
|
235
|
+
# The default serializer depends on the cache format version (set via
|
236
|
+
# +config.active_support.cache_format_version+ when using Rails). The
|
237
|
+
# default serializer for each format version includes a fallback
|
238
|
+
# mechanism to deserialize values from any format version. This behavior
|
239
|
+
# makes it easy to migrate between format versions without invalidating
|
240
|
+
# the entire cache.
|
241
|
+
#
|
242
|
+
# You can also specify <tt>serializer: :message_pack</tt> to use a
|
243
|
+
# preconfigured serializer based on ActiveSupport::MessagePack. The
|
244
|
+
# +:message_pack+ serializer includes the same deserialization fallback
|
245
|
+
# mechanism, allowing easy migration from (or to) the default
|
246
|
+
# serializer. The +:message_pack+ serializer may improve performance,
|
247
|
+
# but it requires the +msgpack+ gem.
|
248
|
+
#
|
249
|
+
# [+:compressor+]
|
250
|
+
# The compressor for serialized cache values. Must respond to +deflate+
|
251
|
+
# and +inflate+.
|
252
|
+
#
|
253
|
+
# The default compressor is +Zlib+. To define a new custom compressor
|
254
|
+
# that also decompresses old cache entries, you can check compressed
|
255
|
+
# values for Zlib's <tt>"\x78"</tt> signature:
|
256
|
+
#
|
257
|
+
# module MyCompressor
|
258
|
+
# def self.deflate(dumped)
|
259
|
+
# # compression logic... (make sure result does not start with "\x78"!)
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
# def self.inflate(compressed)
|
263
|
+
# if compressed.start_with?("\x78")
|
264
|
+
# Zlib.inflate(compressed)
|
265
|
+
# else
|
266
|
+
# # decompression logic...
|
267
|
+
# end
|
268
|
+
# end
|
269
|
+
# end
|
270
|
+
#
|
271
|
+
# ActiveSupport::Cache.lookup_store(:redis_cache_store, compressor: MyCompressor)
|
272
|
+
#
|
273
|
+
# [+:coder+]
|
274
|
+
# The coder for serializing and (optionally) compressing cache entries.
|
275
|
+
# Must respond to +dump+ and +load+.
|
276
|
+
#
|
277
|
+
# The default coder composes the serializer and compressor, and includes
|
278
|
+
# some performance optimizations. If you only need to override the
|
279
|
+
# serializer or compressor, you should specify the +:serializer+ or
|
280
|
+
# +:compressor+ options instead.
|
281
|
+
#
|
282
|
+
# If the store can handle cache entries directly, you may also specify
|
283
|
+
# <tt>coder: nil</tt> to omit the serializer, compressor, and coder. For
|
284
|
+
# example, if you are using ActiveSupport::Cache::MemoryStore and can
|
285
|
+
# guarantee that cache values will not be mutated, you can specify
|
286
|
+
# <tt>coder: nil</tt> to avoid the overhead of safeguarding against
|
287
|
+
# mutation.
|
288
|
+
#
|
289
|
+
# The +:coder+ option is mutally exclusive with the +:serializer+ and
|
290
|
+
# +:compressor+ options. Specifying them together will raise an
|
291
|
+
# +ArgumentError+.
|
208
292
|
#
|
209
293
|
# Any other specified options are treated as default options for the
|
210
294
|
# relevant cache operations, such as #read, #write, and #fetch.
|
211
295
|
def initialize(options = nil)
|
212
|
-
@options = options ? normalize_options(options) : {}
|
296
|
+
@options = options ? validate_options(normalize_options(options)) : {}
|
297
|
+
|
213
298
|
@options[:compress] = true unless @options.key?(:compress)
|
214
|
-
@options[:compress_threshold]
|
299
|
+
@options[:compress_threshold] ||= DEFAULT_COMPRESS_LIMIT
|
300
|
+
|
301
|
+
@coder = @options.delete(:coder) do
|
302
|
+
legacy_serializer = Cache.format_version < 7.1 && !@options[:serializer]
|
303
|
+
serializer = @options.delete(:serializer) || default_serializer
|
304
|
+
serializer = Cache::SerializerWithFallback[serializer] if serializer.is_a?(Symbol)
|
305
|
+
compressor = @options.delete(:compressor) { Zlib }
|
306
|
+
|
307
|
+
Cache::Coder.new(serializer, compressor, legacy_serializer: legacy_serializer)
|
308
|
+
end
|
309
|
+
|
310
|
+
@coder ||= Cache::SerializerWithFallback[:passthrough]
|
215
311
|
|
216
|
-
@coder = @options.delete(:coder) { default_coder } || NullCoder
|
217
312
|
@coder_supports_compression = @coder.respond_to?(:dump_compressed)
|
218
313
|
end
|
219
314
|
|
@@ -225,7 +320,7 @@ module ActiveSupport
|
|
225
320
|
|
226
321
|
# Silences the logger within a block.
|
227
322
|
def mute
|
228
|
-
previous_silence, @silence =
|
323
|
+
previous_silence, @silence = @silence, true
|
229
324
|
yield
|
230
325
|
ensure
|
231
326
|
@silence = previous_silence
|
@@ -251,8 +346,8 @@ module ActiveSupport
|
|
251
346
|
#
|
252
347
|
# ==== Options
|
253
348
|
#
|
254
|
-
# Internally, +fetch+ calls
|
255
|
-
# miss. Thus, +fetch+ supports the same options as #read and #write.
|
349
|
+
# Internally, +fetch+ calls +read_entry+, and calls +write_entry+ on a
|
350
|
+
# cache miss. Thus, +fetch+ supports the same options as #read and #write.
|
256
351
|
# Additionally, +fetch+ supports the following options:
|
257
352
|
#
|
258
353
|
# * <tt>force: true</tt> - Forces a cache "miss," meaning we treat the
|
@@ -292,31 +387,59 @@ module ActiveSupport
|
|
292
387
|
# has elapsed.
|
293
388
|
#
|
294
389
|
# # Set all values to expire after one minute.
|
295
|
-
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1
|
390
|
+
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1)
|
296
391
|
#
|
297
|
-
# cache.write(
|
392
|
+
# cache.write("foo", "original value")
|
298
393
|
# val_1 = nil
|
299
394
|
# val_2 = nil
|
300
|
-
#
|
395
|
+
# p cache.read("foo") # => "original value"
|
301
396
|
#
|
302
|
-
#
|
303
|
-
#
|
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
|
304
405
|
# sleep 1
|
305
|
-
#
|
406
|
+
# "new value 1"
|
306
407
|
# end
|
307
408
|
# end
|
308
409
|
#
|
309
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
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"
|
313
417
|
# end
|
314
418
|
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
#
|
318
|
-
#
|
319
|
-
#
|
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
|
431
|
+
#
|
432
|
+
# ==== Dynamic Options
|
433
|
+
#
|
434
|
+
# In some cases it may be necessary to dynamically compute options based
|
435
|
+
# on the cached value. To support this, an ActiveSupport::Cache::WriteOptions
|
436
|
+
# instance is passed as the second argument to the block. For example:
|
437
|
+
#
|
438
|
+
# cache.fetch("authentication-token:#{user.id}") do |key, options|
|
439
|
+
# token = authenticate_to_service
|
440
|
+
# options.expires_at = token.expires_at
|
441
|
+
# token
|
442
|
+
# end
|
320
443
|
#
|
321
444
|
def fetch(name, options = nil, &block)
|
322
445
|
if block_given?
|
@@ -324,18 +447,30 @@ module ActiveSupport
|
|
324
447
|
key = normalize_key(name, options)
|
325
448
|
|
326
449
|
entry = nil
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
450
|
+
unless options[:force]
|
451
|
+
instrument(:read, key, options) do |payload|
|
452
|
+
cached_entry = read_entry(key, **options, event: payload)
|
453
|
+
entry = handle_expired_entry(cached_entry, key, options)
|
454
|
+
if entry
|
455
|
+
if entry.mismatched?(normalize_version(name, options))
|
456
|
+
entry = nil
|
457
|
+
else
|
458
|
+
begin
|
459
|
+
entry.value
|
460
|
+
rescue DeserializationError
|
461
|
+
entry = nil
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
payload[:super_operation] = :fetch if payload
|
466
|
+
payload[:hit] = !!entry if payload
|
467
|
+
end
|
333
468
|
end
|
334
469
|
|
335
470
|
if entry
|
336
471
|
get_entry_value(entry, name, options)
|
337
472
|
else
|
338
|
-
save_block_result_to_cache(name, options, &block)
|
473
|
+
save_block_result_to_cache(name, key, options, &block)
|
339
474
|
end
|
340
475
|
elsif options && options[:force]
|
341
476
|
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
|
@@ -354,6 +489,7 @@ module ActiveSupport
|
|
354
489
|
#
|
355
490
|
# ==== Options
|
356
491
|
#
|
492
|
+
# * +:namespace+ - Replace the store namespace for this call.
|
357
493
|
# * +:version+ - Specifies a version for the cache entry. If the cached
|
358
494
|
# version does not match the requested version, the read will be treated
|
359
495
|
# as a cache miss. This feature is used to support recyclable cache keys.
|
@@ -364,7 +500,7 @@ module ActiveSupport
|
|
364
500
|
key = normalize_key(name, options)
|
365
501
|
version = normalize_version(name, options)
|
366
502
|
|
367
|
-
instrument(:read,
|
503
|
+
instrument(:read, key, options) do |payload|
|
368
504
|
entry = read_entry(key, **options, event: payload)
|
369
505
|
|
370
506
|
if entry
|
@@ -377,7 +513,12 @@ module ActiveSupport
|
|
377
513
|
nil
|
378
514
|
else
|
379
515
|
payload[:hit] = true if payload
|
380
|
-
|
516
|
+
begin
|
517
|
+
entry.value
|
518
|
+
rescue DeserializationError
|
519
|
+
payload[:hit] = false
|
520
|
+
nil
|
521
|
+
end
|
381
522
|
end
|
382
523
|
else
|
383
524
|
payload[:hit] = false if payload
|
@@ -393,10 +534,12 @@ module ActiveSupport
|
|
393
534
|
#
|
394
535
|
# Returns a hash mapping the names provided to the values found.
|
395
536
|
def read_multi(*names)
|
537
|
+
return {} if names.empty?
|
538
|
+
|
396
539
|
options = names.extract_options!
|
397
540
|
options = merged_options(options)
|
398
541
|
|
399
|
-
|
542
|
+
instrument_multi :read_multi, names, options do |payload|
|
400
543
|
read_multi_entries(names, **options, event: payload).tap do |results|
|
401
544
|
payload[:hits] = results.keys
|
402
545
|
end
|
@@ -405,9 +548,11 @@ module ActiveSupport
|
|
405
548
|
|
406
549
|
# Cache Storage API to write multiple values at once.
|
407
550
|
def write_multi(hash, options = nil)
|
551
|
+
return hash if hash.empty?
|
552
|
+
|
408
553
|
options = merged_options(options)
|
409
554
|
|
410
|
-
|
555
|
+
instrument_multi :write_multi, hash, options do |payload|
|
411
556
|
entries = hash.each_with_object({}) do |(name, value), memo|
|
412
557
|
memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
413
558
|
end
|
@@ -433,7 +578,8 @@ module ActiveSupport
|
|
433
578
|
# # => { "bim" => "bam",
|
434
579
|
# # "unknown_key" => "Fallback value for key: unknown_key" }
|
435
580
|
#
|
436
|
-
#
|
581
|
+
# You may also specify additional options via the +options+ argument. See #fetch for details.
|
582
|
+
# Other options are passed to the underlying cache implementation. For example:
|
437
583
|
#
|
438
584
|
# cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
|
439
585
|
# "buzz"
|
@@ -446,29 +592,41 @@ module ActiveSupport
|
|
446
592
|
# # => nil
|
447
593
|
def fetch_multi(*names)
|
448
594
|
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
|
595
|
+
return {} if names.empty?
|
449
596
|
|
450
597
|
options = names.extract_options!
|
451
598
|
options = merged_options(options)
|
452
599
|
|
453
|
-
|
454
|
-
|
455
|
-
|
600
|
+
writes = {}
|
601
|
+
ordered = instrument_multi :read_multi, names, options do |payload|
|
602
|
+
if options[:force]
|
603
|
+
reads = {}
|
604
|
+
else
|
605
|
+
reads = read_multi_entries(names, **options)
|
606
|
+
end
|
607
|
+
|
456
608
|
ordered = names.index_with do |name|
|
457
609
|
reads.fetch(name) { writes[name] = yield(name) }
|
458
610
|
end
|
611
|
+
writes.compact! if options[:skip_nil]
|
459
612
|
|
460
613
|
payload[:hits] = reads.keys
|
461
614
|
payload[:super_operation] = :fetch_multi
|
462
615
|
|
463
|
-
write_multi(writes, options)
|
464
|
-
|
465
616
|
ordered
|
466
617
|
end
|
618
|
+
|
619
|
+
write_multi(writes, options)
|
620
|
+
|
621
|
+
ordered
|
467
622
|
end
|
468
623
|
|
469
624
|
# Writes the value to the cache with the key. The value must be supported
|
470
625
|
# by the +coder+'s +dump+ and +load+ methods.
|
471
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
|
+
#
|
472
630
|
# By default, cache entries larger than 1kB are compressed. Compression
|
473
631
|
# allows more data to be stored in the same memory footprint, leading to
|
474
632
|
# fewer cache evictions and higher hit rates.
|
@@ -501,32 +659,38 @@ module ActiveSupport
|
|
501
659
|
# Other options will be handled by the specific cache store implementation.
|
502
660
|
def write(name, value, options = nil)
|
503
661
|
options = merged_options(options)
|
662
|
+
key = normalize_key(name, options)
|
504
663
|
|
505
|
-
instrument(:write,
|
664
|
+
instrument(:write, key, options) do
|
506
665
|
entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
507
|
-
write_entry(
|
666
|
+
write_entry(key, entry, **options)
|
508
667
|
end
|
509
668
|
end
|
510
669
|
|
511
|
-
# Deletes an entry in the cache. Returns +true+ if an entry is deleted
|
670
|
+
# Deletes an entry in the cache. Returns +true+ if an entry is deleted
|
671
|
+
# and +false+ otherwise.
|
512
672
|
#
|
513
673
|
# Options are passed to the underlying cache implementation.
|
514
674
|
def delete(name, options = nil)
|
515
675
|
options = merged_options(options)
|
676
|
+
key = normalize_key(name, options)
|
516
677
|
|
517
|
-
instrument(:delete,
|
518
|
-
delete_entry(
|
678
|
+
instrument(:delete, key, options) do
|
679
|
+
delete_entry(key, **options)
|
519
680
|
end
|
520
681
|
end
|
521
682
|
|
522
|
-
# Deletes multiple entries in the cache.
|
683
|
+
# Deletes multiple entries in the cache. Returns the number of deleted
|
684
|
+
# entries.
|
523
685
|
#
|
524
686
|
# Options are passed to the underlying cache implementation.
|
525
687
|
def delete_multi(names, options = nil)
|
688
|
+
return 0 if names.empty?
|
689
|
+
|
526
690
|
options = merged_options(options)
|
527
691
|
names.map! { |key| normalize_key(key, options) }
|
528
692
|
|
529
|
-
|
693
|
+
instrument_multi(:delete_multi, names, options) do
|
530
694
|
delete_multi_entries(names, **options)
|
531
695
|
end
|
532
696
|
end
|
@@ -536,9 +700,10 @@ module ActiveSupport
|
|
536
700
|
# Options are passed to the underlying cache implementation.
|
537
701
|
def exist?(name, options = nil)
|
538
702
|
options = merged_options(options)
|
703
|
+
key = normalize_key(name, options)
|
539
704
|
|
540
|
-
instrument(:exist?,
|
541
|
-
entry = read_entry(
|
705
|
+
instrument(:exist?, key) do |payload|
|
706
|
+
entry = read_entry(key, **options, event: payload)
|
542
707
|
(entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
|
543
708
|
end
|
544
709
|
end
|
@@ -594,8 +759,15 @@ module ActiveSupport
|
|
594
759
|
end
|
595
760
|
|
596
761
|
private
|
597
|
-
def
|
598
|
-
|
762
|
+
def default_serializer
|
763
|
+
case Cache.format_version
|
764
|
+
when 7.0
|
765
|
+
Cache::SerializerWithFallback[:marshal_7_0]
|
766
|
+
when 7.1
|
767
|
+
Cache::SerializerWithFallback[:marshal_7_1]
|
768
|
+
else
|
769
|
+
raise ArgumentError, "Unrecognized ActiveSupport::Cache.format_version: #{Cache.format_version.inspect}"
|
770
|
+
end
|
599
771
|
end
|
600
772
|
|
601
773
|
# Adds the namespace defined in the options to a pattern designed to
|
@@ -632,14 +804,16 @@ module ActiveSupport
|
|
632
804
|
def serialize_entry(entry, **options)
|
633
805
|
options = merged_options(options)
|
634
806
|
if @coder_supports_compression && options[:compress]
|
635
|
-
@coder.dump_compressed(entry, options[:compress_threshold]
|
807
|
+
@coder.dump_compressed(entry, options[:compress_threshold])
|
636
808
|
else
|
637
809
|
@coder.dump(entry)
|
638
810
|
end
|
639
811
|
end
|
640
812
|
|
641
|
-
def deserialize_entry(payload)
|
813
|
+
def deserialize_entry(payload, **)
|
642
814
|
payload.nil? ? nil : @coder.load(payload)
|
815
|
+
rescue DeserializationError
|
816
|
+
nil
|
643
817
|
end
|
644
818
|
|
645
819
|
# Reads multiple entries from the cache implementation. Subclasses MAY
|
@@ -685,6 +859,22 @@ module ActiveSupport
|
|
685
859
|
def merged_options(call_options)
|
686
860
|
if call_options
|
687
861
|
call_options = normalize_options(call_options)
|
862
|
+
if call_options.key?(:expires_in) && call_options.key?(:expires_at)
|
863
|
+
raise ArgumentError, "Either :expires_in or :expires_at can be supplied, but not both"
|
864
|
+
end
|
865
|
+
|
866
|
+
expires_at = call_options.delete(:expires_at)
|
867
|
+
call_options[:expires_in] = (expires_at - Time.now) if expires_at
|
868
|
+
|
869
|
+
if call_options[:expires_in].is_a?(Time)
|
870
|
+
expires_in = call_options[:expires_in]
|
871
|
+
raise ArgumentError.new("expires_in parameter should not be a Time. Did you mean to use expires_at? Got: #{expires_in}")
|
872
|
+
end
|
873
|
+
if call_options[:expires_in]&.negative?
|
874
|
+
expires_in = call_options.delete(:expires_in)
|
875
|
+
handle_invalid_expires_in("Cache expiration time is invalid, cannot be negative: #{expires_in}")
|
876
|
+
end
|
877
|
+
|
688
878
|
if options.empty?
|
689
879
|
call_options
|
690
880
|
else
|
@@ -695,6 +885,16 @@ module ActiveSupport
|
|
695
885
|
end
|
696
886
|
end
|
697
887
|
|
888
|
+
def handle_invalid_expires_in(message)
|
889
|
+
error = ArgumentError.new(message)
|
890
|
+
if ActiveSupport::Cache::Store.raise_on_invalid_cache_expiration_time
|
891
|
+
raise error
|
892
|
+
else
|
893
|
+
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
894
|
+
logger.error("#{error.class}: #{error.message}") if logger
|
895
|
+
end
|
896
|
+
end
|
897
|
+
|
698
898
|
# Normalize aliased options to their canonical form
|
699
899
|
def normalize_options(options)
|
700
900
|
options = options.dup
|
@@ -707,10 +907,31 @@ module ActiveSupport
|
|
707
907
|
options
|
708
908
|
end
|
709
909
|
|
710
|
-
|
711
|
-
|
910
|
+
def validate_options(options)
|
911
|
+
if options.key?(:coder) && options[:serializer]
|
912
|
+
raise ArgumentError, "Cannot specify :serializer and :coder options together"
|
913
|
+
end
|
914
|
+
|
915
|
+
if options.key?(:coder) && options[:compressor]
|
916
|
+
raise ArgumentError, "Cannot specify :compressor and :coder options together"
|
917
|
+
end
|
918
|
+
|
919
|
+
if Cache.format_version < 7.1 && !options[:serializer] && options[:compressor]
|
920
|
+
raise ArgumentError, "Cannot specify :compressor option when using" \
|
921
|
+
" default serializer and cache format version is < 7.1"
|
922
|
+
end
|
923
|
+
|
924
|
+
options
|
925
|
+
end
|
926
|
+
|
927
|
+
# Expands and namespaces the cache key.
|
928
|
+
# Raises an exception when the key is +nil+ or an empty string.
|
929
|
+
# May be overridden by cache stores to do additional normalization.
|
712
930
|
def normalize_key(key, options = nil)
|
713
|
-
|
931
|
+
str_key = expanded_key(key)
|
932
|
+
raise(ArgumentError, "key cannot be blank") if !str_key || str_key.empty?
|
933
|
+
|
934
|
+
namespace_key str_key, options
|
714
935
|
end
|
715
936
|
|
716
937
|
# Prefix the key with a namespace string:
|
@@ -773,14 +994,33 @@ module ActiveSupport
|
|
773
994
|
end
|
774
995
|
end
|
775
996
|
|
776
|
-
def instrument(operation, key, options = nil)
|
997
|
+
def instrument(operation, key, options = nil, &block)
|
998
|
+
_instrument(operation, key: key, options: options, &block)
|
999
|
+
end
|
1000
|
+
|
1001
|
+
def instrument_multi(operation, keys, options = nil, &block)
|
1002
|
+
_instrument(operation, multi: true, key: keys, options: options, &block)
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
def _instrument(operation, multi: false, options: nil, **payload, &block)
|
777
1006
|
if logger && logger.debug? && !silence?
|
778
|
-
|
1007
|
+
debug_key =
|
1008
|
+
if multi
|
1009
|
+
": #{payload[:key].size} key(s) specified"
|
1010
|
+
elsif payload[:key]
|
1011
|
+
": #{payload[:key]}"
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
debug_options = " (#{options.inspect})" unless options.blank?
|
1015
|
+
|
1016
|
+
logger.debug "Cache #{operation}#{debug_key}#{debug_options}"
|
779
1017
|
end
|
780
1018
|
|
781
|
-
payload =
|
1019
|
+
payload[:store] = self.class.name
|
782
1020
|
payload.merge!(options) if options.is_a?(Hash)
|
783
|
-
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload)
|
1021
|
+
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) do
|
1022
|
+
block&.call(payload)
|
1023
|
+
end
|
784
1024
|
end
|
785
1025
|
|
786
1026
|
def handle_expired_entry(entry, key, options)
|
@@ -790,7 +1030,8 @@ module ActiveSupport
|
|
790
1030
|
# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
|
791
1031
|
# for a brief period while the entry is being recalculated.
|
792
1032
|
entry.expires_at = Time.now.to_f + race_ttl
|
793
|
-
|
1033
|
+
options[:expires_in] = race_ttl * 2
|
1034
|
+
write_entry(key, entry, **options)
|
794
1035
|
else
|
795
1036
|
delete_entry(key, **options)
|
796
1037
|
end
|
@@ -800,13 +1041,15 @@ module ActiveSupport
|
|
800
1041
|
end
|
801
1042
|
|
802
1043
|
def get_entry_value(entry, name, options)
|
803
|
-
instrument(:fetch_hit, name, options)
|
1044
|
+
instrument(:fetch_hit, name, options)
|
804
1045
|
entry.value
|
805
1046
|
end
|
806
1047
|
|
807
|
-
def save_block_result_to_cache(name, options)
|
808
|
-
|
809
|
-
|
1048
|
+
def save_block_result_to_cache(name, key, options)
|
1049
|
+
options = options.dup
|
1050
|
+
|
1051
|
+
result = instrument(:generate, key, options) do
|
1052
|
+
yield(name, WriteOptions.new(options))
|
810
1053
|
end
|
811
1054
|
|
812
1055
|
write(name, result, options) unless result.nil? && options[:skip_nil]
|
@@ -814,217 +1057,46 @@ module ActiveSupport
|
|
814
1057
|
end
|
815
1058
|
end
|
816
1059
|
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
def dump_compressed(entry, threshold)
|
825
|
-
entry.compressed(threshold)
|
826
|
-
end
|
827
|
-
|
828
|
-
def load(payload)
|
829
|
-
payload
|
830
|
-
end
|
831
|
-
end
|
832
|
-
|
833
|
-
module Coders # :nodoc:
|
834
|
-
MARK_61 = "\x04\b".b.freeze # The one set by Marshal.
|
835
|
-
MARK_70_UNCOMPRESSED = "\x00".b.freeze
|
836
|
-
MARK_70_COMPRESSED = "\x01".b.freeze
|
837
|
-
|
838
|
-
class << self
|
839
|
-
def [](version)
|
840
|
-
case version
|
841
|
-
when 6.1
|
842
|
-
Rails61Coder
|
843
|
-
when 7.0
|
844
|
-
Rails70Coder
|
845
|
-
else
|
846
|
-
raise ArgumentError, "Unknown ActiveSupport::Cache.format_version: #{Cache.format_version.inspect}"
|
847
|
-
end
|
848
|
-
end
|
849
|
-
end
|
850
|
-
|
851
|
-
module Loader
|
852
|
-
extend self
|
853
|
-
|
854
|
-
def load(payload)
|
855
|
-
if !payload.is_a?(String)
|
856
|
-
ActiveSupport::Cache::Store.logger&.warn %{Payload wasn't a string, was #{payload.class.name} - couldn't unmarshal, so returning nil."}
|
857
|
-
|
858
|
-
return nil
|
859
|
-
elsif payload.start_with?(MARK_70_UNCOMPRESSED)
|
860
|
-
members = Marshal.load(payload.byteslice(1..-1))
|
861
|
-
elsif payload.start_with?(MARK_70_COMPRESSED)
|
862
|
-
members = Marshal.load(Zlib::Inflate.inflate(payload.byteslice(1..-1)))
|
863
|
-
elsif payload.start_with?(MARK_61)
|
864
|
-
return Marshal.load(payload)
|
865
|
-
else
|
866
|
-
ActiveSupport::Cache::Store.logger&.warn %{Invalid cache prefix: #{payload.byteslice(0).inspect}, expected "\\x00" or "\\x01"}
|
867
|
-
|
868
|
-
return nil
|
869
|
-
end
|
870
|
-
Entry.unpack(members)
|
871
|
-
end
|
872
|
-
end
|
873
|
-
|
874
|
-
module Rails61Coder
|
875
|
-
include Loader
|
876
|
-
extend self
|
877
|
-
|
878
|
-
def dump(entry)
|
879
|
-
Marshal.dump(entry)
|
880
|
-
end
|
881
|
-
|
882
|
-
def dump_compressed(entry, threshold)
|
883
|
-
Marshal.dump(entry.compressed(threshold))
|
884
|
-
end
|
885
|
-
end
|
886
|
-
|
887
|
-
module Rails70Coder
|
888
|
-
include Loader
|
889
|
-
extend self
|
890
|
-
|
891
|
-
def dump(entry)
|
892
|
-
MARK_70_UNCOMPRESSED + Marshal.dump(entry.pack)
|
893
|
-
end
|
894
|
-
|
895
|
-
def dump_compressed(entry, threshold)
|
896
|
-
payload = Marshal.dump(entry.pack)
|
897
|
-
if payload.bytesize >= threshold
|
898
|
-
compressed_payload = Zlib::Deflate.deflate(payload)
|
899
|
-
if compressed_payload.bytesize < payload.bytesize
|
900
|
-
return MARK_70_COMPRESSED + compressed_payload
|
901
|
-
end
|
902
|
-
end
|
903
|
-
|
904
|
-
MARK_70_UNCOMPRESSED + payload
|
905
|
-
end
|
906
|
-
end
|
907
|
-
end
|
908
|
-
|
909
|
-
# This class is used to represent cache entries. Cache entries have a value, an optional
|
910
|
-
# expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
|
911
|
-
# on the cache. The version is used to support the :version option on the cache for rejecting
|
912
|
-
# mismatches.
|
913
|
-
#
|
914
|
-
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
|
915
|
-
# using short instance variable names that are lazily defined.
|
916
|
-
class Entry # :nodoc:
|
917
|
-
class << self
|
918
|
-
def unpack(members)
|
919
|
-
new(members[0], expires_at: members[1], version: members[2])
|
920
|
-
end
|
1060
|
+
# Enables the dynamic configuration of Cache entry options while ensuring
|
1061
|
+
# that conflicting options are not both set. When a block is given to
|
1062
|
+
# ActiveSupport::Cache::Store#fetch, the second argument will be an
|
1063
|
+
# instance of +WriteOptions+.
|
1064
|
+
class WriteOptions
|
1065
|
+
def initialize(options) # :nodoc:
|
1066
|
+
@options = options
|
921
1067
|
end
|
922
1068
|
|
923
|
-
|
924
|
-
|
925
|
-
# Creates a new cache entry for the specified value. Options supported are
|
926
|
-
# +:compressed+, +:version+, +:expires_at+ and +:expires_in+.
|
927
|
-
def initialize(value, compressed: false, version: nil, expires_in: nil, expires_at: nil, **)
|
928
|
-
@value = value
|
929
|
-
@version = version
|
930
|
-
@created_at = 0.0
|
931
|
-
@expires_in = expires_at&.to_f || expires_in && (expires_in.to_f + Time.now.to_f)
|
932
|
-
@compressed = true if compressed
|
1069
|
+
def version
|
1070
|
+
@options[:version]
|
933
1071
|
end
|
934
1072
|
|
935
|
-
def
|
936
|
-
|
1073
|
+
def version=(version)
|
1074
|
+
@options[:version] = version
|
937
1075
|
end
|
938
1076
|
|
939
|
-
def
|
940
|
-
@
|
1077
|
+
def expires_in
|
1078
|
+
@options[:expires_in]
|
941
1079
|
end
|
942
1080
|
|
943
|
-
#
|
944
|
-
#
|
945
|
-
|
946
|
-
|
1081
|
+
# Sets the Cache entry's +expires_in+ value. If an +expires_at+ option was
|
1082
|
+
# previously set, this will unset it since +expires_in+ and +expires_at+
|
1083
|
+
# cannot both be set.
|
1084
|
+
def expires_in=(expires_in)
|
1085
|
+
@options.delete(:expires_at)
|
1086
|
+
@options[:expires_in] = expires_in
|
947
1087
|
end
|
948
1088
|
|
949
1089
|
def expires_at
|
950
|
-
@
|
951
|
-
end
|
952
|
-
|
953
|
-
def expires_at=(value)
|
954
|
-
if value
|
955
|
-
@expires_in = value.to_f - @created_at
|
956
|
-
else
|
957
|
-
@expires_in = nil
|
958
|
-
end
|
959
|
-
end
|
960
|
-
|
961
|
-
# Returns the size of the cached value. This could be less than
|
962
|
-
# <tt>value.bytesize</tt> if the data is compressed.
|
963
|
-
def bytesize
|
964
|
-
case value
|
965
|
-
when NilClass
|
966
|
-
0
|
967
|
-
when String
|
968
|
-
@value.bytesize
|
969
|
-
else
|
970
|
-
@s ||= Marshal.dump(@value).bytesize
|
971
|
-
end
|
1090
|
+
@options[:expires_at]
|
972
1091
|
end
|
973
1092
|
|
974
|
-
|
975
|
-
|
1093
|
+
# Sets the Cache entry's +expires_at+ value. If an +expires_in+ option was
|
1094
|
+
# previously set, this will unset it since +expires_at+ and +expires_in+
|
1095
|
+
# cannot both be set.
|
1096
|
+
def expires_at=(expires_at)
|
1097
|
+
@options.delete(:expires_in)
|
1098
|
+
@options[:expires_at] = expires_at
|
976
1099
|
end
|
977
|
-
|
978
|
-
def compressed(compress_threshold)
|
979
|
-
return self if compressed?
|
980
|
-
|
981
|
-
case @value
|
982
|
-
when nil, true, false, Numeric
|
983
|
-
uncompressed_size = 0
|
984
|
-
when String
|
985
|
-
uncompressed_size = @value.bytesize
|
986
|
-
else
|
987
|
-
serialized = Marshal.dump(@value)
|
988
|
-
uncompressed_size = serialized.bytesize
|
989
|
-
end
|
990
|
-
|
991
|
-
if uncompressed_size >= compress_threshold
|
992
|
-
serialized ||= Marshal.dump(@value)
|
993
|
-
compressed = Zlib::Deflate.deflate(serialized)
|
994
|
-
|
995
|
-
if compressed.bytesize < uncompressed_size
|
996
|
-
return Entry.new(compressed, compressed: true, expires_at: expires_at, version: version)
|
997
|
-
end
|
998
|
-
end
|
999
|
-
self
|
1000
|
-
end
|
1001
|
-
|
1002
|
-
def local?
|
1003
|
-
false
|
1004
|
-
end
|
1005
|
-
|
1006
|
-
# Duplicates the value in a class. This is used by cache implementations that don't natively
|
1007
|
-
# serialize entries to protect against accidental cache modifications.
|
1008
|
-
def dup_value!
|
1009
|
-
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
|
1010
|
-
if @value.is_a?(String)
|
1011
|
-
@value = @value.dup
|
1012
|
-
else
|
1013
|
-
@value = Marshal.load(Marshal.dump(@value))
|
1014
|
-
end
|
1015
|
-
end
|
1016
|
-
end
|
1017
|
-
|
1018
|
-
def pack
|
1019
|
-
members = [value, expires_at, version]
|
1020
|
-
members.pop while !members.empty? && members.last.nil?
|
1021
|
-
members
|
1022
|
-
end
|
1023
|
-
|
1024
|
-
private
|
1025
|
-
def uncompress(value)
|
1026
|
-
Marshal.load(Zlib::Inflate.inflate(value))
|
1027
|
-
end
|
1028
1100
|
end
|
1029
1101
|
end
|
1030
1102
|
end
|