activesupport 3.1.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +798 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +13 -7
- data/lib/active_support/array_inquirer.rb +44 -0
- data/lib/active_support/backtrace_cleaner.rb +38 -34
- data/lib/active_support/benchmarkable.rb +17 -28
- data/lib/active_support/cache/file_store.rb +85 -70
- data/lib/active_support/cache/mem_cache_store.rb +75 -66
- data/lib/active_support/cache/memory_store.rb +31 -23
- data/lib/active_support/cache/null_store.rb +41 -0
- data/lib/active_support/cache/strategy/local_cache.rb +73 -70
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
- data/lib/active_support/cache.rb +360 -294
- data/lib/active_support/callbacks.rb +563 -393
- data/lib/active_support/concern.rb +42 -34
- data/lib/active_support/concurrency/latch.rb +19 -0
- data/lib/active_support/concurrency/share_lock.rb +186 -0
- data/lib/active_support/configurable.rb +70 -12
- data/lib/active_support/core_ext/array/access.rb +53 -9
- data/lib/active_support/core_ext/array/conversions.rb +109 -62
- data/lib/active_support/core_ext/array/extract_options.rb +2 -2
- data/lib/active_support/core_ext/array/grouping.rb +39 -32
- data/lib/active_support/core_ext/array/inquiry.rb +17 -0
- data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
- data/lib/active_support/core_ext/array/wrap.rb +16 -18
- data/lib/active_support/core_ext/array.rb +2 -2
- data/lib/active_support/core_ext/benchmark.rb +7 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -36
- data/lib/active_support/core_ext/class/attribute.rb +47 -34
- data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -79
- data/lib/active_support/core_ext/class/subclasses.rb +12 -7
- data/lib/active_support/core_ext/class.rb +0 -3
- data/lib/active_support/core_ext/date/blank.rb +12 -0
- data/lib/active_support/core_ext/date/calculations.rb +57 -167
- data/lib/active_support/core_ext/date/conversions.rb +31 -42
- data/lib/active_support/core_ext/date/zones.rb +2 -10
- data/lib/active_support/core_ext/date.rb +5 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -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 +1 -0
- data/lib/active_support/core_ext/date_time/blank.rb +12 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +132 -65
- data/lib/active_support/core_ext/date_time/compatibility.rb +5 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +36 -34
- data/lib/active_support/core_ext/date_time.rb +5 -0
- data/lib/active_support/core_ext/digest/uuid.rb +51 -0
- data/lib/active_support/core_ext/enumerable.rb +81 -74
- data/lib/active_support/core_ext/file/atomic.rb +53 -26
- data/lib/active_support/core_ext/file.rb +0 -1
- data/lib/active_support/core_ext/hash/compact.rb +20 -0
- data/lib/active_support/core_ext/hash/conversions.rb +175 -70
- data/lib/active_support/core_ext/hash/deep_merge.rb +30 -8
- data/lib/active_support/core_ext/hash/except.rb +11 -12
- data/lib/active_support/core_ext/hash/indifferent_access.rb +7 -8
- data/lib/active_support/core_ext/hash/keys.rb +147 -24
- data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
- data/lib/active_support/core_ext/hash/slice.rb +22 -14
- data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
- data/lib/active_support/core_ext/hash.rb +2 -2
- data/lib/active_support/core_ext/integer/inflections.rb +13 -1
- data/lib/active_support/core_ext/integer/multiple.rb +4 -0
- data/lib/active_support/core_ext/integer/time.rb +12 -22
- data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
- data/lib/active_support/core_ext/kernel/concern.rb +12 -0
- data/lib/active_support/core_ext/kernel/debugger.rb +2 -15
- data/lib/active_support/core_ext/kernel/reporting.rb +12 -62
- data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
- data/lib/active_support/core_ext/kernel.rb +2 -3
- data/lib/active_support/core_ext/load_error.rb +14 -7
- data/lib/active_support/core_ext/marshal.rb +22 -0
- data/lib/active_support/core_ext/module/aliasing.rb +16 -12
- data/lib/active_support/core_ext/module/anonymous.rb +12 -8
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
- data/lib/active_support/core_ext/module/attribute_accessors.rb +165 -13
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
- data/lib/active_support/core_ext/module/concerning.rb +135 -0
- data/lib/active_support/core_ext/module/delegation.rb +141 -68
- data/lib/active_support/core_ext/module/deprecation.rb +17 -3
- data/lib/active_support/core_ext/module/introspection.rb +9 -31
- data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
- data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
- data/lib/active_support/core_ext/module/reachable.rb +1 -3
- data/lib/active_support/core_ext/module/remove_method.rb +24 -5
- data/lib/active_support/core_ext/module.rb +3 -3
- data/lib/active_support/core_ext/name_error.rb +15 -2
- data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
- data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
- data/lib/active_support/core_ext/numeric/time.rb +31 -36
- data/lib/active_support/core_ext/numeric.rb +2 -0
- data/lib/active_support/core_ext/object/acts_like.rb +4 -4
- data/lib/active_support/core_ext/object/blank.rb +52 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
- data/lib/active_support/core_ext/object/duplicable.rb +12 -20
- data/lib/active_support/core_ext/object/inclusion.rb +13 -1
- data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
- data/lib/active_support/core_ext/object/json.rb +205 -0
- data/lib/active_support/core_ext/object/to_param.rb +1 -55
- data/lib/active_support/core_ext/object/to_query.rb +66 -9
- data/lib/active_support/core_ext/object/try.rb +124 -33
- data/lib/active_support/core_ext/object/with_options.rb +37 -11
- data/lib/active_support/core_ext/object.rb +2 -1
- data/lib/active_support/core_ext/range/conversions.rb +17 -7
- data/lib/active_support/core_ext/range/each.rb +21 -0
- data/lib/active_support/core_ext/range/include_range.rb +20 -18
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +23 -0
- data/lib/active_support/core_ext/string/access.rb +95 -90
- data/lib/active_support/core_ext/string/behavior.rb +1 -1
- data/lib/active_support/core_ext/string/conversions.rb +41 -38
- data/lib/active_support/core_ext/string/exclude.rb +6 -1
- data/lib/active_support/core_ext/string/filters.rb +70 -17
- data/lib/active_support/core_ext/string/indent.rb +43 -0
- data/lib/active_support/core_ext/string/inflections.rb +139 -59
- data/lib/active_support/core_ext/string/inquiry.rb +2 -2
- data/lib/active_support/core_ext/string/multibyte.rb +46 -65
- data/lib/active_support/core_ext/string/output_safety.rb +153 -56
- data/lib/active_support/core_ext/string/strip.rb +3 -6
- data/lib/active_support/core_ext/string/zones.rb +14 -0
- data/lib/active_support/core_ext/string.rb +2 -3
- data/lib/active_support/core_ext/struct.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +173 -173
- data/lib/active_support/core_ext/time/compatibility.rb +5 -0
- data/lib/active_support/core_ext/time/conversions.rb +33 -29
- data/lib/active_support/core_ext/time/marshal.rb +2 -56
- data/lib/active_support/core_ext/time/zones.rb +57 -32
- data/lib/active_support/core_ext/time.rb +5 -0
- data/lib/active_support/core_ext/uri.rb +13 -19
- data/lib/active_support/core_ext.rb +3 -2
- data/lib/active_support/dependencies/autoload.rb +47 -20
- data/lib/active_support/dependencies/interlock.rb +51 -0
- data/lib/active_support/dependencies.rb +315 -265
- data/lib/active_support/deprecation/behaviors.rb +71 -30
- data/lib/active_support/deprecation/instance_delegator.rb +24 -0
- data/lib/active_support/deprecation/method_wrappers.rb +59 -18
- data/lib/active_support/deprecation/proxy_wrappers.rb +82 -14
- data/lib/active_support/deprecation/reporting.rb +61 -14
- data/lib/active_support/deprecation.rb +38 -13
- data/lib/active_support/descendants_tracker.rb +34 -19
- data/lib/active_support/duration/iso8601_parser.rb +122 -0
- data/lib/active_support/duration/iso8601_serializer.rb +51 -0
- data/lib/active_support/duration.rb +85 -14
- data/lib/active_support/evented_file_update_checker.rb +194 -0
- data/lib/active_support/execution_wrapper.rb +117 -0
- data/lib/active_support/executor.rb +6 -0
- data/lib/active_support/file_update_checker.rb +138 -17
- data/lib/active_support/gem_version.rb +15 -0
- data/lib/active_support/gzip.rb +11 -5
- data/lib/active_support/hash_with_indifferent_access.rb +199 -49
- data/lib/active_support/i18n.rb +6 -2
- data/lib/active_support/i18n_railtie.rb +40 -21
- data/lib/active_support/inflections.rb +22 -13
- data/lib/active_support/inflector/inflections.rb +175 -144
- data/lib/active_support/inflector/methods.rb +328 -91
- data/lib/active_support/inflector/transliterate.rb +51 -37
- data/lib/active_support/json/decoding.rb +31 -22
- data/lib/active_support/json/encoding.rb +88 -248
- data/lib/active_support/key_generator.rb +71 -0
- data/lib/active_support/lazy_load_hooks.rb +27 -25
- data/lib/active_support/locale/en.yml +102 -3
- data/lib/active_support/log_subscriber/test_helper.rb +24 -21
- data/lib/active_support/log_subscriber.rb +36 -49
- data/lib/active_support/logger.rb +106 -0
- data/lib/active_support/logger_silence.rb +28 -0
- data/lib/active_support/logger_thread_safe_level.rb +31 -0
- data/lib/active_support/message_encryptor.rb +72 -36
- data/lib/active_support/message_verifier.rb +96 -24
- data/lib/active_support/multibyte/chars.rb +88 -333
- data/lib/active_support/multibyte/unicode.rb +156 -136
- data/lib/active_support/multibyte.rb +5 -28
- data/lib/active_support/notifications/fanout.rb +115 -19
- data/lib/active_support/notifications/instrumenter.rb +52 -15
- data/lib/active_support/notifications.rb +168 -33
- data/lib/active_support/number_helper/number_converter.rb +182 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
- data/lib/active_support/number_helper.rb +368 -0
- data/lib/active_support/option_merger.rb +1 -1
- data/lib/active_support/ordered_hash.rb +18 -183
- data/lib/active_support/ordered_options.rb +44 -24
- data/lib/active_support/per_thread_registry.rb +58 -0
- data/lib/active_support/proxy_object.rb +13 -0
- data/lib/active_support/rails.rb +27 -0
- data/lib/active_support/railtie.rb +25 -34
- data/lib/active_support/reloader.rb +129 -0
- data/lib/active_support/rescuable.rb +98 -48
- data/lib/active_support/security_utils.rb +27 -0
- data/lib/active_support/string_inquirer.rb +14 -9
- data/lib/active_support/subscriber.rb +120 -0
- data/lib/active_support/tagged_logging.rb +78 -0
- data/lib/active_support/test_case.rb +69 -17
- data/lib/active_support/testing/assertions.rb +43 -41
- data/lib/active_support/testing/autorun.rb +12 -0
- data/lib/active_support/testing/constant_lookup.rb +50 -0
- data/lib/active_support/testing/declarative.rb +7 -21
- data/lib/active_support/testing/deprecation.rb +14 -33
- data/lib/active_support/testing/file_fixtures.rb +34 -0
- data/lib/active_support/testing/isolation.rb +53 -95
- data/lib/active_support/testing/method_call_assertions.rb +41 -0
- data/lib/active_support/testing/setup_and_teardown.rb +21 -82
- data/lib/active_support/testing/stream.rb +42 -0
- data/lib/active_support/testing/tagged_logging.rb +25 -0
- data/lib/active_support/testing/time_helpers.rb +134 -0
- data/lib/active_support/time.rb +6 -23
- data/lib/active_support/time_with_zone.rb +239 -92
- data/lib/active_support/values/time_zone.rb +236 -160
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +5 -7
- data/lib/active_support/xml_mini/jdom.rb +19 -13
- data/lib/active_support/xml_mini/libxml.rb +3 -4
- data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
- data/lib/active_support/xml_mini/nokogiri.rb +3 -4
- data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
- data/lib/active_support/xml_mini/rexml.rb +8 -10
- data/lib/active_support/xml_mini.rb +66 -34
- data/lib/active_support.rb +40 -23
- metadata +185 -134
- data/CHANGELOG +0 -1534
- data/lib/active_support/base64.rb +0 -42
- data/lib/active_support/basic_object.rb +0 -21
- data/lib/active_support/buffered_logger.rb +0 -137
- data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
- data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
- data/lib/active_support/core_ext/array/random_access.rb +0 -30
- data/lib/active_support/core_ext/array/uniq_by.rb +0 -16
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -44
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
- data/lib/active_support/core_ext/date/freeze.rb +0 -31
- data/lib/active_support/core_ext/date_time/zones.rb +0 -21
- data/lib/active_support/core_ext/exception.rb +0 -3
- data/lib/active_support/core_ext/file/path.rb +0 -5
- data/lib/active_support/core_ext/float/rounding.rb +0 -19
- data/lib/active_support/core_ext/float.rb +0 -1
- data/lib/active_support/core_ext/hash/deep_dup.rb +0 -11
- data/lib/active_support/core_ext/hash/diff.rb +0 -13
- data/lib/active_support/core_ext/kernel/requires.rb +0 -28
- data/lib/active_support/core_ext/logger.rb +0 -81
- data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
- data/lib/active_support/core_ext/module/method_names.rb +0 -14
- data/lib/active_support/core_ext/module/synchronization.rb +0 -43
- data/lib/active_support/core_ext/object/to_json.rb +0 -19
- data/lib/active_support/core_ext/proc.rb +0 -14
- data/lib/active_support/core_ext/process/daemon.rb +0 -23
- data/lib/active_support/core_ext/process.rb +0 -1
- data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
- data/lib/active_support/core_ext/range/cover.rb +0 -3
- data/lib/active_support/core_ext/rexml.rb +0 -46
- data/lib/active_support/core_ext/string/encoding.rb +0 -11
- data/lib/active_support/core_ext/string/interpolation.rb +0 -2
- data/lib/active_support/core_ext/string/xchar.rb +0 -18
- data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
- data/lib/active_support/file_watcher.rb +0 -36
- data/lib/active_support/json/variable.rb +0 -9
- data/lib/active_support/memoizable.rb +0 -105
- data/lib/active_support/multibyte/exceptions.rb +0 -8
- data/lib/active_support/multibyte/utils.rb +0 -60
- data/lib/active_support/ruby/shim.rb +0 -22
- data/lib/active_support/secure_random.rb +0 -6
- data/lib/active_support/testing/mochaing.rb +0 -7
- data/lib/active_support/testing/pending.rb +0 -52
- data/lib/active_support/testing/performance/jruby.rb +0 -115
- data/lib/active_support/testing/performance/rubinius.rb +0 -113
- data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
- data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
- data/lib/active_support/testing/performance/ruby.rb +0 -152
- data/lib/active_support/testing/performance.rb +0 -317
- data/lib/active_support/time/autoload.rb +0 -5
- data/lib/active_support/whiny_nil.rb +0 -60
data/lib/active_support/cache.rb
CHANGED
@@ -3,21 +3,20 @@ require 'zlib'
|
|
3
3
|
require 'active_support/core_ext/array/extract_options'
|
4
4
|
require 'active_support/core_ext/array/wrap'
|
5
5
|
require 'active_support/core_ext/benchmark'
|
6
|
-
require 'active_support/core_ext/
|
7
|
-
require 'active_support/core_ext/class/attribute_accessors'
|
6
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
8
7
|
require 'active_support/core_ext/numeric/bytes'
|
9
8
|
require 'active_support/core_ext/numeric/time'
|
10
9
|
require 'active_support/core_ext/object/to_param'
|
11
10
|
require 'active_support/core_ext/string/inflections'
|
11
|
+
require 'active_support/core_ext/string/strip'
|
12
12
|
|
13
13
|
module ActiveSupport
|
14
14
|
# See ActiveSupport::Cache::Store for documentation.
|
15
15
|
module Cache
|
16
|
-
autoload :FileStore,
|
17
|
-
autoload :MemoryStore,
|
16
|
+
autoload :FileStore, 'active_support/cache/file_store'
|
17
|
+
autoload :MemoryStore, 'active_support/cache/memory_store'
|
18
18
|
autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
|
19
|
-
autoload :
|
20
|
-
autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store'
|
19
|
+
autoload :NullStore, 'active_support/cache/null_store'
|
21
20
|
|
22
21
|
# These options mean something to all cache implementations. Individual cache
|
23
22
|
# implementations may support additional options.
|
@@ -27,75 +26,87 @@ module ActiveSupport
|
|
27
26
|
autoload :LocalCache, 'active_support/cache/strategy/local_cache'
|
28
27
|
end
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
ActiveSupport::Cache.const_get(store_class_name)
|
68
|
-
end
|
69
|
-
store_class.new(*parameters)
|
70
|
-
when nil
|
71
|
-
ActiveSupport::Cache::MemoryStore.new
|
72
|
-
else
|
73
|
-
store
|
29
|
+
class << self
|
30
|
+
# Creates a new Store object according to the given options.
|
31
|
+
#
|
32
|
+
# If no arguments are passed to this method, then a new
|
33
|
+
# ActiveSupport::Cache::MemoryStore object will be returned.
|
34
|
+
#
|
35
|
+
# If you pass a Symbol as the first argument, then a corresponding cache
|
36
|
+
# store class under the ActiveSupport::Cache namespace will be created.
|
37
|
+
# For example:
|
38
|
+
#
|
39
|
+
# ActiveSupport::Cache.lookup_store(:memory_store)
|
40
|
+
# # => returns a new ActiveSupport::Cache::MemoryStore object
|
41
|
+
#
|
42
|
+
# ActiveSupport::Cache.lookup_store(:mem_cache_store)
|
43
|
+
# # => returns a new ActiveSupport::Cache::MemCacheStore object
|
44
|
+
#
|
45
|
+
# Any additional arguments will be passed to the corresponding cache store
|
46
|
+
# class's constructor:
|
47
|
+
#
|
48
|
+
# ActiveSupport::Cache.lookup_store(:file_store, '/tmp/cache')
|
49
|
+
# # => same as: ActiveSupport::Cache::FileStore.new('/tmp/cache')
|
50
|
+
#
|
51
|
+
# If the first argument is not a Symbol, then it will simply be returned:
|
52
|
+
#
|
53
|
+
# ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
|
54
|
+
# # => returns MyOwnCacheStore.new
|
55
|
+
def lookup_store(*store_option)
|
56
|
+
store, *parameters = *Array.wrap(store_option).flatten
|
57
|
+
|
58
|
+
case store
|
59
|
+
when Symbol
|
60
|
+
retrieve_store_class(store).new(*parameters)
|
61
|
+
when nil
|
62
|
+
ActiveSupport::Cache::MemoryStore.new
|
63
|
+
else
|
64
|
+
store
|
65
|
+
end
|
74
66
|
end
|
75
|
-
end
|
76
67
|
|
77
|
-
|
78
|
-
|
68
|
+
# Expands out the +key+ argument into a key that can be used for the
|
69
|
+
# cache store. Optionally accepts a namespace, and all keys will be
|
70
|
+
# scoped within that namespace.
|
71
|
+
#
|
72
|
+
# If the +key+ argument provided is an array, or responds to +to_a+, then
|
73
|
+
# each of elements in the array will be turned into parameters/keys and
|
74
|
+
# concatenated into a single key. For example:
|
75
|
+
#
|
76
|
+
# expand_cache_key([:foo, :bar]) # => "foo/bar"
|
77
|
+
# expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
|
78
|
+
#
|
79
|
+
# The +key+ argument can also respond to +cache_key+ or +to_param+.
|
80
|
+
def expand_cache_key(key, namespace = nil)
|
81
|
+
expanded_cache_key = namespace ? "#{namespace}/" : ""
|
82
|
+
|
83
|
+
if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
|
84
|
+
expanded_cache_key << "#{prefix}/"
|
85
|
+
end
|
79
86
|
|
80
|
-
|
81
|
-
|
82
|
-
expanded_cache_key << "#{prefix}/"
|
87
|
+
expanded_cache_key << retrieve_cache_key(key)
|
88
|
+
expanded_cache_key
|
83
89
|
end
|
84
90
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
else
|
92
|
-
|
93
|
-
|
94
|
-
elsif key
|
95
|
-
key.to_param
|
96
|
-
end.to_s
|
91
|
+
private
|
92
|
+
def retrieve_cache_key(key)
|
93
|
+
case
|
94
|
+
when key.respond_to?(:cache_key) then key.cache_key
|
95
|
+
when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
|
96
|
+
when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
|
97
|
+
else key.to_param
|
98
|
+
end.to_s
|
99
|
+
end
|
97
100
|
|
98
|
-
|
101
|
+
# Obtains the specified cache store class, given the name of the +store+.
|
102
|
+
# Raises an error when the store class cannot be found.
|
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
|
99
110
|
end
|
100
111
|
|
101
112
|
# An abstract cache store class. There are multiple cache store
|
@@ -111,56 +122,56 @@ module ActiveSupport
|
|
111
122
|
#
|
112
123
|
# cache = ActiveSupport::Cache::MemoryStore.new
|
113
124
|
#
|
114
|
-
# cache.read(
|
115
|
-
# cache.write(
|
116
|
-
# cache.read(
|
125
|
+
# cache.read('city') # => nil
|
126
|
+
# cache.write('city', "Duckburgh")
|
127
|
+
# cache.read('city') # => "Duckburgh"
|
117
128
|
#
|
118
129
|
# Keys are always translated into Strings and are case sensitive. When an
|
119
|
-
# object is specified as a key
|
120
|
-
#
|
121
|
-
# Arrays can be used as keys. The
|
122
|
-
#
|
130
|
+
# object is specified as a key and has a +cache_key+ method defined, this
|
131
|
+
# method will be called to define the key. Otherwise, the +to_param+
|
132
|
+
# method will be called. Hashes and Arrays can also be used as keys. The
|
133
|
+
# elements will be delimited by slashes, and the elements within a Hash
|
134
|
+
# will be sorted by key so they are consistent.
|
123
135
|
#
|
124
|
-
# cache.read(
|
136
|
+
# cache.read('city') == cache.read(:city) # => true
|
125
137
|
#
|
126
138
|
# Nil values can be cached.
|
127
139
|
#
|
128
|
-
# If your cache is on a shared infrastructure, you can define a namespace
|
129
|
-
# your cache entries. If a namespace is defined, it will be prefixed on
|
130
|
-
# key. The namespace can be either a static value or a Proc. If it
|
131
|
-
# will be invoked when each key is evaluated so that you can
|
132
|
-
# to invalidate keys.
|
140
|
+
# If your cache is on a shared infrastructure, you can define a namespace
|
141
|
+
# for your cache entries. If a namespace is defined, it will be prefixed on
|
142
|
+
# to every key. The namespace can be either a static value or a Proc. If it
|
143
|
+
# is a Proc, it will be invoked when each key is evaluated so that you can
|
144
|
+
# use application logic to invalidate keys.
|
133
145
|
#
|
134
|
-
# cache.namespace =
|
146
|
+
# cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
|
135
147
|
# @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
|
136
148
|
#
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
# <tt>:compress_threshold</tt>. The default threshold is 32K.
|
149
|
+
# Caches can also store values in a compressed format to save space and
|
150
|
+
# reduce time spent sending data. Since there is overhead, values must be
|
151
|
+
# large enough to warrant compression. To turn on compression either pass
|
152
|
+
# <tt>compress: true</tt> in the initializer or as an option to +fetch+
|
153
|
+
# or +write+. To specify the threshold at which to compress values, set the
|
154
|
+
# <tt>:compress_threshold</tt> option. The default threshold is 16K.
|
144
155
|
class Store
|
145
|
-
|
146
156
|
cattr_accessor :logger, :instance_writer => true
|
147
157
|
|
148
158
|
attr_reader :silence, :options
|
149
159
|
alias :silence? :silence
|
150
160
|
|
151
|
-
#
|
152
|
-
# for
|
153
|
-
|
161
|
+
# Creates a new cache. The options will be passed to any write method calls
|
162
|
+
# except for <tt>:namespace</tt> which can be used to set the global
|
163
|
+
# namespace for the cache.
|
164
|
+
def initialize(options = nil)
|
154
165
|
@options = options ? options.dup : {}
|
155
166
|
end
|
156
167
|
|
157
|
-
#
|
168
|
+
# Silences the logger.
|
158
169
|
def silence!
|
159
170
|
@silence = true
|
160
171
|
self
|
161
172
|
end
|
162
173
|
|
163
|
-
#
|
174
|
+
# Silences the logger within a block.
|
164
175
|
def mute
|
165
176
|
previous_silence, @silence = defined?(@silence) && @silence, true
|
166
177
|
yield
|
@@ -168,137 +179,128 @@ module ActiveSupport
|
|
168
179
|
@silence = previous_silence
|
169
180
|
end
|
170
181
|
|
171
|
-
# Set to true if cache stores should be instrumented. Default is false.
|
172
|
-
def self.instrument=(boolean)
|
173
|
-
Thread.current[:instrument_cache_store] = boolean
|
174
|
-
end
|
175
|
-
|
176
|
-
def self.instrument
|
177
|
-
Thread.current[:instrument_cache_store] || false
|
178
|
-
end
|
179
|
-
|
180
182
|
# Fetches data from the cache, using the given key. If there is data in
|
181
183
|
# the cache with the given key, then that data is returned.
|
182
184
|
#
|
183
|
-
# If there is no such data in the cache (a cache miss
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
185
|
+
# If there is no such data in the cache (a cache miss), then +nil+ will be
|
186
|
+
# returned. However, if a block has been passed, that block will be passed
|
187
|
+
# the key and executed in the event of a cache miss. The return value of the
|
188
|
+
# block will be written to the cache under the given cache key, and that
|
189
|
+
# return value will be returned.
|
188
190
|
#
|
189
|
-
# cache.write(
|
190
|
-
# cache.fetch(
|
191
|
+
# cache.write('today', 'Monday')
|
192
|
+
# cache.fetch('today') # => "Monday"
|
191
193
|
#
|
192
|
-
# cache.fetch(
|
193
|
-
# cache.fetch(
|
194
|
-
#
|
194
|
+
# cache.fetch('city') # => nil
|
195
|
+
# cache.fetch('city') do
|
196
|
+
# 'Duckburgh'
|
195
197
|
# end
|
196
|
-
# cache.fetch(
|
198
|
+
# cache.fetch('city') # => "Duckburgh"
|
197
199
|
#
|
198
200
|
# You may also specify additional options via the +options+ argument.
|
199
|
-
# Setting <tt
|
201
|
+
# Setting <tt>force: true</tt> forces a cache "miss," meaning we treat
|
202
|
+
# the cache value as missing even if it's present. Passing a block is
|
203
|
+
# required when `force` is true so this always results in a cache write.
|
200
204
|
#
|
201
|
-
# cache.write(
|
202
|
-
# cache.fetch(
|
205
|
+
# cache.write('today', 'Monday')
|
206
|
+
# cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday'
|
207
|
+
# cache.fetch('today', force: true) # => ArgumentError
|
208
|
+
#
|
209
|
+
# The `:force` option is useful when you're calling some other method to
|
210
|
+
# ask whether you should force a cache write. Otherwise, it's clearer to
|
211
|
+
# just call `Cache#write`.
|
203
212
|
#
|
204
213
|
# Setting <tt>:compress</tt> will store a large cache entry set by the call
|
205
214
|
# in a compressed format.
|
206
215
|
#
|
207
|
-
#
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
#
|
213
|
-
# cache = ActiveSupport::Cache::MemoryStore.new(:
|
214
|
-
# cache.write(key, value, :
|
215
|
-
#
|
216
|
-
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where
|
217
|
-
# is used very frequently and is under heavy load. If a
|
218
|
-
#
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
216
|
+
# Setting <tt>:expires_in</tt> will set an expiration time on the cache.
|
217
|
+
# All caches support auto-expiring content after a specified number of
|
218
|
+
# seconds. This value can be specified as an option to the constructor
|
219
|
+
# (in which case all entries will be affected), or it can be supplied to
|
220
|
+
# the +fetch+ or +write+ method to effect just one entry.
|
221
|
+
#
|
222
|
+
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
|
223
|
+
# cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
|
224
|
+
#
|
225
|
+
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where
|
226
|
+
# a cache entry is used very frequently and is under heavy load. If a
|
227
|
+
# cache expires and due to heavy load several different processes will try
|
228
|
+
# to read data natively and then they all will try to write to cache. To
|
229
|
+
# avoid that case the first process to find an expired cache entry will
|
230
|
+
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.
|
231
|
+
# Yes, this process is extending the time for a stale value by another few
|
232
|
+
# seconds. Because of extended life of the previous cache, other processes
|
233
|
+
# will continue to use slightly stale data for a just a bit longer. In the
|
234
|
+
# meantime that first process will go ahead and will write into cache the
|
235
|
+
# new value. After that all the processes will start getting the new value.
|
236
|
+
# The key is to keep <tt>:race_condition_ttl</tt> small.
|
237
|
+
#
|
238
|
+
# If the process regenerating the entry errors out, the entry will be
|
239
|
+
# regenerated after the specified number of seconds. Also note that the
|
240
|
+
# life of stale cache is extended only if it expired recently. Otherwise
|
241
|
+
# a new value is generated and <tt>:race_condition_ttl</tt> does not play
|
242
|
+
# any role.
|
231
243
|
#
|
232
244
|
# # Set all values to expire after one minute.
|
233
|
-
# cache = ActiveSupport::Cache::
|
245
|
+
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
|
234
246
|
#
|
235
|
-
# cache.write(
|
247
|
+
# cache.write('foo', 'original value')
|
236
248
|
# val_1 = nil
|
237
249
|
# val_2 = nil
|
238
250
|
# sleep 60
|
239
251
|
#
|
240
252
|
# Thread.new do
|
241
|
-
# val_1 = cache.fetch(
|
253
|
+
# val_1 = cache.fetch('foo', race_condition_ttl: 10) do
|
242
254
|
# sleep 1
|
243
|
-
#
|
255
|
+
# 'new value 1'
|
244
256
|
# end
|
245
257
|
# end
|
246
258
|
#
|
247
259
|
# Thread.new do
|
248
|
-
# val_2 = cache.fetch(
|
249
|
-
#
|
260
|
+
# val_2 = cache.fetch('foo', race_condition_ttl: 10) do
|
261
|
+
# 'new value 2'
|
250
262
|
# end
|
251
263
|
# end
|
252
264
|
#
|
253
|
-
# #
|
254
|
-
# #
|
255
|
-
#
|
256
|
-
# #
|
265
|
+
# cache.fetch('foo') # => "original value"
|
266
|
+
# sleep 10 # First thread extended the life of cache by another 10 seconds
|
267
|
+
# cache.fetch('foo') # => "new value 1"
|
268
|
+
# val_1 # => "new value 1"
|
269
|
+
# val_2 # => "original value"
|
257
270
|
#
|
258
271
|
# Other options will be handled by the specific cache store implementation.
|
259
|
-
# Internally, #fetch calls #read_entry, and calls #write_entry on a cache
|
260
|
-
# +options+ will be passed to the #read and #write calls.
|
272
|
+
# Internally, #fetch calls #read_entry, and calls #write_entry on a cache
|
273
|
+
# miss. +options+ will be passed to the #read and #write calls.
|
261
274
|
#
|
262
275
|
# For example, MemCacheStore's #write method supports the +:raw+
|
263
276
|
# option, which tells the memcached server to store all values as strings.
|
264
277
|
# We can use this option with #fetch too:
|
265
278
|
#
|
266
279
|
# cache = ActiveSupport::Cache::MemCacheStore.new
|
267
|
-
# cache.fetch("foo", :
|
280
|
+
# cache.fetch("foo", force: true, raw: true) do
|
268
281
|
# :bar
|
269
282
|
# end
|
270
|
-
# cache.fetch(
|
283
|
+
# cache.fetch('foo') # => "bar"
|
271
284
|
def fetch(name, options = nil)
|
272
285
|
if block_given?
|
273
286
|
options = merged_options(options)
|
274
|
-
key =
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
race_ttl = options[:race_condition_ttl].to_f
|
283
|
-
if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl
|
284
|
-
entry.expires_at = Time.now + race_ttl
|
285
|
-
write_entry(key, entry, :expires_in => race_ttl * 2)
|
286
|
-
else
|
287
|
-
delete_entry(key, options)
|
288
|
-
end
|
289
|
-
entry = nil
|
287
|
+
key = normalize_key(name, options)
|
288
|
+
|
289
|
+
entry = nil
|
290
|
+
instrument(:read, name, options) do |payload|
|
291
|
+
cached_entry = read_entry(key, options) unless options[:force]
|
292
|
+
entry = handle_expired_entry(cached_entry, key, options)
|
293
|
+
payload[:super_operation] = :fetch if payload
|
294
|
+
payload[:hit] = !!entry if payload
|
290
295
|
end
|
291
296
|
|
292
297
|
if entry
|
293
|
-
|
294
|
-
entry.value
|
298
|
+
get_entry_value(entry, name, options)
|
295
299
|
else
|
296
|
-
|
297
|
-
yield
|
298
|
-
end
|
299
|
-
write(name, result, options)
|
300
|
-
result
|
300
|
+
save_block_result_to_cache(name, options) { |_name| yield _name }
|
301
301
|
end
|
302
|
+
elsif options && options[:force]
|
303
|
+
raise ArgumentError, 'Missing block: Calling `Cache#fetch` with `force: true` requires a block.'
|
302
304
|
else
|
303
305
|
read(name, options)
|
304
306
|
end
|
@@ -306,12 +308,12 @@ module ActiveSupport
|
|
306
308
|
|
307
309
|
# Fetches data from the cache, using the given key. If there is data in
|
308
310
|
# the cache with the given key, then that data is returned. Otherwise,
|
309
|
-
# nil is returned.
|
311
|
+
# +nil+ is returned.
|
310
312
|
#
|
311
313
|
# Options are passed to the underlying cache implementation.
|
312
314
|
def read(name, options = nil)
|
313
315
|
options = merged_options(options)
|
314
|
-
key =
|
316
|
+
key = normalize_key(name, options)
|
315
317
|
instrument(:read, name, options) do |payload|
|
316
318
|
entry = read_entry(key, options)
|
317
319
|
if entry
|
@@ -330,7 +332,7 @@ module ActiveSupport
|
|
330
332
|
end
|
331
333
|
end
|
332
334
|
|
333
|
-
#
|
335
|
+
# Reads multiple values at once from the cache. Options can be passed
|
334
336
|
# in the last argument.
|
335
337
|
#
|
336
338
|
# Some cache implementation may optimize this method.
|
@@ -339,9 +341,10 @@ module ActiveSupport
|
|
339
341
|
def read_multi(*names)
|
340
342
|
options = names.extract_options!
|
341
343
|
options = merged_options(options)
|
344
|
+
|
342
345
|
results = {}
|
343
346
|
names.each do |name|
|
344
|
-
key =
|
347
|
+
key = normalize_key(name, options)
|
345
348
|
entry = read_entry(key, options)
|
346
349
|
if entry
|
347
350
|
if entry.expired?
|
@@ -354,14 +357,45 @@ module ActiveSupport
|
|
354
357
|
results
|
355
358
|
end
|
356
359
|
|
360
|
+
# Fetches data from the cache, using the given keys. If there is data in
|
361
|
+
# the cache with the given keys, then that data is returned. Otherwise,
|
362
|
+
# the supplied block is called for each key for which there was no data,
|
363
|
+
# and the result will be written to the cache and returned.
|
364
|
+
#
|
365
|
+
# Options are passed to the underlying cache implementation.
|
366
|
+
#
|
367
|
+
# Returns a hash with the data for each of the names. For example:
|
368
|
+
#
|
369
|
+
# cache.write("bim", "bam")
|
370
|
+
# cache.fetch_multi("bim", "unknown_key") do |key|
|
371
|
+
# "Fallback value for key: #{key}"
|
372
|
+
# end
|
373
|
+
# # => { "bim" => "bam",
|
374
|
+
# # "unknown_key" => "Fallback value for key: unknown_key" }
|
375
|
+
#
|
376
|
+
def fetch_multi(*names)
|
377
|
+
options = names.extract_options!
|
378
|
+
options = merged_options(options)
|
379
|
+
results = read_multi(*names, options)
|
380
|
+
|
381
|
+
names.each_with_object({}) do |name, memo|
|
382
|
+
memo[name] = results.fetch(name) do
|
383
|
+
value = yield name
|
384
|
+
write(name, value, options)
|
385
|
+
value
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
357
390
|
# Writes the value to the cache, with the key.
|
358
391
|
#
|
359
392
|
# Options are passed to the underlying cache implementation.
|
360
393
|
def write(name, value, options = nil)
|
361
394
|
options = merged_options(options)
|
362
|
-
|
395
|
+
|
396
|
+
instrument(:write, name, options) do
|
363
397
|
entry = Entry.new(value, options)
|
364
|
-
write_entry(
|
398
|
+
write_entry(normalize_key(name, options), entry, options)
|
365
399
|
end
|
366
400
|
end
|
367
401
|
|
@@ -370,27 +404,25 @@ module ActiveSupport
|
|
370
404
|
# Options are passed to the underlying cache implementation.
|
371
405
|
def delete(name, options = nil)
|
372
406
|
options = merged_options(options)
|
373
|
-
|
374
|
-
|
407
|
+
|
408
|
+
instrument(:delete, name) do
|
409
|
+
delete_entry(normalize_key(name, options), options)
|
375
410
|
end
|
376
411
|
end
|
377
412
|
|
378
|
-
#
|
413
|
+
# Returns +true+ if the cache contains an entry for the given key.
|
379
414
|
#
|
380
415
|
# Options are passed to the underlying cache implementation.
|
381
416
|
def exist?(name, options = nil)
|
382
417
|
options = merged_options(options)
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
else
|
388
|
-
false
|
389
|
-
end
|
418
|
+
|
419
|
+
instrument(:exist?, name) do
|
420
|
+
entry = read_entry(normalize_key(name, options), options)
|
421
|
+
(entry && !entry.expired?) || false
|
390
422
|
end
|
391
423
|
end
|
392
424
|
|
393
|
-
#
|
425
|
+
# Deletes all entries with keys matching the pattern.
|
394
426
|
#
|
395
427
|
# Options are passed to the underlying cache implementation.
|
396
428
|
#
|
@@ -399,7 +431,7 @@ module ActiveSupport
|
|
399
431
|
raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
|
400
432
|
end
|
401
433
|
|
402
|
-
#
|
434
|
+
# Increments an integer value in the cache.
|
403
435
|
#
|
404
436
|
# Options are passed to the underlying cache implementation.
|
405
437
|
#
|
@@ -408,7 +440,7 @@ module ActiveSupport
|
|
408
440
|
raise NotImplementedError.new("#{self.class.name} does not support increment")
|
409
441
|
end
|
410
442
|
|
411
|
-
#
|
443
|
+
# Decrements an integer value in the cache.
|
412
444
|
#
|
413
445
|
# Options are passed to the underlying cache implementation.
|
414
446
|
#
|
@@ -417,7 +449,7 @@ module ActiveSupport
|
|
417
449
|
raise NotImplementedError.new("#{self.class.name} does not support decrement")
|
418
450
|
end
|
419
451
|
|
420
|
-
#
|
452
|
+
# Cleanups the cache by removing expired entries.
|
421
453
|
#
|
422
454
|
# Options are passed to the underlying cache implementation.
|
423
455
|
#
|
@@ -426,10 +458,10 @@ module ActiveSupport
|
|
426
458
|
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
|
427
459
|
end
|
428
460
|
|
429
|
-
#
|
461
|
+
# Clears the entire cache. Be careful with this method since it could
|
430
462
|
# affect other processes if shared cache is being used.
|
431
463
|
#
|
432
|
-
#
|
464
|
+
# The options hash is passed to the underlying cache implementation.
|
433
465
|
#
|
434
466
|
# All implementations may not support this method.
|
435
467
|
def clear(options = nil)
|
@@ -437,9 +469,10 @@ module ActiveSupport
|
|
437
469
|
end
|
438
470
|
|
439
471
|
protected
|
440
|
-
#
|
441
|
-
# Implementations that support delete_matched should call
|
442
|
-
# a pattern that matches names into one that
|
472
|
+
# Adds the namespace defined in the options to a pattern designed to
|
473
|
+
# match keys. Implementations that support delete_matched should call
|
474
|
+
# this method to translate a pattern that matches names into one that
|
475
|
+
# matches namespaced keys.
|
443
476
|
def key_matcher(pattern, options)
|
444
477
|
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
|
445
478
|
if prefix
|
@@ -455,23 +488,26 @@ module ActiveSupport
|
|
455
488
|
end
|
456
489
|
end
|
457
490
|
|
458
|
-
#
|
491
|
+
# Reads an entry from the cache implementation. Subclasses must implement
|
492
|
+
# this method.
|
459
493
|
def read_entry(key, options) # :nodoc:
|
460
494
|
raise NotImplementedError.new
|
461
495
|
end
|
462
496
|
|
463
|
-
#
|
497
|
+
# Writes an entry to the cache implementation. Subclasses must implement
|
498
|
+
# this method.
|
464
499
|
def write_entry(key, entry, options) # :nodoc:
|
465
500
|
raise NotImplementedError.new
|
466
501
|
end
|
467
502
|
|
468
|
-
#
|
503
|
+
# Deletes an entry from the cache implementation. Subclasses must
|
504
|
+
# implement this method.
|
469
505
|
def delete_entry(key, options) # :nodoc:
|
470
506
|
raise NotImplementedError.new
|
471
507
|
end
|
472
508
|
|
473
509
|
private
|
474
|
-
#
|
510
|
+
# Merges the default options with ones specific to a method call.
|
475
511
|
def merged_options(call_options) # :nodoc:
|
476
512
|
if call_options
|
477
513
|
options.merge(call_options)
|
@@ -480,8 +516,8 @@ module ActiveSupport
|
|
480
516
|
end
|
481
517
|
end
|
482
518
|
|
483
|
-
#
|
484
|
-
# object responds to +cache_key+. Otherwise, to_param method will be
|
519
|
+
# Expands key to be a consistent string value. Invokes +cache_key+ if
|
520
|
+
# object responds to +cache_key+. Otherwise, +to_param+ method will be
|
485
521
|
# called. If the key is a Hash, then keys will be sorted alphabetically.
|
486
522
|
def expanded_key(key) # :nodoc:
|
487
523
|
return key.cache_key.to_s if key.respond_to?(:cache_key)
|
@@ -500,8 +536,9 @@ module ActiveSupport
|
|
500
536
|
key.to_param
|
501
537
|
end
|
502
538
|
|
503
|
-
#
|
504
|
-
|
539
|
+
# Prefixes a key with the namespace. Namespace and key will be delimited
|
540
|
+
# with a colon.
|
541
|
+
def normalize_key(key, options)
|
505
542
|
key = expanded_key(key)
|
506
543
|
namespace = options[:namespace] if options
|
507
544
|
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
|
@@ -509,127 +546,156 @@ module ActiveSupport
|
|
509
546
|
key
|
510
547
|
end
|
511
548
|
|
549
|
+
def namespaced_key(*args)
|
550
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
|
551
|
+
`namespaced_key` is deprecated and will be removed from Rails 5.1.
|
552
|
+
Please use `normalize_key` which will return a fully resolved key.
|
553
|
+
MESSAGE
|
554
|
+
normalize_key(*args)
|
555
|
+
end
|
556
|
+
|
512
557
|
def instrument(operation, key, options = nil)
|
513
|
-
log
|
558
|
+
log { "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" }
|
514
559
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
|
519
|
-
else
|
520
|
-
yield(nil)
|
521
|
-
end
|
560
|
+
payload = { :key => key }
|
561
|
+
payload.merge!(options) if options.is_a?(Hash)
|
562
|
+
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
|
522
563
|
end
|
523
564
|
|
524
|
-
def log
|
565
|
+
def log
|
525
566
|
return unless logger && logger.debug? && !silence?
|
526
|
-
logger.debug(
|
567
|
+
logger.debug(yield)
|
527
568
|
end
|
528
|
-
end
|
529
569
|
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
570
|
+
def handle_expired_entry(entry, key, options)
|
571
|
+
if entry && entry.expired?
|
572
|
+
race_ttl = options[:race_condition_ttl].to_i
|
573
|
+
if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
|
574
|
+
# When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
|
575
|
+
# for a brief period while the entry is being recalculated.
|
576
|
+
entry.expires_at = Time.now + race_ttl
|
577
|
+
write_entry(key, entry, :expires_in => race_ttl * 2)
|
578
|
+
else
|
579
|
+
delete_entry(key, options)
|
580
|
+
end
|
581
|
+
entry = nil
|
582
|
+
end
|
583
|
+
entry
|
584
|
+
end
|
534
585
|
|
535
|
-
|
586
|
+
def get_entry_value(entry, name, options)
|
587
|
+
instrument(:fetch_hit, name, options) { }
|
588
|
+
entry.value
|
589
|
+
end
|
536
590
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
entry.instance_variable_set(:@created_at, created_at.to_f)
|
545
|
-
entry.instance_variable_set(:@compressed, !!options[:compressed])
|
546
|
-
entry.instance_variable_set(:@expires_in, options[:expires_in])
|
547
|
-
entry
|
591
|
+
def save_block_result_to_cache(name, options)
|
592
|
+
result = instrument(:generate, name, options) do
|
593
|
+
yield(name)
|
594
|
+
end
|
595
|
+
|
596
|
+
write(name, result, options)
|
597
|
+
result
|
548
598
|
end
|
549
|
-
|
599
|
+
end
|
550
600
|
|
551
|
-
|
601
|
+
# This class is used to represent cache entries. Cache entries have a value and an optional
|
602
|
+
# expiration time. The expiration time is used to support the :race_condition_ttl option
|
603
|
+
# on the cache.
|
604
|
+
#
|
605
|
+
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
|
606
|
+
# using short instance variable names that are lazily defined.
|
607
|
+
class Entry # :nodoc:
|
608
|
+
DEFAULT_COMPRESS_LIMIT = 16.kilobytes
|
609
|
+
|
610
|
+
# Creates a new cache entry for the specified value. Options supported are
|
552
611
|
# +:compress+, +:compress_threshold+, and +:expires_in+.
|
553
612
|
def initialize(value, options = {})
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
@created_at = Time.now.to_f
|
558
|
-
if value
|
559
|
-
if should_compress?(value, options)
|
560
|
-
@value = Zlib::Deflate.deflate(Marshal.dump(value))
|
561
|
-
@compressed = true
|
562
|
-
else
|
563
|
-
@value = value
|
564
|
-
end
|
613
|
+
if should_compress?(value, options)
|
614
|
+
@value = compress(value)
|
615
|
+
@compressed = true
|
565
616
|
else
|
566
|
-
@value =
|
617
|
+
@value = value
|
567
618
|
end
|
568
|
-
end
|
569
619
|
|
570
|
-
|
571
|
-
|
572
|
-
@
|
620
|
+
@created_at = Time.now.to_f
|
621
|
+
@expires_in = options[:expires_in]
|
622
|
+
@expires_in = @expires_in.to_f if @expires_in
|
573
623
|
end
|
574
624
|
|
575
|
-
# Get the value stored in the cache.
|
576
625
|
def value
|
577
|
-
|
578
|
-
val = compressed? ? Marshal.load(Zlib::Inflate.inflate(@value)) : @value
|
579
|
-
unless val.frozen?
|
580
|
-
val.freeze rescue nil
|
581
|
-
end
|
582
|
-
val
|
583
|
-
end
|
584
|
-
end
|
585
|
-
|
586
|
-
def compressed?
|
587
|
-
@compressed
|
626
|
+
compressed? ? uncompress(@value) : @value
|
588
627
|
end
|
589
628
|
|
590
|
-
#
|
591
|
-
# value set when the entry was created.
|
629
|
+
# Checks if the entry is expired. The +expires_in+ parameter can override
|
630
|
+
# the value set when the entry was created.
|
592
631
|
def expired?
|
593
632
|
@expires_in && @created_at + @expires_in <= Time.now.to_f
|
594
633
|
end
|
595
634
|
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
635
|
+
def expires_at
|
636
|
+
@expires_in ? @created_at + @expires_in : nil
|
637
|
+
end
|
638
|
+
|
639
|
+
def expires_at=(value)
|
640
|
+
if value
|
641
|
+
@expires_in = value.to_f - @created_at
|
600
642
|
else
|
601
643
|
@expires_in = nil
|
602
644
|
end
|
603
645
|
end
|
604
646
|
|
605
|
-
#
|
606
|
-
|
607
|
-
@expires_in ? @created_at + @expires_in : nil
|
608
|
-
end
|
609
|
-
|
610
|
-
# Returns the size of the cached value. This could be less than value.size
|
611
|
-
# if the data is compressed.
|
647
|
+
# Returns the size of the cached value. This could be less than
|
648
|
+
# <tt>value.size</tt> if the data is compressed.
|
612
649
|
def size
|
613
|
-
if @
|
614
|
-
|
615
|
-
elsif @value.respond_to?(:bytesize)
|
616
|
-
@value.bytesize
|
650
|
+
if defined?(@s)
|
651
|
+
@s
|
617
652
|
else
|
618
|
-
|
653
|
+
case value
|
654
|
+
when NilClass
|
655
|
+
0
|
656
|
+
when String
|
657
|
+
@value.bytesize
|
658
|
+
else
|
659
|
+
@s = Marshal.dump(@value).bytesize
|
660
|
+
end
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
# Duplicates the value in a class. This is used by cache implementations that don't natively
|
665
|
+
# serialize entries to protect against accidental cache modifications.
|
666
|
+
def dup_value!
|
667
|
+
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
|
668
|
+
if @value.is_a?(String)
|
669
|
+
@value = @value.dup
|
670
|
+
else
|
671
|
+
@value = Marshal.load(Marshal.dump(@value))
|
672
|
+
end
|
619
673
|
end
|
620
674
|
end
|
621
675
|
|
622
676
|
private
|
623
677
|
def should_compress?(value, options)
|
624
|
-
if options[:compress]
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
end
|
678
|
+
if value && options[:compress]
|
679
|
+
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
|
680
|
+
serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
|
681
|
+
|
682
|
+
return true if serialized_value_size >= compress_threshold
|
630
683
|
end
|
684
|
+
|
631
685
|
false
|
632
686
|
end
|
687
|
+
|
688
|
+
def compressed?
|
689
|
+
defined?(@compressed) ? @compressed : false
|
690
|
+
end
|
691
|
+
|
692
|
+
def compress(value)
|
693
|
+
Zlib::Deflate.deflate(Marshal.dump(value))
|
694
|
+
end
|
695
|
+
|
696
|
+
def uncompress(value)
|
697
|
+
Marshal.load(Zlib::Inflate.inflate(value))
|
698
|
+
end
|
633
699
|
end
|
634
700
|
end
|
635
701
|
end
|