activesupport 6.0.3.7 → 7.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +220 -533
- data/MIT-LICENSE +1 -1
- data/README.rdoc +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 +3 -3
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/cache/file_store.rb +18 -11
- data/lib/active_support/cache/mem_cache_store.rb +143 -37
- data/lib/active_support/cache/memory_store.rb +56 -28
- data/lib/active_support/cache/null_store.rb +10 -2
- data/lib/active_support/cache/redis_cache_store.rb +63 -88
- data/lib/active_support/cache/strategy/local_cache.rb +46 -57
- data/lib/active_support/cache.rb +273 -82
- data/lib/active_support/callbacks.rb +226 -118
- 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 +9 -7
- 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.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 +21 -40
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +4 -4
- data/lib/active_support/core_ext/date/conversions.rb +5 -4
- 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 +13 -0
- 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 +5 -5
- 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 +139 -15
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/conversions.rb +2 -2
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- 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/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 +79 -72
- 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/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.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +8 -1
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- 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 +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +69 -45
- 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 +26 -6
- data/lib/active_support/core_ext/time/conversions.rb +6 -3
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +4 -19
- 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 -764
- data/lib/active_support/deprecation/behaviors.rb +19 -3
- 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 +6 -1
- data/lib/active_support/descendants_tracker.rb +177 -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 +11 -1
- data/lib/active_support/encrypted_file.rb +20 -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 +70 -134
- 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 +30 -4
- 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 +4 -4
- data/lib/active_support/isolated_execution_state.rb +56 -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 +19 -2
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber.rb +21 -3
- 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 +12 -10
- data/lib/active_support/message_verifier.rb +50 -18
- data/lib/active_support/messages/metadata.rb +11 -3
- 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 +47 -26
- data/lib/active_support/number_helper/number_converter.rb +2 -4
- data/lib/active_support/number_helper/number_to_currency_converter.rb +10 -9
- 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 +9 -16
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +8 -2
- 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/rescuable.rb +6 -6
- 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 +9 -21
- data/lib/active_support/testing/assertions.rb +49 -12
- 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 +120 -55
- data/lib/active_support/values/time_zone.rb +49 -18
- 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}/"
|
@@ -182,7 +201,12 @@ module ActiveSupport
|
|
182
201
|
# except for <tt>:namespace</tt> which can be used to set the global
|
183
202
|
# namespace for the cache.
|
184
203
|
def initialize(options = nil)
|
185
|
-
@options = options ? options
|
204
|
+
@options = options ? normalize_options(options) : {}
|
205
|
+
@options[:compress] = true unless @options.key?(:compress)
|
206
|
+
@options[:compress_threshold] = DEFAULT_COMPRESS_LIMIT unless @options.key?(:compress_threshold)
|
207
|
+
|
208
|
+
@coder = @options.delete(:coder) { default_coder } || NullCoder
|
209
|
+
@coder_supports_compression = @coder.respond_to?(:dump_compressed)
|
186
210
|
end
|
187
211
|
|
188
212
|
# Silences the logger.
|
@@ -244,11 +268,21 @@ module ActiveSupport
|
|
244
268
|
# All caches support auto-expiring content after a specified number of
|
245
269
|
# seconds. This value can be specified as an option to the constructor
|
246
270
|
# (in which case all entries will be affected), or it can be supplied to
|
247
|
-
# the +fetch+ or +write+ method to
|
271
|
+
# the +fetch+ or +write+ method to affect just one entry.
|
272
|
+
# <tt>:expire_in</tt> and <tt>:expired_in</tt> are aliases for
|
273
|
+
# <tt>:expires_in</tt>.
|
248
274
|
#
|
249
275
|
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
|
250
276
|
# cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
|
251
277
|
#
|
278
|
+
# Setting <tt>:expires_at</tt> will set an absolute expiration time on the cache.
|
279
|
+
# All caches support auto-expiring content after a specified number of
|
280
|
+
# seconds. This value can only be supplied to the +fetch+ or +write+ method to
|
281
|
+
# affect just one entry.
|
282
|
+
#
|
283
|
+
# cache = ActiveSupport::Cache::MemoryStore.new
|
284
|
+
# cache.write(key, value, expires_at: Time.now.at_end_of_hour)
|
285
|
+
#
|
252
286
|
# Setting <tt>:version</tt> verifies the cache stored under <tt>name</tt>
|
253
287
|
# is of the same version. nil is returned on mismatches despite contents.
|
254
288
|
# This feature is used to support recyclable cache keys.
|
@@ -312,14 +346,14 @@ module ActiveSupport
|
|
312
346
|
# :bar
|
313
347
|
# end
|
314
348
|
# cache.fetch('foo') # => "bar"
|
315
|
-
def fetch(name, options = nil)
|
349
|
+
def fetch(name, options = nil, &block)
|
316
350
|
if block_given?
|
317
351
|
options = merged_options(options)
|
318
352
|
key = normalize_key(name, options)
|
319
353
|
|
320
354
|
entry = nil
|
321
355
|
instrument(:read, name, options) do |payload|
|
322
|
-
cached_entry = read_entry(key, **options) unless options[:force]
|
356
|
+
cached_entry = read_entry(key, **options, event: payload) unless options[:force]
|
323
357
|
entry = handle_expired_entry(cached_entry, key, options)
|
324
358
|
entry = nil if entry && entry.mismatched?(normalize_version(name, options))
|
325
359
|
payload[:super_operation] = :fetch if payload
|
@@ -327,9 +361,9 @@ module ActiveSupport
|
|
327
361
|
end
|
328
362
|
|
329
363
|
if entry
|
330
|
-
get_entry_value(entry, name,
|
364
|
+
get_entry_value(entry, name, options)
|
331
365
|
else
|
332
|
-
save_block_result_to_cache(name,
|
366
|
+
save_block_result_to_cache(name, options, &block)
|
333
367
|
end
|
334
368
|
elsif options && options[:force]
|
335
369
|
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
|
@@ -353,7 +387,7 @@ module ActiveSupport
|
|
353
387
|
version = normalize_version(name, options)
|
354
388
|
|
355
389
|
instrument(:read, name, options) do |payload|
|
356
|
-
entry = read_entry(key, **options)
|
390
|
+
entry = read_entry(key, **options, event: payload)
|
357
391
|
|
358
392
|
if entry
|
359
393
|
if entry.expired?
|
@@ -385,7 +419,7 @@ module ActiveSupport
|
|
385
419
|
options = merged_options(options)
|
386
420
|
|
387
421
|
instrument :read_multi, names, options do |payload|
|
388
|
-
read_multi_entries(names, **options).tap do |results|
|
422
|
+
read_multi_entries(names, **options, event: payload).tap do |results|
|
389
423
|
payload[:hits] = results.keys
|
390
424
|
end
|
391
425
|
end
|
@@ -441,14 +475,14 @@ module ActiveSupport
|
|
441
475
|
instrument :read_multi, names, options do |payload|
|
442
476
|
reads = read_multi_entries(names, **options)
|
443
477
|
writes = {}
|
444
|
-
ordered = names.
|
445
|
-
|
478
|
+
ordered = names.index_with do |name|
|
479
|
+
reads.fetch(name) { writes[name] = yield(name) }
|
446
480
|
end
|
447
481
|
|
448
482
|
payload[:hits] = reads.keys
|
449
483
|
payload[:super_operation] = :fetch_multi
|
450
484
|
|
451
|
-
write_multi(writes,
|
485
|
+
write_multi(writes, options)
|
452
486
|
|
453
487
|
ordered
|
454
488
|
end
|
@@ -477,18 +511,34 @@ module ActiveSupport
|
|
477
511
|
end
|
478
512
|
end
|
479
513
|
|
514
|
+
# Deletes multiple entries in the cache.
|
515
|
+
#
|
516
|
+
# Options are passed to the underlying cache implementation.
|
517
|
+
def delete_multi(names, options = nil)
|
518
|
+
options = merged_options(options)
|
519
|
+
names.map! { |key| normalize_key(key, options) }
|
520
|
+
|
521
|
+
instrument :delete_multi, names do
|
522
|
+
delete_multi_entries(names, **options)
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
480
526
|
# Returns +true+ if the cache contains an entry for the given key.
|
481
527
|
#
|
482
528
|
# Options are passed to the underlying cache implementation.
|
483
529
|
def exist?(name, options = nil)
|
484
530
|
options = merged_options(options)
|
485
531
|
|
486
|
-
instrument(:exist?, name) do
|
487
|
-
entry = read_entry(normalize_key(name, options), **options)
|
532
|
+
instrument(:exist?, name) do |payload|
|
533
|
+
entry = read_entry(normalize_key(name, options), **options, event: payload)
|
488
534
|
(entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
|
489
535
|
end
|
490
536
|
end
|
491
537
|
|
538
|
+
def new_entry(value, options = nil) # :nodoc:
|
539
|
+
Entry.new(value, **merged_options(options))
|
540
|
+
end
|
541
|
+
|
492
542
|
# Deletes all entries with keys matching the pattern.
|
493
543
|
#
|
494
544
|
# Options are passed to the underlying cache implementation.
|
@@ -536,6 +586,10 @@ module ActiveSupport
|
|
536
586
|
end
|
537
587
|
|
538
588
|
private
|
589
|
+
def default_coder
|
590
|
+
Coders[Cache.format_version]
|
591
|
+
end
|
592
|
+
|
539
593
|
# Adds the namespace defined in the options to a pattern designed to
|
540
594
|
# match keys. Implementations that support delete_matched should call
|
541
595
|
# this method to translate a pattern that matches names into one that
|
@@ -567,26 +621,36 @@ module ActiveSupport
|
|
567
621
|
raise NotImplementedError.new
|
568
622
|
end
|
569
623
|
|
624
|
+
def serialize_entry(entry, **options)
|
625
|
+
options = merged_options(options)
|
626
|
+
if @coder_supports_compression && options[:compress]
|
627
|
+
@coder.dump_compressed(entry, options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT)
|
628
|
+
else
|
629
|
+
@coder.dump(entry)
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
def deserialize_entry(payload)
|
634
|
+
payload.nil? ? nil : @coder.load(payload)
|
635
|
+
end
|
636
|
+
|
570
637
|
# Reads multiple entries from the cache implementation. Subclasses MAY
|
571
638
|
# implement this method.
|
572
639
|
def read_multi_entries(names, **options)
|
573
|
-
|
574
|
-
|
575
|
-
|
640
|
+
names.each_with_object({}) do |name, results|
|
641
|
+
key = normalize_key(name, options)
|
642
|
+
entry = read_entry(key, **options)
|
643
|
+
|
644
|
+
next unless entry
|
645
|
+
|
576
646
|
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
|
647
|
+
|
648
|
+
if entry.expired?
|
649
|
+
delete_entry(key, **options)
|
650
|
+
elsif !entry.mismatched?(version)
|
651
|
+
results[name] = entry.value
|
587
652
|
end
|
588
653
|
end
|
589
|
-
results
|
590
654
|
end
|
591
655
|
|
592
656
|
# Writes multiple entries to the cache implementation. Subclasses MAY
|
@@ -603,9 +667,16 @@ module ActiveSupport
|
|
603
667
|
raise NotImplementedError.new
|
604
668
|
end
|
605
669
|
|
670
|
+
# Deletes multiples entries in the cache implementation. Subclasses MAY
|
671
|
+
# implement this method.
|
672
|
+
def delete_multi_entries(entries, **options)
|
673
|
+
entries.count { |key| delete_entry(key, **options) }
|
674
|
+
end
|
675
|
+
|
606
676
|
# Merges the default options with ones specific to a method call.
|
607
677
|
def merged_options(call_options)
|
608
678
|
if call_options
|
679
|
+
call_options = normalize_options(call_options)
|
609
680
|
if options.empty?
|
610
681
|
call_options
|
611
682
|
else
|
@@ -616,6 +687,18 @@ module ActiveSupport
|
|
616
687
|
end
|
617
688
|
end
|
618
689
|
|
690
|
+
# Normalize aliased options to their canonical form
|
691
|
+
def normalize_options(options)
|
692
|
+
options = options.dup
|
693
|
+
OPTION_ALIASES.each do |canonical_name, aliases|
|
694
|
+
alias_key = aliases.detect { |key| options.key?(key) }
|
695
|
+
options[canonical_name] ||= options[alias_key] if alias_key
|
696
|
+
options.except!(*aliases)
|
697
|
+
end
|
698
|
+
|
699
|
+
options
|
700
|
+
end
|
701
|
+
|
619
702
|
# Expands and namespaces the cache key. May be overridden by
|
620
703
|
# cache stores to do additional normalization.
|
621
704
|
def normalize_key(key, options = nil)
|
@@ -639,6 +722,10 @@ module ActiveSupport
|
|
639
722
|
namespace = namespace.call
|
640
723
|
end
|
641
724
|
|
725
|
+
if key && key.encoding != Encoding::UTF_8
|
726
|
+
key = key.dup.force_encoding(Encoding::UTF_8)
|
727
|
+
end
|
728
|
+
|
642
729
|
if namespace
|
643
730
|
"#{namespace}:#{key}"
|
644
731
|
else
|
@@ -655,15 +742,15 @@ module ActiveSupport
|
|
655
742
|
case key
|
656
743
|
when Array
|
657
744
|
if key.size > 1
|
658
|
-
key
|
745
|
+
key.collect { |element| expanded_key(element) }
|
659
746
|
else
|
660
|
-
|
747
|
+
expanded_key(key.first)
|
661
748
|
end
|
662
749
|
when Hash
|
663
|
-
key
|
664
|
-
|
665
|
-
|
666
|
-
|
750
|
+
key.collect { |k, v| "#{k}=#{v}" }.sort!
|
751
|
+
else
|
752
|
+
key
|
753
|
+
end.to_param
|
667
754
|
end
|
668
755
|
|
669
756
|
def normalize_version(key, options = nil)
|
@@ -673,31 +760,28 @@ module ActiveSupport
|
|
673
760
|
def expanded_version(key)
|
674
761
|
case
|
675
762
|
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
|
763
|
+
when key.is_a?(Array) then key.map { |element| expanded_version(element) }.tap(&:compact!).to_param
|
677
764
|
when key.respond_to?(:to_a) then expanded_version(key.to_a)
|
678
765
|
end
|
679
766
|
end
|
680
767
|
|
681
768
|
def instrument(operation, key, options = nil)
|
682
|
-
|
769
|
+
if logger && logger.debug? && !silence?
|
770
|
+
logger.debug "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}"
|
771
|
+
end
|
683
772
|
|
684
|
-
payload = { key: key }
|
773
|
+
payload = { key: key, store: self.class.name }
|
685
774
|
payload.merge!(options) if options.is_a?(Hash)
|
686
775
|
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
|
687
776
|
end
|
688
777
|
|
689
|
-
def log
|
690
|
-
return unless logger && logger.debug? && !silence?
|
691
|
-
logger.debug(yield)
|
692
|
-
end
|
693
|
-
|
694
778
|
def handle_expired_entry(entry, key, options)
|
695
779
|
if entry && entry.expired?
|
696
780
|
race_ttl = options[:race_condition_ttl].to_i
|
697
781
|
if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
|
698
782
|
# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
|
699
783
|
# for a brief period while the entry is being recalculated.
|
700
|
-
entry.expires_at = Time.now + race_ttl
|
784
|
+
entry.expires_at = Time.now.to_f + race_ttl
|
701
785
|
write_entry(key, entry, expires_in: race_ttl * 2)
|
702
786
|
else
|
703
787
|
delete_entry(key, **options)
|
@@ -712,7 +796,7 @@ module ActiveSupport
|
|
712
796
|
entry.value
|
713
797
|
end
|
714
798
|
|
715
|
-
def save_block_result_to_cache(name,
|
799
|
+
def save_block_result_to_cache(name, options)
|
716
800
|
result = instrument(:generate, name, options) do
|
717
801
|
yield(name)
|
718
802
|
end
|
@@ -722,6 +806,98 @@ module ActiveSupport
|
|
722
806
|
end
|
723
807
|
end
|
724
808
|
|
809
|
+
module NullCoder # :nodoc:
|
810
|
+
extend self
|
811
|
+
|
812
|
+
def dump(entry)
|
813
|
+
entry
|
814
|
+
end
|
815
|
+
|
816
|
+
def dump_compressed(entry, threshold)
|
817
|
+
entry.compressed(threshold)
|
818
|
+
end
|
819
|
+
|
820
|
+
def load(payload)
|
821
|
+
payload
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|
825
|
+
module Coders # :nodoc:
|
826
|
+
MARK_61 = "\x04\b".b.freeze # The one set by Marshal.
|
827
|
+
MARK_70_UNCOMPRESSED = "\x00".b.freeze
|
828
|
+
MARK_70_COMPRESSED = "\x01".b.freeze
|
829
|
+
|
830
|
+
class << self
|
831
|
+
def [](version)
|
832
|
+
case version
|
833
|
+
when 6.1
|
834
|
+
Rails61Coder
|
835
|
+
when 7.0
|
836
|
+
Rails70Coder
|
837
|
+
else
|
838
|
+
raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}"
|
839
|
+
end
|
840
|
+
end
|
841
|
+
end
|
842
|
+
|
843
|
+
module Loader
|
844
|
+
extend self
|
845
|
+
|
846
|
+
def load(payload)
|
847
|
+
if !payload.is_a?(String)
|
848
|
+
ActiveSupport::Cache::Store.logger&.warn %{Payload wasn't a string, was #{payload.class.name} - couldn't unmarshal, so returning nil."}
|
849
|
+
|
850
|
+
return nil
|
851
|
+
elsif payload.start_with?(MARK_70_UNCOMPRESSED)
|
852
|
+
members = Marshal.load(payload.byteslice(1..-1))
|
853
|
+
elsif payload.start_with?(MARK_70_COMPRESSED)
|
854
|
+
members = Marshal.load(Zlib::Inflate.inflate(payload.byteslice(1..-1)))
|
855
|
+
elsif payload.start_with?(MARK_61)
|
856
|
+
return Marshal.load(payload)
|
857
|
+
else
|
858
|
+
ActiveSupport::Cache::Store.logger&.warn %{Invalid cache prefix: #{payload.byteslice(0).inspect}, expected "\\x00" or "\\x01"}
|
859
|
+
|
860
|
+
return nil
|
861
|
+
end
|
862
|
+
Entry.unpack(members)
|
863
|
+
end
|
864
|
+
end
|
865
|
+
|
866
|
+
module Rails61Coder
|
867
|
+
include Loader
|
868
|
+
extend self
|
869
|
+
|
870
|
+
def dump(entry)
|
871
|
+
Marshal.dump(entry)
|
872
|
+
end
|
873
|
+
|
874
|
+
def dump_compressed(entry, threshold)
|
875
|
+
Marshal.dump(entry.compressed(threshold))
|
876
|
+
end
|
877
|
+
end
|
878
|
+
|
879
|
+
module Rails70Coder
|
880
|
+
include Loader
|
881
|
+
extend self
|
882
|
+
|
883
|
+
def dump(entry)
|
884
|
+
MARK_70_UNCOMPRESSED + Marshal.dump(entry.pack)
|
885
|
+
end
|
886
|
+
|
887
|
+
def dump_compressed(entry, threshold)
|
888
|
+
payload = Marshal.dump(entry.pack)
|
889
|
+
if payload.bytesize >= threshold
|
890
|
+
compressed_payload = Zlib::Deflate.deflate(payload)
|
891
|
+
if compressed_payload.bytesize < payload.bytesize
|
892
|
+
return MARK_70_COMPRESSED + compressed_payload
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
MARK_70_UNCOMPRESSED + payload
|
897
|
+
end
|
898
|
+
end
|
899
|
+
end
|
900
|
+
|
725
901
|
# This class is used to represent cache entries. Cache entries have a value, an optional
|
726
902
|
# expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
|
727
903
|
# on the cache. The version is used to support the :version option on the cache for rejecting
|
@@ -730,19 +906,22 @@ module ActiveSupport
|
|
730
906
|
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
|
731
907
|
# using short instance variable names that are lazily defined.
|
732
908
|
class Entry # :nodoc:
|
733
|
-
|
909
|
+
class << self
|
910
|
+
def unpack(members)
|
911
|
+
new(members[0], expires_at: members[1], version: members[2])
|
912
|
+
end
|
913
|
+
end
|
734
914
|
|
735
|
-
|
915
|
+
attr_reader :version
|
736
916
|
|
737
917
|
# Creates a new cache entry for the specified value. Options supported are
|
738
|
-
# +:
|
739
|
-
def initialize(value,
|
918
|
+
# +:compressed+, +:version+, +:expires_at+ and +:expires_in+.
|
919
|
+
def initialize(value, compressed: false, version: nil, expires_in: nil, expires_at: nil, **)
|
740
920
|
@value = value
|
741
921
|
@version = version
|
742
|
-
@created_at =
|
743
|
-
@expires_in = expires_in && expires_in.to_f
|
744
|
-
|
745
|
-
compress!(compress_threshold) if compress
|
922
|
+
@created_at = 0.0
|
923
|
+
@expires_in = expires_at&.to_f || expires_in && (expires_in.to_f + Time.now.to_f)
|
924
|
+
@compressed = true if compressed
|
746
925
|
end
|
747
926
|
|
748
927
|
def value
|
@@ -772,8 +951,8 @@ module ActiveSupport
|
|
772
951
|
end
|
773
952
|
|
774
953
|
# Returns the size of the cached value. This could be less than
|
775
|
-
# <tt>value.
|
776
|
-
def
|
954
|
+
# <tt>value.bytesize</tt> if the data is compressed.
|
955
|
+
def bytesize
|
777
956
|
case value
|
778
957
|
when NilClass
|
779
958
|
0
|
@@ -784,6 +963,38 @@ module ActiveSupport
|
|
784
963
|
end
|
785
964
|
end
|
786
965
|
|
966
|
+
def compressed? # :nodoc:
|
967
|
+
defined?(@compressed)
|
968
|
+
end
|
969
|
+
|
970
|
+
def compressed(compress_threshold)
|
971
|
+
return self if compressed?
|
972
|
+
|
973
|
+
case @value
|
974
|
+
when nil, true, false, Numeric
|
975
|
+
uncompressed_size = 0
|
976
|
+
when String
|
977
|
+
uncompressed_size = @value.bytesize
|
978
|
+
else
|
979
|
+
serialized = Marshal.dump(@value)
|
980
|
+
uncompressed_size = serialized.bytesize
|
981
|
+
end
|
982
|
+
|
983
|
+
if uncompressed_size >= compress_threshold
|
984
|
+
serialized ||= Marshal.dump(@value)
|
985
|
+
compressed = Zlib::Deflate.deflate(serialized)
|
986
|
+
|
987
|
+
if compressed.bytesize < uncompressed_size
|
988
|
+
return Entry.new(compressed, compressed: true, expires_at: expires_at, version: version)
|
989
|
+
end
|
990
|
+
end
|
991
|
+
self
|
992
|
+
end
|
993
|
+
|
994
|
+
def local?
|
995
|
+
false
|
996
|
+
end
|
997
|
+
|
787
998
|
# Duplicates the value in a class. This is used by cache implementations that don't natively
|
788
999
|
# serialize entries to protect against accidental cache modifications.
|
789
1000
|
def dup_value!
|
@@ -796,33 +1007,13 @@ module ActiveSupport
|
|
796
1007
|
end
|
797
1008
|
end
|
798
1009
|
|
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
|
1010
|
+
def pack
|
1011
|
+
members = [value, expires_at, version]
|
1012
|
+
members.pop while !members.empty? && members.last.nil?
|
1013
|
+
members
|
1014
|
+
end
|
825
1015
|
|
1016
|
+
private
|
826
1017
|
def uncompress(value)
|
827
1018
|
Marshal.load(Zlib::Inflate.inflate(value))
|
828
1019
|
end
|