activesupport 6.0.4.4 → 7.0.4.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.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +257 -532
- data/MIT-LICENSE +1 -1
- data/lib/active_support/actionable_error.rb +1 -1
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +5 -5
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/cache/file_store.rb +16 -10
- data/lib/active_support/cache/mem_cache_store.rb +163 -42
- data/lib/active_support/cache/memory_store.rb +57 -29
- data/lib/active_support/cache/null_store.rb +10 -2
- data/lib/active_support/cache/redis_cache_store.rb +79 -98
- data/lib/active_support/cache/strategy/local_cache.rb +49 -57
- data/lib/active_support/cache.rb +378 -179
- data/lib/active_support/callbacks.rb +230 -122
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +49 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +9 -6
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +13 -12
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/array.rb +1 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +9 -22
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +9 -9
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date.rb +1 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +17 -4
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +39 -13
- data/lib/active_support/core_ext/enumerable.rb +164 -23
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +2 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +2 -2
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +40 -36
- data/lib/active_support/core_ext/module/introspection.rb +1 -25
- data/lib/active_support/core_ext/name_error.rb +23 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +80 -73
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric.rb +1 -0
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +11 -0
- data/lib/active_support/core_ext/object/json.rb +42 -26
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/object/with_options.rb +20 -1
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +6 -25
- data/lib/active_support/core_ext/range/conversions.rb +8 -8
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +1 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +8 -1
- data/lib/active_support/core_ext/securerandom.rb +1 -1
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +3 -2
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +39 -5
- data/lib/active_support/core_ext/string/inquiry.rb +2 -1
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +92 -41
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +25 -7
- data/lib/active_support/core_ext/time/conversions.rb +15 -12
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +7 -22
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -23
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +39 -16
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +58 -769
- data/lib/active_support/deprecation/behaviors.rb +23 -7
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +6 -5
- data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +7 -2
- data/lib/active_support/descendants_tracker.rb +174 -64
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +24 -10
- data/lib/active_support/duration.rb +134 -55
- data/lib/active_support/encrypted_configuration.rb +13 -2
- data/lib/active_support/encrypted_file.rb +32 -3
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +72 -138
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +43 -21
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +51 -25
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +14 -19
- data/lib/active_support/inflector/inflections.rb +24 -9
- data/lib/active_support/inflector/methods.rb +29 -49
- data/lib/active_support/inflector/transliterate.rb +5 -5
- data/lib/active_support/isolated_execution_state.rb +72 -0
- data/lib/active_support/json/decoding.rb +4 -4
- data/lib/active_support/json/encoding.rb +8 -4
- data/lib/active_support/key_generator.rb +23 -6
- data/lib/active_support/lazy_load_hooks.rb +28 -4
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +23 -5
- data/lib/active_support/logger.rb +1 -1
- data/lib/active_support/logger_silence.rb +2 -26
- data/lib/active_support/logger_thread_safe_level.rb +34 -21
- data/lib/active_support/message_encryptor.rb +16 -13
- data/lib/active_support/message_verifier.rb +50 -18
- data/lib/active_support/messages/metadata.rb +2 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +6 -5
- data/lib/active_support/multibyte/chars.rb +13 -52
- data/lib/active_support/multibyte/unicode.rb +1 -87
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +110 -69
- data/lib/active_support/notifications/instrumenter.rb +37 -29
- data/lib/active_support/notifications.rb +55 -28
- data/lib/active_support/number_helper/number_converter.rb +2 -4
- data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
- data/lib/active_support/number_helper/rounding_helper.rb +12 -32
- data/lib/active_support/number_helper.rb +29 -16
- data/lib/active_support/option_merger.rb +11 -18
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +9 -3
- data/lib/active_support/parameter_filter.rb +21 -11
- data/lib/active_support/per_thread_registry.rb +6 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +77 -5
- data/lib/active_support/reloader.rb +1 -1
- data/lib/active_support/rescuable.rb +16 -16
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +2 -2
- data/lib/active_support/subscriber.rb +19 -25
- data/lib/active_support/tagged_logging.rb +31 -6
- data/lib/active_support/test_case.rb +13 -21
- data/lib/active_support/testing/assertions.rb +50 -13
- data/lib/active_support/testing/deprecation.rb +52 -1
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +5 -5
- data/lib/active_support/testing/parallelization/server.rb +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +16 -95
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +3 -5
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +53 -5
- data/lib/active_support/time_with_zone.rb +126 -62
- data/lib/active_support/values/time_zone.rb +54 -23
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +4 -4
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +9 -2
- data/lib/active_support/xml_mini.rb +5 -4
- data/lib/active_support.rb +29 -1
- metadata +46 -45
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
- data/lib/active_support/core_ext/hash/compact.rb +0 -5
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
- data/lib/active_support/core_ext/marshal.rb +0 -24
- data/lib/active_support/core_ext/module/reachable.rb +0 -6
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
- data/lib/active_support/core_ext/range/include_range.rb +0 -9
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
data/lib/active_support/cache.rb
CHANGED
@@ -3,10 +3,12 @@
|
|
3
3
|
require "zlib"
|
4
4
|
require "active_support/core_ext/array/extract_options"
|
5
5
|
require "active_support/core_ext/array/wrap"
|
6
|
+
require "active_support/core_ext/enumerable"
|
6
7
|
require "active_support/core_ext/module/attribute_accessors"
|
7
8
|
require "active_support/core_ext/numeric/bytes"
|
8
9
|
require "active_support/core_ext/numeric/time"
|
9
10
|
require "active_support/core_ext/object/to_param"
|
11
|
+
require "active_support/core_ext/object/try"
|
10
12
|
require "active_support/core_ext/string/inflections"
|
11
13
|
|
12
14
|
module ActiveSupport
|
@@ -20,13 +22,24 @@ module ActiveSupport
|
|
20
22
|
|
21
23
|
# These options mean something to all cache implementations. Individual cache
|
22
24
|
# implementations may support additional options.
|
23
|
-
UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
|
25
|
+
UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :expire_in, :expired_in, :race_condition_ttl, :coder, :skip_nil]
|
26
|
+
|
27
|
+
DEFAULT_COMPRESS_LIMIT = 1.kilobyte
|
28
|
+
|
29
|
+
# Mapping of canonical option names to aliases that a store will recognize.
|
30
|
+
OPTION_ALIASES = {
|
31
|
+
expires_in: [:expire_in, :expired_in]
|
32
|
+
}.freeze
|
24
33
|
|
25
34
|
module Strategy
|
26
35
|
autoload :LocalCache, "active_support/cache/strategy/local_cache"
|
27
36
|
end
|
28
37
|
|
38
|
+
@format_version = 6.1
|
39
|
+
|
29
40
|
class << self
|
41
|
+
attr_accessor :format_version
|
42
|
+
|
30
43
|
# Creates a new Store object according to the given options.
|
31
44
|
#
|
32
45
|
# If no arguments are passed to this method, then a new
|
@@ -56,7 +69,13 @@ module ActiveSupport
|
|
56
69
|
case store
|
57
70
|
when Symbol
|
58
71
|
options = parameters.extract_options!
|
59
|
-
|
72
|
+
# clean this up once Ruby 2.7 support is dropped
|
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
|
60
79
|
when Array
|
61
80
|
lookup_store(*store)
|
62
81
|
when nil
|
@@ -79,7 +98,7 @@ module ActiveSupport
|
|
79
98
|
#
|
80
99
|
# The +key+ argument can also respond to +cache_key+ or +to_param+.
|
81
100
|
def expand_cache_key(key, namespace = nil)
|
82
|
-
expanded_cache_key =
|
101
|
+
expanded_cache_key = namespace ? +"#{namespace}/" : +""
|
83
102
|
|
84
103
|
if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
|
85
104
|
expanded_cache_key << "#{prefix}/"
|
@@ -120,9 +139,10 @@ module ActiveSupport
|
|
120
139
|
# popular cache store for large production websites.
|
121
140
|
#
|
122
141
|
# Some implementations may not support all methods beyond the basic cache
|
123
|
-
# methods of
|
142
|
+
# methods of #fetch, #write, #read, #exist?, and #delete.
|
124
143
|
#
|
125
|
-
# ActiveSupport::Cache::Store can store any
|
144
|
+
# ActiveSupport::Cache::Store can store any Ruby object that is supported by
|
145
|
+
# its +coder+'s +dump+ and +load+ methods.
|
126
146
|
#
|
127
147
|
# cache = ActiveSupport::Cache::MemoryStore.new
|
128
148
|
#
|
@@ -130,6 +150,8 @@ module ActiveSupport
|
|
130
150
|
# cache.write('city', "Duckburgh")
|
131
151
|
# cache.read('city') # => "Duckburgh"
|
132
152
|
#
|
153
|
+
# cache.write('not serializable', Proc.new {}) # => TypeError
|
154
|
+
#
|
133
155
|
# Keys are always translated into Strings and are case sensitive. When an
|
134
156
|
# object is specified as a key and has a +cache_key+ method defined, this
|
135
157
|
# method will be called to define the key. Otherwise, the +to_param+
|
@@ -150,11 +172,6 @@ module ActiveSupport
|
|
150
172
|
# cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
|
151
173
|
# @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
|
152
174
|
#
|
153
|
-
# Cached data larger than 1kB are compressed by default. To turn off
|
154
|
-
# compression, pass <tt>compress: false</tt> to the initializer or to
|
155
|
-
# individual +fetch+ or +write+ method calls. The 1kB compression
|
156
|
-
# threshold is configurable with the <tt>:compress_threshold</tt> option,
|
157
|
-
# specified in bytes.
|
158
175
|
class Store
|
159
176
|
cattr_accessor :logger, instance_writer: true
|
160
177
|
|
@@ -178,11 +195,26 @@ module ActiveSupport
|
|
178
195
|
end
|
179
196
|
end
|
180
197
|
|
181
|
-
# Creates a new cache.
|
182
|
-
#
|
183
|
-
#
|
198
|
+
# Creates a new cache.
|
199
|
+
#
|
200
|
+
# ==== Options
|
201
|
+
#
|
202
|
+
# * +:namespace+ - Sets the namespace for the cache. This option is
|
203
|
+
# especially useful if your application shares a cache with other
|
204
|
+
# applications.
|
205
|
+
# * +:coder+ - Replaces the default cache entry serialization mechanism
|
206
|
+
# with a custom one. The +coder+ must respond to +dump+ and +load+.
|
207
|
+
# Using a custom coder disables automatic compression.
|
208
|
+
#
|
209
|
+
# Any other specified options are treated as default options for the
|
210
|
+
# relevant cache operations, such as #read, #write, and #fetch.
|
184
211
|
def initialize(options = nil)
|
185
|
-
@options = options ? options
|
212
|
+
@options = options ? normalize_options(options) : {}
|
213
|
+
@options[:compress] = true unless @options.key?(:compress)
|
214
|
+
@options[:compress_threshold] = DEFAULT_COMPRESS_LIMIT unless @options.key?(:compress_threshold)
|
215
|
+
|
216
|
+
@coder = @options.delete(:coder) { default_coder } || NullCoder
|
217
|
+
@coder_supports_compression = @coder.respond_to?(:dump_compressed)
|
186
218
|
end
|
187
219
|
|
188
220
|
# Silences the logger.
|
@@ -217,101 +249,75 @@ module ActiveSupport
|
|
217
249
|
# end
|
218
250
|
# cache.fetch('city') # => "Duckburgh"
|
219
251
|
#
|
220
|
-
#
|
221
|
-
# Setting <tt>force: true</tt> forces a cache "miss," meaning we treat
|
222
|
-
# the cache value as missing even if it's present. Passing a block is
|
223
|
-
# required when +force+ is true so this always results in a cache write.
|
252
|
+
# ==== Options
|
224
253
|
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
# The +:force+ option is useful when you're calling some other method to
|
230
|
-
# ask whether you should force a cache write. Otherwise, it's clearer to
|
231
|
-
# just call <tt>Cache#write</tt>.
|
232
|
-
#
|
233
|
-
# Setting <tt>skip_nil: true</tt> will not cache nil result:
|
234
|
-
#
|
235
|
-
# cache.fetch('foo') { nil }
|
236
|
-
# cache.fetch('bar', skip_nil: true) { nil }
|
237
|
-
# cache.exist?('foo') # => true
|
238
|
-
# cache.exist?('bar') # => false
|
239
|
-
#
|
240
|
-
#
|
241
|
-
# Setting <tt>compress: false</tt> disables compression of the cache entry.
|
242
|
-
#
|
243
|
-
# Setting <tt>:expires_in</tt> will set an expiration time on the cache.
|
244
|
-
# All caches support auto-expiring content after a specified number of
|
245
|
-
# seconds. This value can be specified as an option to the constructor
|
246
|
-
# (in which case all entries will be affected), or it can be supplied to
|
247
|
-
# the +fetch+ or +write+ method to effect just one entry.
|
248
|
-
#
|
249
|
-
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
|
250
|
-
# cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
|
251
|
-
#
|
252
|
-
# Setting <tt>:version</tt> verifies the cache stored under <tt>name</tt>
|
253
|
-
# is of the same version. nil is returned on mismatches despite contents.
|
254
|
-
# This feature is used to support recyclable cache keys.
|
255
|
-
#
|
256
|
-
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where
|
257
|
-
# a cache entry is used very frequently and is under heavy load. If a
|
258
|
-
# cache expires and due to heavy load several different processes will try
|
259
|
-
# to read data natively and then they all will try to write to cache. To
|
260
|
-
# avoid that case the first process to find an expired cache entry will
|
261
|
-
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.
|
262
|
-
# Yes, this process is extending the time for a stale value by another few
|
263
|
-
# seconds. Because of extended life of the previous cache, other processes
|
264
|
-
# will continue to use slightly stale data for a just a bit longer. In the
|
265
|
-
# meantime that first process will go ahead and will write into cache the
|
266
|
-
# new value. After that all the processes will start getting the new value.
|
267
|
-
# The key is to keep <tt>:race_condition_ttl</tt> small.
|
268
|
-
#
|
269
|
-
# If the process regenerating the entry errors out, the entry will be
|
270
|
-
# regenerated after the specified number of seconds. Also note that the
|
271
|
-
# life of stale cache is extended only if it expired recently. Otherwise
|
272
|
-
# a new value is generated and <tt>:race_condition_ttl</tt> does not play
|
273
|
-
# any role.
|
274
|
-
#
|
275
|
-
# # Set all values to expire after one minute.
|
276
|
-
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
|
277
|
-
#
|
278
|
-
# cache.write('foo', 'original value')
|
279
|
-
# val_1 = nil
|
280
|
-
# val_2 = nil
|
281
|
-
# sleep 60
|
282
|
-
#
|
283
|
-
# Thread.new do
|
284
|
-
# val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
|
285
|
-
# sleep 1
|
286
|
-
# 'new value 1'
|
287
|
-
# end
|
288
|
-
# end
|
254
|
+
# Internally, +fetch+ calls #read_entry, and calls #write_entry on a cache
|
255
|
+
# miss. Thus, +fetch+ supports the same options as #read and #write.
|
256
|
+
# Additionally, +fetch+ supports the following options:
|
289
257
|
#
|
290
|
-
#
|
291
|
-
#
|
292
|
-
#
|
293
|
-
# end
|
294
|
-
# end
|
258
|
+
# * <tt>force: true</tt> - Forces a cache "miss," meaning we treat the
|
259
|
+
# cache value as missing even if it's present. Passing a block is
|
260
|
+
# required when +force+ is true so this always results in a cache write.
|
295
261
|
#
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
299
|
-
# val_1 # => "new value 1"
|
300
|
-
# val_2 # => "original value"
|
262
|
+
# cache.write('today', 'Monday')
|
263
|
+
# cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday'
|
264
|
+
# cache.fetch('today', force: true) # => ArgumentError
|
301
265
|
#
|
302
|
-
#
|
303
|
-
#
|
304
|
-
#
|
266
|
+
# The +:force+ option is useful when you're calling some other method to
|
267
|
+
# ask whether you should force a cache write. Otherwise, it's clearer to
|
268
|
+
# just call +write+.
|
305
269
|
#
|
306
|
-
#
|
307
|
-
#
|
308
|
-
#
|
270
|
+
# * <tt>skip_nil: true</tt> - Prevents caching a nil result:
|
271
|
+
#
|
272
|
+
# cache.fetch('foo') { nil }
|
273
|
+
# cache.fetch('bar', skip_nil: true) { nil }
|
274
|
+
# cache.exist?('foo') # => true
|
275
|
+
# cache.exist?('bar') # => false
|
276
|
+
#
|
277
|
+
# * +:race_condition_ttl+ - Specifies the number of seconds during which
|
278
|
+
# an expired value can be reused while a new value is being generated.
|
279
|
+
# This can be used to prevent race conditions when cache entries expire,
|
280
|
+
# by preventing multiple processes from simultaneously regenerating the
|
281
|
+
# same entry (also known as the dog pile effect).
|
282
|
+
#
|
283
|
+
# When a process encounters a cache entry that has expired less than
|
284
|
+
# +:race_condition_ttl+ seconds ago, it will bump the expiration time by
|
285
|
+
# +:race_condition_ttl+ seconds before generating a new value. During
|
286
|
+
# this extended time window, while the process generates a new value,
|
287
|
+
# other processes will continue to use the old value. After the first
|
288
|
+
# process writes the new value, other processes will then use it.
|
289
|
+
#
|
290
|
+
# If the first process errors out while generating a new value, another
|
291
|
+
# process can try to generate a new value after the extended time window
|
292
|
+
# has elapsed.
|
293
|
+
#
|
294
|
+
# # Set all values to expire after one minute.
|
295
|
+
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
|
296
|
+
#
|
297
|
+
# cache.write('foo', 'original value')
|
298
|
+
# val_1 = nil
|
299
|
+
# val_2 = nil
|
300
|
+
# sleep 60
|
301
|
+
#
|
302
|
+
# Thread.new do
|
303
|
+
# val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
|
304
|
+
# sleep 1
|
305
|
+
# 'new value 1'
|
306
|
+
# end
|
307
|
+
# end
|
308
|
+
#
|
309
|
+
# Thread.new do
|
310
|
+
# val_2 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
|
311
|
+
# 'new value 2'
|
312
|
+
# end
|
313
|
+
# end
|
314
|
+
#
|
315
|
+
# cache.fetch('foo') # => "original value"
|
316
|
+
# sleep 10 # First thread extended the life of cache by another 10 seconds
|
317
|
+
# cache.fetch('foo') # => "new value 1"
|
318
|
+
# val_1 # => "new value 1"
|
319
|
+
# val_2 # => "original value"
|
309
320
|
#
|
310
|
-
# cache = ActiveSupport::Cache::MemCacheStore.new
|
311
|
-
# cache.fetch("foo", force: true, raw: true) do
|
312
|
-
# :bar
|
313
|
-
# end
|
314
|
-
# cache.fetch('foo') # => "bar"
|
315
321
|
def fetch(name, options = nil, &block)
|
316
322
|
if block_given?
|
317
323
|
options = merged_options(options)
|
@@ -319,7 +325,7 @@ module ActiveSupport
|
|
319
325
|
|
320
326
|
entry = nil
|
321
327
|
instrument(:read, name, options) do |payload|
|
322
|
-
cached_entry = read_entry(key, **options) unless options[:force]
|
328
|
+
cached_entry = read_entry(key, **options, event: payload) unless options[:force]
|
323
329
|
entry = handle_expired_entry(cached_entry, key, options)
|
324
330
|
entry = nil if entry && entry.mismatched?(normalize_version(name, options))
|
325
331
|
payload[:super_operation] = :fetch if payload
|
@@ -346,14 +352,20 @@ module ActiveSupport
|
|
346
352
|
# <tt>:version</tt> options, both of these conditions are applied before
|
347
353
|
# the data is returned.
|
348
354
|
#
|
349
|
-
# Options
|
355
|
+
# ==== Options
|
356
|
+
#
|
357
|
+
# * +:version+ - Specifies a version for the cache entry. If the cached
|
358
|
+
# version does not match the requested version, the read will be treated
|
359
|
+
# as a cache miss. This feature is used to support recyclable cache keys.
|
360
|
+
#
|
361
|
+
# Other options will be handled by the specific cache store implementation.
|
350
362
|
def read(name, options = nil)
|
351
363
|
options = merged_options(options)
|
352
364
|
key = normalize_key(name, options)
|
353
365
|
version = normalize_version(name, options)
|
354
366
|
|
355
367
|
instrument(:read, name, options) do |payload|
|
356
|
-
entry = read_entry(key, **options)
|
368
|
+
entry = read_entry(key, **options, event: payload)
|
357
369
|
|
358
370
|
if entry
|
359
371
|
if entry.expired?
|
@@ -385,7 +397,7 @@ module ActiveSupport
|
|
385
397
|
options = merged_options(options)
|
386
398
|
|
387
399
|
instrument :read_multi, names, options do |payload|
|
388
|
-
read_multi_entries(names, **options).tap do |results|
|
400
|
+
read_multi_entries(names, **options, event: payload).tap do |results|
|
389
401
|
payload[:hits] = results.keys
|
390
402
|
end
|
391
403
|
end
|
@@ -441,8 +453,8 @@ module ActiveSupport
|
|
441
453
|
instrument :read_multi, names, options do |payload|
|
442
454
|
reads = read_multi_entries(names, **options)
|
443
455
|
writes = {}
|
444
|
-
ordered = names.
|
445
|
-
|
456
|
+
ordered = names.index_with do |name|
|
457
|
+
reads.fetch(name) { writes[name] = yield(name) }
|
446
458
|
end
|
447
459
|
|
448
460
|
payload[:hits] = reads.keys
|
@@ -454,9 +466,39 @@ module ActiveSupport
|
|
454
466
|
end
|
455
467
|
end
|
456
468
|
|
457
|
-
# Writes the value to the cache
|
469
|
+
# Writes the value to the cache with the key. The value must be supported
|
470
|
+
# by the +coder+'s +dump+ and +load+ methods.
|
458
471
|
#
|
459
|
-
#
|
472
|
+
# By default, cache entries larger than 1kB are compressed. Compression
|
473
|
+
# allows more data to be stored in the same memory footprint, leading to
|
474
|
+
# fewer cache evictions and higher hit rates.
|
475
|
+
#
|
476
|
+
# ==== Options
|
477
|
+
#
|
478
|
+
# * <tt>compress: false</tt> - Disables compression of the cache entry.
|
479
|
+
#
|
480
|
+
# * +:compress_threshold+ - The compression threshold, specified in bytes.
|
481
|
+
# \Cache entries larger than this threshold will be compressed. Defaults
|
482
|
+
# to +1.kilobyte+.
|
483
|
+
#
|
484
|
+
# * +:expires_in+ - Sets a relative expiration time for the cache entry,
|
485
|
+
# specified in seconds. +:expire_in+ and +:expired_in+ are aliases for
|
486
|
+
# +:expires_in+.
|
487
|
+
#
|
488
|
+
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
|
489
|
+
# cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
|
490
|
+
#
|
491
|
+
# * +:expires_at+ - Sets an absolute expiration time for the cache entry.
|
492
|
+
#
|
493
|
+
# cache = ActiveSupport::Cache::MemoryStore.new
|
494
|
+
# cache.write(key, value, expires_at: Time.now.at_end_of_hour)
|
495
|
+
#
|
496
|
+
# * +:version+ - Specifies a version for the cache entry. When reading
|
497
|
+
# from the cache, if the cached version does not match the requested
|
498
|
+
# version, the read will be treated as a cache miss. This feature is
|
499
|
+
# used to support recyclable cache keys.
|
500
|
+
#
|
501
|
+
# Other options will be handled by the specific cache store implementation.
|
460
502
|
def write(name, value, options = nil)
|
461
503
|
options = merged_options(options)
|
462
504
|
|
@@ -477,18 +519,34 @@ module ActiveSupport
|
|
477
519
|
end
|
478
520
|
end
|
479
521
|
|
522
|
+
# Deletes multiple entries in the cache.
|
523
|
+
#
|
524
|
+
# Options are passed to the underlying cache implementation.
|
525
|
+
def delete_multi(names, options = nil)
|
526
|
+
options = merged_options(options)
|
527
|
+
names.map! { |key| normalize_key(key, options) }
|
528
|
+
|
529
|
+
instrument :delete_multi, names do
|
530
|
+
delete_multi_entries(names, **options)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
480
534
|
# Returns +true+ if the cache contains an entry for the given key.
|
481
535
|
#
|
482
536
|
# Options are passed to the underlying cache implementation.
|
483
537
|
def exist?(name, options = nil)
|
484
538
|
options = merged_options(options)
|
485
539
|
|
486
|
-
instrument(:exist?, name) do
|
487
|
-
entry = read_entry(normalize_key(name, options), **options)
|
540
|
+
instrument(:exist?, name) do |payload|
|
541
|
+
entry = read_entry(normalize_key(name, options), **options, event: payload)
|
488
542
|
(entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
|
489
543
|
end
|
490
544
|
end
|
491
545
|
|
546
|
+
def new_entry(value, options = nil) # :nodoc:
|
547
|
+
Entry.new(value, **merged_options(options))
|
548
|
+
end
|
549
|
+
|
492
550
|
# Deletes all entries with keys matching the pattern.
|
493
551
|
#
|
494
552
|
# Options are passed to the underlying cache implementation.
|
@@ -516,7 +574,7 @@ module ActiveSupport
|
|
516
574
|
raise NotImplementedError.new("#{self.class.name} does not support decrement")
|
517
575
|
end
|
518
576
|
|
519
|
-
#
|
577
|
+
# Cleans up the cache by removing expired entries.
|
520
578
|
#
|
521
579
|
# Options are passed to the underlying cache implementation.
|
522
580
|
#
|
@@ -536,6 +594,10 @@ module ActiveSupport
|
|
536
594
|
end
|
537
595
|
|
538
596
|
private
|
597
|
+
def default_coder
|
598
|
+
Coders[Cache.format_version]
|
599
|
+
end
|
600
|
+
|
539
601
|
# Adds the namespace defined in the options to a pattern designed to
|
540
602
|
# match keys. Implementations that support delete_matched should call
|
541
603
|
# this method to translate a pattern that matches names into one that
|
@@ -567,26 +629,36 @@ module ActiveSupport
|
|
567
629
|
raise NotImplementedError.new
|
568
630
|
end
|
569
631
|
|
632
|
+
def serialize_entry(entry, **options)
|
633
|
+
options = merged_options(options)
|
634
|
+
if @coder_supports_compression && options[:compress]
|
635
|
+
@coder.dump_compressed(entry, options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT)
|
636
|
+
else
|
637
|
+
@coder.dump(entry)
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
def deserialize_entry(payload)
|
642
|
+
payload.nil? ? nil : @coder.load(payload)
|
643
|
+
end
|
644
|
+
|
570
645
|
# Reads multiple entries from the cache implementation. Subclasses MAY
|
571
646
|
# implement this method.
|
572
647
|
def read_multi_entries(names, **options)
|
573
|
-
|
574
|
-
|
575
|
-
|
648
|
+
names.each_with_object({}) do |name, results|
|
649
|
+
key = normalize_key(name, options)
|
650
|
+
entry = read_entry(key, **options)
|
651
|
+
|
652
|
+
next unless entry
|
653
|
+
|
576
654
|
version = normalize_version(name, options)
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
elsif entry.mismatched?(version)
|
583
|
-
# Skip mismatched versions
|
584
|
-
else
|
585
|
-
results[name] = entry.value
|
586
|
-
end
|
655
|
+
|
656
|
+
if entry.expired?
|
657
|
+
delete_entry(key, **options)
|
658
|
+
elsif !entry.mismatched?(version)
|
659
|
+
results[name] = entry.value
|
587
660
|
end
|
588
661
|
end
|
589
|
-
results
|
590
662
|
end
|
591
663
|
|
592
664
|
# Writes multiple entries to the cache implementation. Subclasses MAY
|
@@ -603,9 +675,16 @@ module ActiveSupport
|
|
603
675
|
raise NotImplementedError.new
|
604
676
|
end
|
605
677
|
|
678
|
+
# Deletes multiples entries in the cache implementation. Subclasses MAY
|
679
|
+
# implement this method.
|
680
|
+
def delete_multi_entries(entries, **options)
|
681
|
+
entries.count { |key| delete_entry(key, **options) }
|
682
|
+
end
|
683
|
+
|
606
684
|
# Merges the default options with ones specific to a method call.
|
607
685
|
def merged_options(call_options)
|
608
686
|
if call_options
|
687
|
+
call_options = normalize_options(call_options)
|
609
688
|
if options.empty?
|
610
689
|
call_options
|
611
690
|
else
|
@@ -616,6 +695,18 @@ module ActiveSupport
|
|
616
695
|
end
|
617
696
|
end
|
618
697
|
|
698
|
+
# Normalize aliased options to their canonical form
|
699
|
+
def normalize_options(options)
|
700
|
+
options = options.dup
|
701
|
+
OPTION_ALIASES.each do |canonical_name, aliases|
|
702
|
+
alias_key = aliases.detect { |key| options.key?(key) }
|
703
|
+
options[canonical_name] ||= options[alias_key] if alias_key
|
704
|
+
options.except!(*aliases)
|
705
|
+
end
|
706
|
+
|
707
|
+
options
|
708
|
+
end
|
709
|
+
|
619
710
|
# Expands and namespaces the cache key. May be overridden by
|
620
711
|
# cache stores to do additional normalization.
|
621
712
|
def normalize_key(key, options = nil)
|
@@ -639,6 +730,10 @@ module ActiveSupport
|
|
639
730
|
namespace = namespace.call
|
640
731
|
end
|
641
732
|
|
733
|
+
if key && key.encoding != Encoding::UTF_8
|
734
|
+
key = key.dup.force_encoding(Encoding::UTF_8)
|
735
|
+
end
|
736
|
+
|
642
737
|
if namespace
|
643
738
|
"#{namespace}:#{key}"
|
644
739
|
else
|
@@ -655,15 +750,15 @@ module ActiveSupport
|
|
655
750
|
case key
|
656
751
|
when Array
|
657
752
|
if key.size > 1
|
658
|
-
key
|
753
|
+
key.collect { |element| expanded_key(element) }
|
659
754
|
else
|
660
|
-
|
755
|
+
expanded_key(key.first)
|
661
756
|
end
|
662
757
|
when Hash
|
663
|
-
key
|
664
|
-
|
665
|
-
|
666
|
-
|
758
|
+
key.collect { |k, v| "#{k}=#{v}" }.sort!
|
759
|
+
else
|
760
|
+
key
|
761
|
+
end.to_param
|
667
762
|
end
|
668
763
|
|
669
764
|
def normalize_version(key, options = nil)
|
@@ -673,31 +768,28 @@ module ActiveSupport
|
|
673
768
|
def expanded_version(key)
|
674
769
|
case
|
675
770
|
when key.respond_to?(:cache_version) then key.cache_version.to_param
|
676
|
-
when key.is_a?(Array) then key.map { |element| expanded_version(element) }.compact.to_param
|
771
|
+
when key.is_a?(Array) then key.map { |element| expanded_version(element) }.tap(&:compact!).to_param
|
677
772
|
when key.respond_to?(:to_a) then expanded_version(key.to_a)
|
678
773
|
end
|
679
774
|
end
|
680
775
|
|
681
776
|
def instrument(operation, key, options = nil)
|
682
|
-
|
777
|
+
if logger && logger.debug? && !silence?
|
778
|
+
logger.debug "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}"
|
779
|
+
end
|
683
780
|
|
684
|
-
payload = { key: key }
|
781
|
+
payload = { key: key, store: self.class.name }
|
685
782
|
payload.merge!(options) if options.is_a?(Hash)
|
686
783
|
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
|
687
784
|
end
|
688
785
|
|
689
|
-
def log
|
690
|
-
return unless logger && logger.debug? && !silence?
|
691
|
-
logger.debug(yield)
|
692
|
-
end
|
693
|
-
|
694
786
|
def handle_expired_entry(entry, key, options)
|
695
787
|
if entry && entry.expired?
|
696
788
|
race_ttl = options[:race_condition_ttl].to_i
|
697
789
|
if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
|
698
790
|
# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
|
699
791
|
# for a brief period while the entry is being recalculated.
|
700
|
-
entry.expires_at = Time.now + race_ttl
|
792
|
+
entry.expires_at = Time.now.to_f + race_ttl
|
701
793
|
write_entry(key, entry, expires_in: race_ttl * 2)
|
702
794
|
else
|
703
795
|
delete_entry(key, **options)
|
@@ -722,6 +814,98 @@ module ActiveSupport
|
|
722
814
|
end
|
723
815
|
end
|
724
816
|
|
817
|
+
module NullCoder # :nodoc:
|
818
|
+
extend self
|
819
|
+
|
820
|
+
def dump(entry)
|
821
|
+
entry
|
822
|
+
end
|
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
|
+
|
725
909
|
# This class is used to represent cache entries. Cache entries have a value, an optional
|
726
910
|
# expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
|
727
911
|
# on the cache. The version is used to support the :version option on the cache for rejecting
|
@@ -730,19 +914,22 @@ module ActiveSupport
|
|
730
914
|
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
|
731
915
|
# using short instance variable names that are lazily defined.
|
732
916
|
class Entry # :nodoc:
|
733
|
-
|
917
|
+
class << self
|
918
|
+
def unpack(members)
|
919
|
+
new(members[0], expires_at: members[1], version: members[2])
|
920
|
+
end
|
921
|
+
end
|
734
922
|
|
735
|
-
|
923
|
+
attr_reader :version
|
736
924
|
|
737
925
|
# Creates a new cache entry for the specified value. Options supported are
|
738
|
-
# +:
|
739
|
-
def initialize(value,
|
926
|
+
# +:compressed+, +:version+, +:expires_at+ and +:expires_in+.
|
927
|
+
def initialize(value, compressed: false, version: nil, expires_in: nil, expires_at: nil, **)
|
740
928
|
@value = value
|
741
929
|
@version = version
|
742
|
-
@created_at =
|
743
|
-
@expires_in = expires_in && expires_in.to_f
|
744
|
-
|
745
|
-
compress!(compress_threshold) if compress
|
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
|
746
933
|
end
|
747
934
|
|
748
935
|
def value
|
@@ -772,8 +959,8 @@ module ActiveSupport
|
|
772
959
|
end
|
773
960
|
|
774
961
|
# Returns the size of the cached value. This could be less than
|
775
|
-
# <tt>value.
|
776
|
-
def
|
962
|
+
# <tt>value.bytesize</tt> if the data is compressed.
|
963
|
+
def bytesize
|
777
964
|
case value
|
778
965
|
when NilClass
|
779
966
|
0
|
@@ -784,6 +971,38 @@ module ActiveSupport
|
|
784
971
|
end
|
785
972
|
end
|
786
973
|
|
974
|
+
def compressed? # :nodoc:
|
975
|
+
defined?(@compressed)
|
976
|
+
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
|
+
|
787
1006
|
# Duplicates the value in a class. This is used by cache implementations that don't natively
|
788
1007
|
# serialize entries to protect against accidental cache modifications.
|
789
1008
|
def dup_value!
|
@@ -796,33 +1015,13 @@ module ActiveSupport
|
|
796
1015
|
end
|
797
1016
|
end
|
798
1017
|
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
when String
|
805
|
-
uncompressed_size = @value.bytesize
|
806
|
-
else
|
807
|
-
serialized = Marshal.dump(@value)
|
808
|
-
uncompressed_size = serialized.bytesize
|
809
|
-
end
|
810
|
-
|
811
|
-
if uncompressed_size >= compress_threshold
|
812
|
-
serialized ||= Marshal.dump(@value)
|
813
|
-
compressed = Zlib::Deflate.deflate(serialized)
|
814
|
-
|
815
|
-
if compressed.bytesize < uncompressed_size
|
816
|
-
@value = compressed
|
817
|
-
@compressed = true
|
818
|
-
end
|
819
|
-
end
|
820
|
-
end
|
821
|
-
|
822
|
-
def compressed?
|
823
|
-
defined?(@compressed)
|
824
|
-
end
|
1018
|
+
def pack
|
1019
|
+
members = [value, expires_at, version]
|
1020
|
+
members.pop while !members.empty? && members.last.nil?
|
1021
|
+
members
|
1022
|
+
end
|
825
1023
|
|
1024
|
+
private
|
826
1025
|
def uncompress(value)
|
827
1026
|
Marshal.load(Zlib::Inflate.inflate(value))
|
828
1027
|
end
|