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