activesupport 4.0.12 → 7.0.2.4
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 +5 -5
- data/CHANGELOG.md +249 -501
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -5
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/all.rb +5 -3
- data/lib/active_support/array_inquirer.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +41 -13
- data/lib/active_support/benchmarkable.rb +7 -15
- data/lib/active_support/builder.rb +3 -1
- data/lib/active_support/cache/file_store.rb +96 -74
- data/lib/active_support/cache/mem_cache_store.rb +211 -103
- data/lib/active_support/cache/memory_store.rb +90 -58
- data/lib/active_support/cache/null_store.rb +19 -7
- data/lib/active_support/cache/redis_cache_store.rb +468 -0
- data/lib/active_support/cache/strategy/local_cache.rb +86 -83
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
- data/lib/active_support/cache.rb +580 -241
- data/lib/active_support/callbacks.rb +812 -425
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +103 -14
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +33 -0
- data/lib/active_support/concurrency/share_lock.rb +226 -0
- data/lib/active_support/configurable.rb +21 -19
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +47 -1
- data/lib/active_support/core_ext/array/conversions.rb +35 -44
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/extract_options.rb +2 -0
- data/lib/active_support/core_ext/array/grouping.rb +26 -16
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/wrap.rb +7 -4
- data/lib/active_support/core_ext/array.rb +10 -7
- data/lib/active_support/core_ext/benchmark.rb +5 -3
- data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
- data/lib/active_support/core_ext/big_decimal.rb +3 -1
- data/lib/active_support/core_ext/class/attribute.rb +52 -49
- data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
- data/lib/active_support/core_ext/class/subclasses.rb +25 -26
- data/lib/active_support/core_ext/class.rb +4 -4
- data/lib/active_support/core_ext/date/acts_like.rb +3 -1
- data/lib/active_support/core_ext/date/blank.rb +14 -0
- data/lib/active_support/core_ext/date/calculations.rb +31 -18
- data/lib/active_support/core_ext/date/conversions.rb +43 -32
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date/zones.rb +5 -34
- data/lib/active_support/core_ext/date.rb +7 -4
- data/lib/active_support/core_ext/date_and_time/calculations.rb +198 -66
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +31 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
- data/lib/active_support/core_ext/date_time/blank.rb +14 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +79 -38
- data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +31 -26
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +8 -4
- data/lib/active_support/core_ext/digest/uuid.rb +79 -0
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +249 -17
- data/lib/active_support/core_ext/file/atomic.rb +41 -32
- data/lib/active_support/core_ext/file.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +71 -49
- data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +14 -5
- data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
- data/lib/active_support/core_ext/hash/keys.rb +39 -56
- data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
- data/lib/active_support/core_ext/hash/slice.rb +8 -23
- data/lib/active_support/core_ext/hash.rb +10 -8
- data/lib/active_support/core_ext/integer/inflections.rb +3 -1
- data/lib/active_support/core_ext/integer/multiple.rb +3 -1
- data/lib/active_support/core_ext/integer/time.rb +11 -33
- data/lib/active_support/core_ext/integer.rb +5 -3
- data/lib/active_support/core_ext/kernel/concern.rb +14 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +9 -78
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/kernel.rb +5 -4
- data/lib/active_support/core_ext/load_error.rb +5 -21
- data/lib/active_support/core_ext/module/aliasing.rb +6 -44
- data/lib/active_support/core_ext/module/anonymous.rb +12 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
- data/lib/active_support/core_ext/module/attribute_accessors.rb +186 -44
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +157 -0
- data/lib/active_support/core_ext/module/concerning.rb +140 -0
- data/lib/active_support/core_ext/module/delegation.rb +172 -45
- data/lib/active_support/core_ext/module/deprecation.rb +3 -3
- data/lib/active_support/core_ext/module/introspection.rb +23 -38
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +8 -3
- data/lib/active_support/core_ext/module.rb +13 -10
- data/lib/active_support/core_ext/name_error.rb +45 -4
- data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +135 -127
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric/time.rb +37 -50
- data/lib/active_support/core_ext/numeric.rb +6 -3
- data/lib/active_support/core_ext/object/acts_like.rb +41 -6
- data/lib/active_support/core_ext/object/blank.rb +70 -20
- data/lib/active_support/core_ext/object/conversions.rb +6 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
- data/lib/active_support/core_ext/object/duplicable.rb +17 -47
- data/lib/active_support/core_ext/object/inclusion.rb +18 -15
- data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
- data/lib/active_support/core_ext/object/json.rb +244 -0
- data/lib/active_support/core_ext/object/to_param.rb +3 -1
- data/lib/active_support/core_ext/object/to_query.rb +21 -8
- data/lib/active_support/core_ext/object/try.rb +106 -26
- data/lib/active_support/core_ext/object/with_options.rb +64 -5
- data/lib/active_support/core_ext/object.rb +14 -12
- 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 +57 -0
- data/lib/active_support/core_ext/range/conversions.rb +37 -15
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +18 -17
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
- data/lib/active_support/core_ext/range/overlaps.rb +2 -0
- data/lib/active_support/core_ext/range.rb +7 -4
- data/lib/active_support/core_ext/regexp.rb +10 -1
- data/lib/active_support/core_ext/securerandom.rb +45 -0
- data/lib/active_support/core_ext/string/access.rb +42 -51
- data/lib/active_support/core_ext/string/behavior.rb +3 -1
- data/lib/active_support/core_ext/string/conversions.rb +18 -13
- data/lib/active_support/core_ext/string/exclude.rb +5 -3
- data/lib/active_support/core_ext/string/filters.rb +97 -7
- data/lib/active_support/core_ext/string/indent.rb +6 -4
- data/lib/active_support/core_ext/string/inflections.rb +106 -25
- data/lib/active_support/core_ext/string/inquiry.rb +4 -1
- data/lib/active_support/core_ext/string/multibyte.rb +18 -9
- data/lib/active_support/core_ext/string/output_safety.rb +227 -54
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
- data/lib/active_support/core_ext/string/strip.rb +6 -5
- data/lib/active_support/core_ext/string/zones.rb +4 -1
- data/lib/active_support/core_ext/string.rb +15 -13
- 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/acts_like.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +178 -116
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +37 -25
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +44 -42
- data/lib/active_support/core_ext/time.rb +8 -5
- data/lib/active_support/core_ext/uri.rb +4 -25
- data/lib/active_support/core_ext.rb +4 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +226 -0
- data/lib/active_support/dependencies/autoload.rb +3 -1
- data/lib/active_support/dependencies/interlock.rb +49 -0
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +71 -696
- data/lib/active_support/deprecation/behaviors.rb +65 -16
- data/lib/active_support/deprecation/constant_accessor.rb +52 -0
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +16 -2
- data/lib/active_support/deprecation/method_wrappers.rb +62 -21
- data/lib/active_support/deprecation/proxy_wrappers.rb +82 -31
- data/lib/active_support/deprecation/reporting.rb +81 -18
- data/lib/active_support/deprecation.rb +19 -11
- data/lib/active_support/descendants_tracker.rb +192 -34
- data/lib/active_support/digest.rb +22 -0
- data/lib/active_support/duration/iso8601_parser.rb +123 -0
- data/lib/active_support/duration/iso8601_serializer.rb +67 -0
- data/lib/active_support/duration.rb +437 -39
- data/lib/active_support/encrypted_configuration.rb +56 -0
- data/lib/active_support/encrypted_file.rb +117 -0
- 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 +170 -0
- 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 +151 -0
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +62 -37
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +17 -0
- data/lib/active_support/gzip.rb +7 -5
- data/lib/active_support/hash_with_indifferent_access.rb +207 -54
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +10 -6
- data/lib/active_support/i18n_railtie.rb +48 -19
- data/lib/active_support/inflections.rb +19 -12
- data/lib/active_support/inflector/inflections.rb +97 -37
- data/lib/active_support/inflector/methods.rb +192 -157
- data/lib/active_support/inflector/transliterate.rb +83 -33
- data/lib/active_support/inflector.rb +7 -5
- data/lib/active_support/isolated_execution_state.rb +64 -0
- data/lib/active_support/json/decoding.rb +37 -42
- data/lib/active_support/json/encoding.rb +93 -293
- data/lib/active_support/json.rb +4 -2
- data/lib/active_support/key_generator.rb +30 -47
- data/lib/active_support/lazy_load_hooks.rb +54 -21
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +10 -4
- data/lib/active_support/log_subscriber/test_helper.rb +14 -12
- data/lib/active_support/log_subscriber.rb +61 -18
- data/lib/active_support/logger.rb +40 -4
- data/lib/active_support/logger_silence.rb +17 -20
- data/lib/active_support/logger_thread_safe_level.rb +69 -0
- data/lib/active_support/message_encryptor.rb +178 -55
- data/lib/active_support/message_verifier.rb +195 -26
- data/lib/active_support/messages/metadata.rb +80 -0
- data/lib/active_support/messages/rotation_configuration.rb +23 -0
- data/lib/active_support/messages/rotator.rb +57 -0
- data/lib/active_support/multibyte/chars.rb +45 -92
- data/lib/active_support/multibyte/unicode.rb +44 -377
- data/lib/active_support/multibyte.rb +5 -3
- data/lib/active_support/notifications/fanout.rb +177 -44
- data/lib/active_support/notifications/instrumenter.rb +117 -17
- data/lib/active_support/notifications.rb +106 -39
- data/lib/active_support/number_helper/number_converter.rb +181 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +59 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
- data/lib/active_support/number_helper/rounding_helper.rb +46 -0
- data/lib/active_support/number_helper.rb +152 -394
- data/lib/active_support/option_merger.rb +18 -5
- data/lib/active_support/ordered_hash.rb +8 -6
- data/lib/active_support/ordered_options.rb +43 -7
- data/lib/active_support/parameter_filter.rb +138 -0
- data/lib/active_support/per_thread_registry.rb +24 -11
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +10 -11
- data/lib/active_support/railtie.rb +118 -12
- data/lib/active_support/reloader.rb +130 -0
- data/lib/active_support/rescuable.rb +112 -57
- 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 +38 -0
- data/lib/active_support/string_inquirer.rb +11 -4
- data/lib/active_support/subscriber.rb +109 -39
- data/lib/active_support/tagged_logging.rb +54 -17
- data/lib/active_support/test_case.rb +121 -37
- data/lib/active_support/testing/assertions.rb +177 -39
- data/lib/active_support/testing/autorun.rb +5 -3
- data/lib/active_support/testing/constant_lookup.rb +3 -6
- data/lib/active_support/testing/declarative.rb +10 -22
- data/lib/active_support/testing/deprecation.rb +65 -11
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +56 -87
- data/lib/active_support/testing/method_call_assertions.rb +70 -0
- 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 +55 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/setup_and_teardown.rb +30 -10
- data/lib/active_support/testing/stream.rb +41 -0
- data/lib/active_support/testing/tagged_logging.rb +6 -4
- data/lib/active_support/testing/time_helpers.rb +246 -0
- data/lib/active_support/time.rb +13 -13
- data/lib/active_support/time_with_zone.rb +315 -90
- data/lib/active_support/values/time_zone.rb +306 -135
- data/lib/active_support/version.rb +6 -7
- data/lib/active_support/xml_mini/jdom.rb +117 -115
- data/lib/active_support/xml_mini/libxml.rb +22 -21
- data/lib/active_support/xml_mini/libxmlsax.rb +17 -19
- data/lib/active_support/xml_mini/nokogiri.rb +19 -19
- data/lib/active_support/xml_mini/nokogirisax.rb +16 -17
- data/lib/active_support/xml_mini/rexml.rb +25 -17
- data/lib/active_support/xml_mini.rb +67 -56
- data/lib/active_support.rb +58 -3
- metadata +125 -66
- data/lib/active_support/basic_object.rb +0 -11
- data/lib/active_support/buffered_logger.rb +0 -21
- data/lib/active_support/concurrency/latch.rb +0 -27
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
- data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
- data/lib/active_support/core_ext/date_time/zones.rb +0 -24
- data/lib/active_support/core_ext/hash/diff.rb +0 -14
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
- data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
- data/lib/active_support/core_ext/logger.rb +0 -67
- data/lib/active_support/core_ext/marshal.rb +0 -21
- data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
- data/lib/active_support/core_ext/module/reachable.rb +0 -8
- data/lib/active_support/core_ext/object/to_json.rb +0 -27
- data/lib/active_support/core_ext/proc.rb +0 -17
- data/lib/active_support/core_ext/range/include_range.rb +0 -23
- data/lib/active_support/core_ext/string/encoding.rb +0 -8
- data/lib/active_support/core_ext/struct.rb +0 -6
- data/lib/active_support/core_ext/thread.rb +0 -79
- data/lib/active_support/core_ext/time/marshal.rb +0 -30
- data/lib/active_support/file_watcher.rb +0 -36
- data/lib/active_support/json/variable.rb +0 -18
- data/lib/active_support/testing/pending.rb +0 -14
- data/lib/active_support/values/unicode_tables.dat +0 -0
data/lib/active_support/cache.rb
CHANGED
@@ -1,32 +1,46 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "zlib"
|
4
|
+
require "active_support/core_ext/array/extract_options"
|
5
|
+
require "active_support/core_ext/array/wrap"
|
6
|
+
require "active_support/core_ext/enumerable"
|
7
|
+
require "active_support/core_ext/module/attribute_accessors"
|
8
|
+
require "active_support/core_ext/numeric/bytes"
|
9
|
+
require "active_support/core_ext/numeric/time"
|
10
|
+
require "active_support/core_ext/object/to_param"
|
11
|
+
require "active_support/core_ext/object/try"
|
12
|
+
require "active_support/core_ext/string/inflections"
|
11
13
|
|
12
14
|
module ActiveSupport
|
13
15
|
# See ActiveSupport::Cache::Store for documentation.
|
14
16
|
module Cache
|
15
|
-
autoload :FileStore,
|
16
|
-
autoload :MemoryStore,
|
17
|
-
autoload :MemCacheStore,
|
18
|
-
autoload :NullStore,
|
17
|
+
autoload :FileStore, "active_support/cache/file_store"
|
18
|
+
autoload :MemoryStore, "active_support/cache/memory_store"
|
19
|
+
autoload :MemCacheStore, "active_support/cache/mem_cache_store"
|
20
|
+
autoload :NullStore, "active_support/cache/null_store"
|
21
|
+
autoload :RedisCacheStore, "active_support/cache/redis_cache_store"
|
19
22
|
|
20
23
|
# These options mean something to all cache implementations. Individual cache
|
21
24
|
# implementations may support additional options.
|
22
|
-
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
|
23
33
|
|
24
34
|
module Strategy
|
25
|
-
autoload :LocalCache,
|
35
|
+
autoload :LocalCache, "active_support/cache/strategy/local_cache"
|
26
36
|
end
|
27
37
|
|
38
|
+
@format_version = 6.1
|
39
|
+
|
28
40
|
class << self
|
29
|
-
|
41
|
+
attr_accessor :format_version
|
42
|
+
|
43
|
+
# Creates a new Store object according to the given options.
|
30
44
|
#
|
31
45
|
# If no arguments are passed to this method, then a new
|
32
46
|
# ActiveSupport::Cache::MemoryStore object will be returned.
|
@@ -51,12 +65,19 @@ module ActiveSupport
|
|
51
65
|
#
|
52
66
|
# ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
|
53
67
|
# # => returns MyOwnCacheStore.new
|
54
|
-
def lookup_store(*
|
55
|
-
store, *parameters = *Array.wrap(store_option).flatten
|
56
|
-
|
68
|
+
def lookup_store(store = nil, *parameters)
|
57
69
|
case store
|
58
70
|
when Symbol
|
59
|
-
|
71
|
+
options = parameters.extract_options!
|
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
|
79
|
+
when Array
|
80
|
+
lookup_store(*store)
|
60
81
|
when nil
|
61
82
|
ActiveSupport::Cache::MemoryStore.new
|
62
83
|
else
|
@@ -72,12 +93,12 @@ module ActiveSupport
|
|
72
93
|
# each of elements in the array will be turned into parameters/keys and
|
73
94
|
# concatenated into a single key. For example:
|
74
95
|
#
|
75
|
-
# expand_cache_key([:foo, :bar]) # => "foo/bar"
|
76
|
-
# expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
|
96
|
+
# ActiveSupport::Cache.expand_cache_key([:foo, :bar]) # => "foo/bar"
|
97
|
+
# ActiveSupport::Cache.expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
|
77
98
|
#
|
78
99
|
# The +key+ argument can also respond to +cache_key+ or +to_param+.
|
79
100
|
def expand_cache_key(key, namespace = nil)
|
80
|
-
expanded_cache_key = namespace ? "#{namespace}/" : ""
|
101
|
+
expanded_cache_key = namespace ? +"#{namespace}/" : +""
|
81
102
|
|
82
103
|
if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
|
83
104
|
expanded_cache_key << "#{prefix}/"
|
@@ -88,25 +109,27 @@ module ActiveSupport
|
|
88
109
|
end
|
89
110
|
|
90
111
|
private
|
112
|
+
def retrieve_cache_key(key)
|
113
|
+
case
|
114
|
+
when key.respond_to?(:cache_key_with_version) then key.cache_key_with_version
|
115
|
+
when key.respond_to?(:cache_key) then key.cache_key
|
116
|
+
when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
|
117
|
+
when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
|
118
|
+
else key.to_param
|
119
|
+
end.to_s
|
120
|
+
end
|
91
121
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
def retrieve_store_class(store)
|
104
|
-
require "active_support/cache/#{store}"
|
105
|
-
rescue LoadError => e
|
106
|
-
raise "Could not find cache store adapter for #{store} (#{e})"
|
107
|
-
else
|
108
|
-
ActiveSupport::Cache.const_get(store.to_s.camelize)
|
109
|
-
end
|
122
|
+
# Obtains the specified cache store class, given the name of the +store+.
|
123
|
+
# Raises an error when the store class cannot be found.
|
124
|
+
def retrieve_store_class(store)
|
125
|
+
# require_relative cannot be used here because the class might be
|
126
|
+
# provided by another gem, like redis-activesupport for example.
|
127
|
+
require "active_support/cache/#{store}"
|
128
|
+
rescue LoadError => e
|
129
|
+
raise "Could not find cache store adapter for #{store} (#{e})"
|
130
|
+
else
|
131
|
+
ActiveSupport::Cache.const_get(store.to_s.camelize)
|
132
|
+
end
|
110
133
|
end
|
111
134
|
|
112
135
|
# An abstract cache store class. There are multiple cache store
|
@@ -146,33 +169,53 @@ module ActiveSupport
|
|
146
169
|
# cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
|
147
170
|
# @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
|
148
171
|
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
# <tt
|
153
|
-
#
|
154
|
-
# <tt>:compress_threshold</tt> option. The default threshold is 16K.
|
172
|
+
# Cached data larger than 1kB are compressed by default. To turn off
|
173
|
+
# compression, pass <tt>compress: false</tt> to the initializer or to
|
174
|
+
# individual +fetch+ or +write+ method calls. The 1kB compression
|
175
|
+
# threshold is configurable with the <tt>:compress_threshold</tt> option,
|
176
|
+
# specified in bytes.
|
155
177
|
class Store
|
156
|
-
|
157
|
-
cattr_accessor :logger, :instance_writer => true
|
178
|
+
cattr_accessor :logger, instance_writer: true
|
158
179
|
|
159
180
|
attr_reader :silence, :options
|
160
181
|
alias :silence? :silence
|
161
182
|
|
162
|
-
|
183
|
+
class << self
|
184
|
+
private
|
185
|
+
def retrieve_pool_options(options)
|
186
|
+
{}.tap do |pool_options|
|
187
|
+
pool_options[:size] = options.delete(:pool_size) if options[:pool_size]
|
188
|
+
pool_options[:timeout] = options.delete(:pool_timeout) if options[:pool_timeout]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def ensure_connection_pool_added!
|
193
|
+
require "connection_pool"
|
194
|
+
rescue LoadError => e
|
195
|
+
$stderr.puts "You don't have connection_pool installed in your application. Please add it to your Gemfile and run bundle install"
|
196
|
+
raise e
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Creates a new cache. The options will be passed to any write method calls
|
163
201
|
# except for <tt>:namespace</tt> which can be used to set the global
|
164
202
|
# namespace for the cache.
|
165
203
|
def initialize(options = nil)
|
166
|
-
@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)
|
167
210
|
end
|
168
211
|
|
169
|
-
#
|
212
|
+
# Silences the logger.
|
170
213
|
def silence!
|
171
214
|
@silence = true
|
172
215
|
self
|
173
216
|
end
|
174
217
|
|
175
|
-
#
|
218
|
+
# Silences the logger within a block.
|
176
219
|
def mute
|
177
220
|
previous_silence, @silence = defined?(@silence) && @silence, true
|
178
221
|
yield
|
@@ -180,16 +223,6 @@ module ActiveSupport
|
|
180
223
|
@silence = previous_silence
|
181
224
|
end
|
182
225
|
|
183
|
-
# Set to +true+ if cache stores should be instrumented.
|
184
|
-
# Default is +false+.
|
185
|
-
def self.instrument=(boolean)
|
186
|
-
Thread.current[:instrument_cache_store] = boolean
|
187
|
-
end
|
188
|
-
|
189
|
-
def self.instrument
|
190
|
-
Thread.current[:instrument_cache_store] || false
|
191
|
-
end
|
192
|
-
|
193
226
|
# Fetches data from the cache, using the given key. If there is data in
|
194
227
|
# the cache with the given key, then that data is returned.
|
195
228
|
#
|
@@ -209,26 +242,54 @@ module ActiveSupport
|
|
209
242
|
# cache.fetch('city') # => "Duckburgh"
|
210
243
|
#
|
211
244
|
# You may also specify additional options via the +options+ argument.
|
212
|
-
# Setting <tt>force: true</tt>
|
245
|
+
# Setting <tt>force: true</tt> forces a cache "miss," meaning we treat
|
246
|
+
# the cache value as missing even if it's present. Passing a block is
|
247
|
+
# required when +force+ is true so this always results in a cache write.
|
213
248
|
#
|
214
249
|
# cache.write('today', 'Monday')
|
215
|
-
# cache.fetch('today', force: true)
|
250
|
+
# cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday'
|
251
|
+
# cache.fetch('today', force: true) # => ArgumentError
|
252
|
+
#
|
253
|
+
# The +:force+ option is useful when you're calling some other method to
|
254
|
+
# ask whether you should force a cache write. Otherwise, it's clearer to
|
255
|
+
# just call <tt>Cache#write</tt>.
|
256
|
+
#
|
257
|
+
# Setting <tt>skip_nil: true</tt> will not cache nil result:
|
216
258
|
#
|
217
|
-
#
|
218
|
-
#
|
259
|
+
# cache.fetch('foo') { nil }
|
260
|
+
# cache.fetch('bar', skip_nil: true) { nil }
|
261
|
+
# cache.exist?('foo') # => true
|
262
|
+
# cache.exist?('bar') # => false
|
263
|
+
#
|
264
|
+
#
|
265
|
+
# Setting <tt>compress: false</tt> disables compression of the cache entry.
|
219
266
|
#
|
220
267
|
# Setting <tt>:expires_in</tt> will set an expiration time on the cache.
|
221
268
|
# All caches support auto-expiring content after a specified number of
|
222
269
|
# seconds. This value can be specified as an option to the constructor
|
223
270
|
# (in which case all entries will be affected), or it can be supplied to
|
224
|
-
# 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>.
|
225
274
|
#
|
226
275
|
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
|
227
276
|
# cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
|
228
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
|
+
#
|
286
|
+
# Setting <tt>:version</tt> verifies the cache stored under <tt>name</tt>
|
287
|
+
# is of the same version. nil is returned on mismatches despite contents.
|
288
|
+
# This feature is used to support recyclable cache keys.
|
289
|
+
#
|
229
290
|
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where
|
230
291
|
# a cache entry is used very frequently and is under heavy load. If a
|
231
|
-
# cache expires and due to heavy load
|
292
|
+
# cache expires and due to heavy load several different processes will try
|
232
293
|
# to read data natively and then they all will try to write to cache. To
|
233
294
|
# avoid that case the first process to find an expired cache entry will
|
234
295
|
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.
|
@@ -236,7 +297,7 @@ module ActiveSupport
|
|
236
297
|
# seconds. Because of extended life of the previous cache, other processes
|
237
298
|
# will continue to use slightly stale data for a just a bit longer. In the
|
238
299
|
# meantime that first process will go ahead and will write into cache the
|
239
|
-
# new value. After that all the processes will start getting new value.
|
300
|
+
# new value. After that all the processes will start getting the new value.
|
240
301
|
# The key is to keep <tt>:race_condition_ttl</tt> small.
|
241
302
|
#
|
242
303
|
# If the process regenerating the entry errors out, the entry will be
|
@@ -254,22 +315,23 @@ module ActiveSupport
|
|
254
315
|
# sleep 60
|
255
316
|
#
|
256
317
|
# Thread.new do
|
257
|
-
# val_1 = cache.fetch('foo', race_condition_ttl: 10) do
|
318
|
+
# val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
|
258
319
|
# sleep 1
|
259
320
|
# 'new value 1'
|
260
321
|
# end
|
261
322
|
# end
|
262
323
|
#
|
263
324
|
# Thread.new do
|
264
|
-
# val_2 = cache.fetch('foo', race_condition_ttl: 10) do
|
325
|
+
# val_2 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
|
265
326
|
# 'new value 2'
|
266
327
|
# end
|
267
328
|
# end
|
268
329
|
#
|
269
|
-
# #
|
270
|
-
# #
|
271
|
-
#
|
272
|
-
# #
|
330
|
+
# cache.fetch('foo') # => "original value"
|
331
|
+
# sleep 10 # First thread extended the life of cache by another 10 seconds
|
332
|
+
# cache.fetch('foo') # => "new value 1"
|
333
|
+
# val_1 # => "new value 1"
|
334
|
+
# val_2 # => "original value"
|
273
335
|
#
|
274
336
|
# Other options will be handled by the specific cache store implementation.
|
275
337
|
# Internally, #fetch calls #read_entry, and calls #write_entry on a cache
|
@@ -284,37 +346,55 @@ module ActiveSupport
|
|
284
346
|
# :bar
|
285
347
|
# end
|
286
348
|
# cache.fetch('foo') # => "bar"
|
287
|
-
def fetch(name, options = nil)
|
349
|
+
def fetch(name, options = nil, &block)
|
288
350
|
if block_given?
|
289
351
|
options = merged_options(options)
|
290
|
-
key =
|
352
|
+
key = normalize_key(name, options)
|
291
353
|
|
292
|
-
|
293
|
-
|
354
|
+
entry = nil
|
355
|
+
instrument(:read, name, options) do |payload|
|
356
|
+
cached_entry = read_entry(key, **options, event: payload) unless options[:force]
|
357
|
+
entry = handle_expired_entry(cached_entry, key, options)
|
358
|
+
entry = nil if entry && entry.mismatched?(normalize_version(name, options))
|
359
|
+
payload[:super_operation] = :fetch if payload
|
360
|
+
payload[:hit] = !!entry if payload
|
361
|
+
end
|
294
362
|
|
295
363
|
if entry
|
296
364
|
get_entry_value(entry, name, options)
|
297
365
|
else
|
298
|
-
save_block_result_to_cache(name, options)
|
366
|
+
save_block_result_to_cache(name, options, &block)
|
299
367
|
end
|
368
|
+
elsif options && options[:force]
|
369
|
+
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
|
300
370
|
else
|
301
371
|
read(name, options)
|
302
372
|
end
|
303
373
|
end
|
304
374
|
|
305
|
-
#
|
375
|
+
# Reads data from the cache, using the given key. If there is data in
|
306
376
|
# the cache with the given key, then that data is returned. Otherwise,
|
307
377
|
# +nil+ is returned.
|
308
378
|
#
|
379
|
+
# Note, if data was written with the <tt>:expires_in</tt> or
|
380
|
+
# <tt>:version</tt> options, both of these conditions are applied before
|
381
|
+
# the data is returned.
|
382
|
+
#
|
309
383
|
# Options are passed to the underlying cache implementation.
|
310
384
|
def read(name, options = nil)
|
311
385
|
options = merged_options(options)
|
312
|
-
key
|
386
|
+
key = normalize_key(name, options)
|
387
|
+
version = normalize_version(name, options)
|
388
|
+
|
313
389
|
instrument(:read, name, options) do |payload|
|
314
|
-
entry = read_entry(key, options)
|
390
|
+
entry = read_entry(key, **options, event: payload)
|
391
|
+
|
315
392
|
if entry
|
316
393
|
if entry.expired?
|
317
|
-
delete_entry(key, options)
|
394
|
+
delete_entry(key, **options)
|
395
|
+
payload[:hit] = false if payload
|
396
|
+
nil
|
397
|
+
elsif entry.mismatched?(version)
|
318
398
|
payload[:hit] = false if payload
|
319
399
|
nil
|
320
400
|
else
|
@@ -328,7 +408,7 @@ module ActiveSupport
|
|
328
408
|
end
|
329
409
|
end
|
330
410
|
|
331
|
-
#
|
411
|
+
# Reads multiple values at once from the cache. Options can be passed
|
332
412
|
# in the last argument.
|
333
413
|
#
|
334
414
|
# Some cache implementation may optimize this method.
|
@@ -337,19 +417,75 @@ module ActiveSupport
|
|
337
417
|
def read_multi(*names)
|
338
418
|
options = names.extract_options!
|
339
419
|
options = merged_options(options)
|
340
|
-
|
341
|
-
names
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
420
|
+
|
421
|
+
instrument :read_multi, names, options do |payload|
|
422
|
+
read_multi_entries(names, **options, event: payload).tap do |results|
|
423
|
+
payload[:hits] = results.keys
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
# Cache Storage API to write multiple values at once.
|
429
|
+
def write_multi(hash, options = nil)
|
430
|
+
options = merged_options(options)
|
431
|
+
|
432
|
+
instrument :write_multi, hash, options do |payload|
|
433
|
+
entries = hash.each_with_object({}) do |(name, value), memo|
|
434
|
+
memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
350
435
|
end
|
436
|
+
|
437
|
+
write_multi_entries entries, **options
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
# Fetches data from the cache, using the given keys. If there is data in
|
442
|
+
# the cache with the given keys, then that data is returned. Otherwise,
|
443
|
+
# the supplied block is called for each key for which there was no data,
|
444
|
+
# and the result will be written to the cache and returned.
|
445
|
+
# Therefore, you need to pass a block that returns the data to be written
|
446
|
+
# to the cache. If you do not want to write the cache when the cache is
|
447
|
+
# not found, use #read_multi.
|
448
|
+
#
|
449
|
+
# Returns a hash with the data for each of the names. For example:
|
450
|
+
#
|
451
|
+
# cache.write("bim", "bam")
|
452
|
+
# cache.fetch_multi("bim", "unknown_key") do |key|
|
453
|
+
# "Fallback value for key: #{key}"
|
454
|
+
# end
|
455
|
+
# # => { "bim" => "bam",
|
456
|
+
# # "unknown_key" => "Fallback value for key: unknown_key" }
|
457
|
+
#
|
458
|
+
# Options are passed to the underlying cache implementation. For example:
|
459
|
+
#
|
460
|
+
# cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
|
461
|
+
# "buzz"
|
462
|
+
# end
|
463
|
+
# # => {"fizz"=>"buzz"}
|
464
|
+
# cache.read("fizz")
|
465
|
+
# # => "buzz"
|
466
|
+
# sleep(6)
|
467
|
+
# cache.read("fizz")
|
468
|
+
# # => nil
|
469
|
+
def fetch_multi(*names)
|
470
|
+
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
|
471
|
+
|
472
|
+
options = names.extract_options!
|
473
|
+
options = merged_options(options)
|
474
|
+
|
475
|
+
instrument :read_multi, names, options do |payload|
|
476
|
+
reads = read_multi_entries(names, **options)
|
477
|
+
writes = {}
|
478
|
+
ordered = names.index_with do |name|
|
479
|
+
reads.fetch(name) { writes[name] = yield(name) }
|
480
|
+
end
|
481
|
+
|
482
|
+
payload[:hits] = reads.keys
|
483
|
+
payload[:super_operation] = :fetch_multi
|
484
|
+
|
485
|
+
write_multi(writes, options)
|
486
|
+
|
487
|
+
ordered
|
351
488
|
end
|
352
|
-
results
|
353
489
|
end
|
354
490
|
|
355
491
|
# Writes the value to the cache, with the key.
|
@@ -357,9 +493,10 @@ module ActiveSupport
|
|
357
493
|
# Options are passed to the underlying cache implementation.
|
358
494
|
def write(name, value, options = nil)
|
359
495
|
options = merged_options(options)
|
496
|
+
|
360
497
|
instrument(:write, name, options) do
|
361
|
-
entry = Entry.new(value, options)
|
362
|
-
write_entry(
|
498
|
+
entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
|
499
|
+
write_entry(normalize_key(name, options), entry, **options)
|
363
500
|
end
|
364
501
|
end
|
365
502
|
|
@@ -368,78 +505,100 @@ module ActiveSupport
|
|
368
505
|
# Options are passed to the underlying cache implementation.
|
369
506
|
def delete(name, options = nil)
|
370
507
|
options = merged_options(options)
|
508
|
+
|
371
509
|
instrument(:delete, name) do
|
372
|
-
delete_entry(
|
510
|
+
delete_entry(normalize_key(name, options), **options)
|
511
|
+
end
|
512
|
+
end
|
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)
|
373
523
|
end
|
374
524
|
end
|
375
525
|
|
376
|
-
#
|
526
|
+
# Returns +true+ if the cache contains an entry for the given key.
|
377
527
|
#
|
378
528
|
# Options are passed to the underlying cache implementation.
|
379
529
|
def exist?(name, options = nil)
|
380
530
|
options = merged_options(options)
|
381
|
-
|
382
|
-
|
383
|
-
entry
|
531
|
+
|
532
|
+
instrument(:exist?, name) do |payload|
|
533
|
+
entry = read_entry(normalize_key(name, options), **options, event: payload)
|
534
|
+
(entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
|
384
535
|
end
|
385
536
|
end
|
386
537
|
|
387
|
-
|
538
|
+
def new_entry(value, options = nil) # :nodoc:
|
539
|
+
Entry.new(value, **merged_options(options))
|
540
|
+
end
|
541
|
+
|
542
|
+
# Deletes all entries with keys matching the pattern.
|
388
543
|
#
|
389
544
|
# Options are passed to the underlying cache implementation.
|
390
545
|
#
|
391
|
-
#
|
546
|
+
# Some implementations may not support this method.
|
392
547
|
def delete_matched(matcher, options = nil)
|
393
548
|
raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
|
394
549
|
end
|
395
550
|
|
396
|
-
#
|
551
|
+
# Increments an integer value in the cache.
|
397
552
|
#
|
398
553
|
# Options are passed to the underlying cache implementation.
|
399
554
|
#
|
400
|
-
#
|
555
|
+
# Some implementations may not support this method.
|
401
556
|
def increment(name, amount = 1, options = nil)
|
402
557
|
raise NotImplementedError.new("#{self.class.name} does not support increment")
|
403
558
|
end
|
404
559
|
|
405
|
-
#
|
560
|
+
# Decrements an integer value in the cache.
|
406
561
|
#
|
407
562
|
# Options are passed to the underlying cache implementation.
|
408
563
|
#
|
409
|
-
#
|
564
|
+
# Some implementations may not support this method.
|
410
565
|
def decrement(name, amount = 1, options = nil)
|
411
566
|
raise NotImplementedError.new("#{self.class.name} does not support decrement")
|
412
567
|
end
|
413
568
|
|
414
|
-
#
|
569
|
+
# Cleanups the cache by removing expired entries.
|
415
570
|
#
|
416
571
|
# Options are passed to the underlying cache implementation.
|
417
572
|
#
|
418
|
-
#
|
573
|
+
# Some implementations may not support this method.
|
419
574
|
def cleanup(options = nil)
|
420
575
|
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
|
421
576
|
end
|
422
577
|
|
423
|
-
#
|
578
|
+
# Clears the entire cache. Be careful with this method since it could
|
424
579
|
# affect other processes if shared cache is being used.
|
425
580
|
#
|
426
|
-
#
|
581
|
+
# The options hash is passed to the underlying cache implementation.
|
427
582
|
#
|
428
|
-
#
|
583
|
+
# Some implementations may not support this method.
|
429
584
|
def clear(options = nil)
|
430
585
|
raise NotImplementedError.new("#{self.class.name} does not support clear")
|
431
586
|
end
|
432
587
|
|
433
|
-
|
434
|
-
|
588
|
+
private
|
589
|
+
def default_coder
|
590
|
+
Coders[Cache.format_version]
|
591
|
+
end
|
592
|
+
|
593
|
+
# Adds the namespace defined in the options to a pattern designed to
|
435
594
|
# match keys. Implementations that support delete_matched should call
|
436
595
|
# this method to translate a pattern that matches names into one that
|
437
596
|
# matches namespaced keys.
|
438
|
-
def key_matcher(pattern, options)
|
597
|
+
def key_matcher(pattern, options) # :doc:
|
439
598
|
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
|
440
599
|
if prefix
|
441
600
|
source = pattern.source
|
442
|
-
if source.start_with?(
|
601
|
+
if source.start_with?("^")
|
443
602
|
source = source[1, source.length]
|
444
603
|
else
|
445
604
|
source = ".*#{source[0, source.length]}"
|
@@ -450,98 +609,182 @@ module ActiveSupport
|
|
450
609
|
end
|
451
610
|
end
|
452
611
|
|
453
|
-
#
|
612
|
+
# Reads an entry from the cache implementation. Subclasses must implement
|
454
613
|
# this method.
|
455
|
-
def read_entry(key, options)
|
614
|
+
def read_entry(key, **options)
|
456
615
|
raise NotImplementedError.new
|
457
616
|
end
|
458
617
|
|
459
|
-
#
|
618
|
+
# Writes an entry to the cache implementation. Subclasses must implement
|
460
619
|
# this method.
|
461
|
-
def write_entry(key, entry, options)
|
620
|
+
def write_entry(key, entry, **options)
|
462
621
|
raise NotImplementedError.new
|
463
622
|
end
|
464
623
|
|
465
|
-
|
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
|
+
|
637
|
+
# Reads multiple entries from the cache implementation. Subclasses MAY
|
466
638
|
# implement this method.
|
467
|
-
def
|
639
|
+
def read_multi_entries(names, **options)
|
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
|
+
|
646
|
+
version = normalize_version(name, options)
|
647
|
+
|
648
|
+
if entry.expired?
|
649
|
+
delete_entry(key, **options)
|
650
|
+
elsif !entry.mismatched?(version)
|
651
|
+
results[name] = entry.value
|
652
|
+
end
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
# Writes multiple entries to the cache implementation. Subclasses MAY
|
657
|
+
# implement this method.
|
658
|
+
def write_multi_entries(hash, **options)
|
659
|
+
hash.each do |key, entry|
|
660
|
+
write_entry key, entry, **options
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
# Deletes an entry from the cache implementation. Subclasses must
|
665
|
+
# implement this method.
|
666
|
+
def delete_entry(key, **options)
|
468
667
|
raise NotImplementedError.new
|
469
668
|
end
|
470
669
|
|
471
|
-
|
472
|
-
#
|
473
|
-
def
|
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
|
+
|
676
|
+
# Merges the default options with ones specific to a method call.
|
677
|
+
def merged_options(call_options)
|
474
678
|
if call_options
|
475
|
-
|
679
|
+
call_options = normalize_options(call_options)
|
680
|
+
if options.empty?
|
681
|
+
call_options
|
682
|
+
else
|
683
|
+
options.merge(call_options)
|
684
|
+
end
|
476
685
|
else
|
477
|
-
options
|
686
|
+
options
|
478
687
|
end
|
479
688
|
end
|
480
689
|
|
481
|
-
#
|
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
|
+
|
702
|
+
# Expands and namespaces the cache key. May be overridden by
|
703
|
+
# cache stores to do additional normalization.
|
704
|
+
def normalize_key(key, options = nil)
|
705
|
+
namespace_key expanded_key(key), options
|
706
|
+
end
|
707
|
+
|
708
|
+
# Prefix the key with a namespace string:
|
709
|
+
#
|
710
|
+
# namespace_key 'foo', namespace: 'cache'
|
711
|
+
# # => 'cache:foo'
|
712
|
+
#
|
713
|
+
# With a namespace block:
|
714
|
+
#
|
715
|
+
# namespace_key 'foo', namespace: -> { 'cache' }
|
716
|
+
# # => 'cache:foo'
|
717
|
+
def namespace_key(key, options = nil)
|
718
|
+
options = merged_options(options)
|
719
|
+
namespace = options[:namespace]
|
720
|
+
|
721
|
+
if namespace.respond_to?(:call)
|
722
|
+
namespace = namespace.call
|
723
|
+
end
|
724
|
+
|
725
|
+
if key && key.encoding != Encoding::UTF_8
|
726
|
+
key = key.dup.force_encoding(Encoding::UTF_8)
|
727
|
+
end
|
728
|
+
|
729
|
+
if namespace
|
730
|
+
"#{namespace}:#{key}"
|
731
|
+
else
|
732
|
+
key
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
# Expands key to be a consistent string value. Invokes +cache_key+ if
|
482
737
|
# object responds to +cache_key+. Otherwise, +to_param+ method will be
|
483
738
|
# called. If the key is a Hash, then keys will be sorted alphabetically.
|
484
|
-
def expanded_key(key)
|
739
|
+
def expanded_key(key)
|
485
740
|
return key.cache_key.to_s if key.respond_to?(:cache_key)
|
486
741
|
|
487
742
|
case key
|
488
743
|
when Array
|
489
744
|
if key.size > 1
|
490
|
-
key
|
745
|
+
key.collect { |element| expanded_key(element) }
|
491
746
|
else
|
492
|
-
key
|
747
|
+
expanded_key(key.first)
|
493
748
|
end
|
494
749
|
when Hash
|
495
|
-
key
|
496
|
-
|
497
|
-
|
498
|
-
|
750
|
+
key.collect { |k, v| "#{k}=#{v}" }.sort!
|
751
|
+
else
|
752
|
+
key
|
753
|
+
end.to_param
|
499
754
|
end
|
500
755
|
|
501
|
-
|
502
|
-
|
503
|
-
def namespaced_key(key, options)
|
504
|
-
key = expanded_key(key)
|
505
|
-
namespace = options[:namespace] if options
|
506
|
-
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
|
507
|
-
key = "#{prefix}:#{key}" if prefix
|
508
|
-
key
|
756
|
+
def normalize_version(key, options = nil)
|
757
|
+
(options && options[:version].try(:to_param)) || expanded_version(key)
|
509
758
|
end
|
510
759
|
|
511
|
-
def
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
payload.merge!(options) if options.is_a?(Hash)
|
517
|
-
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
|
518
|
-
else
|
519
|
-
yield(nil)
|
760
|
+
def expanded_version(key)
|
761
|
+
case
|
762
|
+
when key.respond_to?(:cache_version) then key.cache_version.to_param
|
763
|
+
when key.is_a?(Array) then key.map { |element| expanded_version(element) }.tap(&:compact!).to_param
|
764
|
+
when key.respond_to?(:to_a) then expanded_version(key.to_a)
|
520
765
|
end
|
521
766
|
end
|
522
767
|
|
523
|
-
def
|
524
|
-
|
525
|
-
|
526
|
-
end
|
527
|
-
|
528
|
-
def find_cached_entry(key, name, options)
|
529
|
-
instrument(:read, name, options) do |payload|
|
530
|
-
payload[:super_operation] = :fetch if payload
|
531
|
-
read_entry(key, options)
|
768
|
+
def instrument(operation, key, options = nil)
|
769
|
+
if logger && logger.debug? && !silence?
|
770
|
+
logger.debug "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}"
|
532
771
|
end
|
772
|
+
|
773
|
+
payload = { key: key, store: self.class.name }
|
774
|
+
payload.merge!(options) if options.is_a?(Hash)
|
775
|
+
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
|
533
776
|
end
|
534
777
|
|
535
778
|
def handle_expired_entry(entry, key, options)
|
536
779
|
if entry && entry.expired?
|
537
780
|
race_ttl = options[:race_condition_ttl].to_i
|
538
|
-
if race_ttl && (Time.now.to_f - entry.expires_at <= race_ttl)
|
539
|
-
# When an entry has :race_condition_ttl defined, put the stale entry back into the cache
|
540
|
-
# for a brief period while the entry is
|
541
|
-
entry.expires_at = Time.now + race_ttl
|
542
|
-
write_entry(key, entry, :
|
781
|
+
if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
|
782
|
+
# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
|
783
|
+
# for a brief period while the entry is being recalculated.
|
784
|
+
entry.expires_at = Time.now.to_f + race_ttl
|
785
|
+
write_entry(key, entry, expires_in: race_ttl * 2)
|
543
786
|
else
|
544
|
-
delete_entry(key, options)
|
787
|
+
delete_entry(key, **options)
|
545
788
|
end
|
546
789
|
entry = nil
|
547
790
|
end
|
@@ -549,51 +792,149 @@ module ActiveSupport
|
|
549
792
|
end
|
550
793
|
|
551
794
|
def get_entry_value(entry, name, options)
|
552
|
-
instrument(:fetch_hit, name, options) {
|
795
|
+
instrument(:fetch_hit, name, options) { }
|
553
796
|
entry.value
|
554
797
|
end
|
555
798
|
|
556
799
|
def save_block_result_to_cache(name, options)
|
557
|
-
result = instrument(:generate, name, options) do
|
800
|
+
result = instrument(:generate, name, options) do
|
558
801
|
yield(name)
|
559
802
|
end
|
560
|
-
|
803
|
+
|
804
|
+
write(name, result, options) unless result.nil? && options[:skip_nil]
|
561
805
|
result
|
562
806
|
end
|
563
807
|
end
|
564
808
|
|
565
|
-
|
566
|
-
|
567
|
-
|
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
|
+
|
901
|
+
# This class is used to represent cache entries. Cache entries have a value, an optional
|
902
|
+
# expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
|
903
|
+
# on the cache. The version is used to support the :version option on the cache for rejecting
|
904
|
+
# mismatches.
|
568
905
|
#
|
569
906
|
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
|
570
907
|
# using short instance variable names that are lazily defined.
|
571
908
|
class Entry # :nodoc:
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
# +:compress+, +:compress_threshold+, and +:expires_in+.
|
576
|
-
def initialize(value, options = {})
|
577
|
-
if should_compress?(value, options)
|
578
|
-
@value = compress(value)
|
579
|
-
@compressed = true
|
580
|
-
else
|
581
|
-
@value = value
|
909
|
+
class << self
|
910
|
+
def unpack(members)
|
911
|
+
new(members[0], expires_at: members[1], version: members[2])
|
582
912
|
end
|
583
|
-
|
584
|
-
|
585
|
-
|
913
|
+
end
|
914
|
+
|
915
|
+
attr_reader :version
|
916
|
+
|
917
|
+
# Creates a new cache entry for the specified value. Options supported are
|
918
|
+
# +:compressed+, +:version+, +:expires_at+ and +:expires_in+.
|
919
|
+
def initialize(value, compressed: false, version: nil, expires_in: nil, expires_at: nil, **)
|
920
|
+
@value = value
|
921
|
+
@version = version
|
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
|
586
925
|
end
|
587
926
|
|
588
927
|
def value
|
589
|
-
convert_version_4beta1_entry! if defined?(@v)
|
590
928
|
compressed? ? uncompress(@value) : @value
|
591
929
|
end
|
592
930
|
|
593
|
-
|
931
|
+
def mismatched?(version)
|
932
|
+
@version && version && @version != version
|
933
|
+
end
|
934
|
+
|
935
|
+
# Checks if the entry is expired. The +expires_in+ parameter can override
|
594
936
|
# the value set when the entry was created.
|
595
937
|
def expired?
|
596
|
-
convert_version_4beta1_entry! if defined?(@value)
|
597
938
|
@expires_in && @created_at + @expires_in <= Time.now.to_f
|
598
939
|
end
|
599
940
|
|
@@ -610,26 +951,53 @@ module ActiveSupport
|
|
610
951
|
end
|
611
952
|
|
612
953
|
# Returns the size of the cached value. This could be less than
|
613
|
-
# <tt>value.
|
614
|
-
def
|
615
|
-
|
616
|
-
|
954
|
+
# <tt>value.bytesize</tt> if the data is compressed.
|
955
|
+
def bytesize
|
956
|
+
case value
|
957
|
+
when NilClass
|
958
|
+
0
|
959
|
+
when String
|
960
|
+
@value.bytesize
|
617
961
|
else
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
962
|
+
@s ||= Marshal.dump(@value).bytesize
|
963
|
+
end
|
964
|
+
end
|
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)
|
625
989
|
end
|
626
990
|
end
|
991
|
+
self
|
627
992
|
end
|
628
993
|
|
629
|
-
|
994
|
+
def local?
|
995
|
+
false
|
996
|
+
end
|
997
|
+
|
998
|
+
# Duplicates the value in a class. This is used by cache implementations that don't natively
|
630
999
|
# serialize entries to protect against accidental cache modifications.
|
631
1000
|
def dup_value!
|
632
|
-
convert_version_4beta1_entry! if defined?(@v)
|
633
1001
|
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
|
634
1002
|
if @value.is_a?(String)
|
635
1003
|
@value = @value.dup
|
@@ -639,45 +1007,16 @@ module ActiveSupport
|
|
639
1007
|
end
|
640
1008
|
end
|
641
1009
|
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
return true if serialized_value_size >= compress_threshold
|
648
|
-
end
|
649
|
-
false
|
650
|
-
end
|
651
|
-
|
652
|
-
def compressed?
|
653
|
-
defined?(@compressed) ? @compressed : false
|
654
|
-
end
|
655
|
-
|
656
|
-
def compress(value)
|
657
|
-
Zlib::Deflate.deflate(Marshal.dump(value))
|
658
|
-
end
|
1010
|
+
def pack
|
1011
|
+
members = [value, expires_at, version]
|
1012
|
+
members.pop while !members.empty? && members.last.nil?
|
1013
|
+
members
|
1014
|
+
end
|
659
1015
|
|
1016
|
+
private
|
660
1017
|
def uncompress(value)
|
661
1018
|
Marshal.load(Zlib::Inflate.inflate(value))
|
662
1019
|
end
|
663
|
-
|
664
|
-
# The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
|
665
|
-
# to ensure that cache entries created under the old version still work with the new class definition.
|
666
|
-
def convert_version_4beta1_entry!
|
667
|
-
if defined?(@v)
|
668
|
-
@value = @v
|
669
|
-
remove_instance_variable(:@v)
|
670
|
-
end
|
671
|
-
if defined?(@c)
|
672
|
-
@compressed = @c
|
673
|
-
remove_instance_variable(:@c)
|
674
|
-
end
|
675
|
-
if defined?(@x) && @x
|
676
|
-
@created_at ||= Time.now.to_f
|
677
|
-
@expires_in = @x - @created_at
|
678
|
-
remove_instance_variable(:@x)
|
679
|
-
end
|
680
|
-
end
|
681
1020
|
end
|
682
1021
|
end
|
683
1022
|
end
|