activesupport 3.2.22.5 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +364 -135
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -4
- data/lib/active_support/backtrace_cleaner.rb +33 -25
- data/lib/active_support/basic_object.rb +7 -17
- data/lib/active_support/benchmarkable.rb +19 -15
- data/lib/active_support/buffered_logger.rb +9 -113
- data/lib/active_support/cache/file_store.rb +14 -14
- data/lib/active_support/cache/mem_cache_store.rb +24 -30
- data/lib/active_support/cache/memory_store.rb +2 -0
- data/lib/active_support/cache/strategy/local_cache.rb +48 -37
- data/lib/active_support/cache.rb +228 -183
- data/lib/active_support/callbacks.rb +201 -253
- data/lib/active_support/concern.rb +16 -23
- data/lib/active_support/concurrency/latch.rb +27 -0
- data/lib/active_support/configurable.rb +69 -12
- data/lib/active_support/core_ext/array/access.rb +17 -9
- data/lib/active_support/core_ext/array/conversions.rb +115 -55
- data/lib/active_support/core_ext/array/extract_options.rb +2 -2
- data/lib/active_support/core_ext/array/grouping.rb +21 -22
- data/lib/active_support/core_ext/array/uniq_by.rb +12 -9
- data/lib/active_support/core_ext/array/wrap.rb +13 -16
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/benchmark.rb +7 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -24
- data/lib/active_support/core_ext/class/attribute.rb +40 -32
- data/lib/active_support/core_ext/class/attribute_accessors.rb +14 -12
- data/lib/active_support/core_ext/class/delegating_attributes.rb +15 -19
- data/lib/active_support/core_ext/class/subclasses.rb +11 -5
- data/lib/active_support/core_ext/date/calculations.rb +44 -187
- data/lib/active_support/core_ext/date/conversions.rb +16 -38
- data/lib/active_support/core_ext/date/zones.rb +25 -2
- data/lib/active_support/core_ext/date.rb +5 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +77 -62
- data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
- data/lib/active_support/core_ext/date_time/zones.rb +11 -8
- data/lib/active_support/core_ext/date_time.rb +4 -0
- data/lib/active_support/core_ext/enumerable.rb +26 -73
- data/lib/active_support/core_ext/file/atomic.rb +27 -11
- data/lib/active_support/core_ext/file.rb +0 -1
- data/lib/active_support/core_ext/hash/conversions.rb +145 -79
- data/lib/active_support/core_ext/hash/deep_merge.rb +14 -8
- data/lib/active_support/core_ext/hash/diff.rb +5 -4
- data/lib/active_support/core_ext/hash/except.rb +1 -9
- data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -5
- data/lib/active_support/core_ext/hash/keys.rb +108 -24
- data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
- data/lib/active_support/core_ext/hash/slice.rb +12 -12
- data/lib/active_support/core_ext/hash.rb +0 -1
- data/lib/active_support/core_ext/integer/inflections.rb +13 -1
- data/lib/active_support/core_ext/integer/time.rb +17 -12
- data/lib/active_support/core_ext/kernel/debugger.rb +2 -2
- data/lib/active_support/core_ext/kernel/reporting.rb +36 -22
- data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
- data/lib/active_support/core_ext/load_error.rb +7 -5
- data/lib/active_support/core_ext/logger.rb +7 -23
- data/lib/active_support/core_ext/marshal.rb +21 -0
- data/lib/active_support/core_ext/module/aliasing.rb +8 -9
- data/lib/active_support/core_ext/module/anonymous.rb +2 -7
- data/lib/active_support/core_ext/module/attr_internal.rb +0 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +12 -10
- data/lib/active_support/core_ext/module/delegation.rb +92 -49
- data/lib/active_support/core_ext/module/deprecation.rb +19 -3
- data/lib/active_support/core_ext/module/introspection.rb +17 -27
- data/lib/active_support/core_ext/module/qualified_const.rb +8 -20
- data/lib/active_support/core_ext/module/remove_method.rb +1 -5
- data/lib/active_support/core_ext/module.rb +1 -3
- data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
- data/lib/active_support/core_ext/numeric/time.rb +6 -6
- data/lib/active_support/core_ext/numeric.rb +1 -0
- data/lib/active_support/core_ext/object/acts_like.rb +4 -4
- data/lib/active_support/core_ext/object/blank.rb +7 -23
- data/lib/active_support/core_ext/object/deep_dup.rb +46 -0
- data/lib/active_support/core_ext/object/duplicable.rb +1 -30
- data/lib/active_support/core_ext/object/inclusion.rb +8 -7
- data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
- data/lib/active_support/core_ext/object/to_json.rb +8 -0
- data/lib/active_support/core_ext/object/to_param.rb +5 -2
- data/lib/active_support/core_ext/object/try.rb +46 -25
- data/lib/active_support/core_ext/object/with_options.rb +7 -8
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/proc.rb +3 -0
- data/lib/active_support/core_ext/range/conversions.rb +0 -2
- data/lib/active_support/core_ext/range/include_range.rb +3 -1
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- data/lib/active_support/core_ext/range.rb +0 -2
- data/lib/active_support/core_ext/string/access.rb +95 -90
- data/lib/active_support/core_ext/string/conversions.rb +27 -38
- data/lib/active_support/core_ext/string/encoding.rb +6 -9
- data/lib/active_support/core_ext/string/filters.rb +24 -18
- data/lib/active_support/core_ext/string/indent.rb +43 -0
- data/lib/active_support/core_ext/string/inflections.rb +70 -60
- data/lib/active_support/core_ext/string/inquiry.rb +2 -2
- data/lib/active_support/core_ext/string/multibyte.rb +41 -64
- data/lib/active_support/core_ext/string/output_safety.rb +59 -51
- data/lib/active_support/core_ext/string/zones.rb +13 -0
- data/lib/active_support/core_ext/string.rb +2 -3
- data/lib/active_support/core_ext/struct.rb +6 -0
- data/lib/active_support/core_ext/thread.rb +74 -0
- data/lib/active_support/core_ext/time/calculations.rb +108 -184
- data/lib/active_support/core_ext/time/conversions.rb +27 -51
- data/lib/active_support/core_ext/time/marshal.rb +0 -27
- data/lib/active_support/core_ext/time/zones.rb +27 -17
- data/lib/active_support/core_ext/time.rb +5 -0
- data/lib/active_support/core_ext/uri.rb +13 -17
- data/lib/active_support/core_ext.rb +3 -2
- data/lib/active_support/dependencies/autoload.rb +47 -20
- data/lib/active_support/dependencies.rb +160 -141
- data/lib/active_support/deprecation/behaviors.rb +44 -30
- data/lib/active_support/deprecation/instance_delegator.rb +24 -0
- data/lib/active_support/deprecation/method_wrappers.rb +33 -18
- data/lib/active_support/deprecation/proxy_wrappers.rb +58 -13
- data/lib/active_support/deprecation/reporting.rb +40 -11
- data/lib/active_support/deprecation.rb +39 -14
- data/lib/active_support/descendants_tracker.rb +34 -19
- data/lib/active_support/duration.rb +6 -8
- data/lib/active_support/file_update_checker.rb +63 -47
- data/lib/active_support/gzip.rb +11 -5
- data/lib/active_support/hash_with_indifferent_access.rb +127 -42
- data/lib/active_support/i18n.rb +4 -0
- data/lib/active_support/i18n_railtie.rb +5 -22
- data/lib/active_support/inflections.rb +14 -12
- data/lib/active_support/inflector/inflections.rb +108 -71
- data/lib/active_support/inflector/methods.rb +181 -160
- data/lib/active_support/inflector/transliterate.rb +16 -17
- data/lib/active_support/json/decoding.rb +18 -17
- data/lib/active_support/json/encoding.rb +93 -39
- data/lib/active_support/json/variable.rb +10 -1
- data/lib/active_support/key_generator.rb +75 -0
- data/lib/active_support/lazy_load_hooks.rb +21 -19
- data/lib/active_support/locale/en.yml +100 -3
- data/lib/active_support/log_subscriber/test_helper.rb +18 -15
- data/lib/active_support/log_subscriber.rb +35 -52
- data/lib/active_support/logger.rb +57 -0
- data/lib/active_support/logger_silence.rb +24 -0
- data/lib/active_support/message_encryptor.rb +38 -35
- data/lib/active_support/message_verifier.rb +9 -15
- data/lib/active_support/multibyte/chars.rb +80 -333
- data/lib/active_support/multibyte/unicode.rb +74 -64
- data/lib/active_support/multibyte.rb +5 -28
- data/lib/active_support/notifications/fanout.rb +112 -18
- data/lib/active_support/notifications/instrumenter.rb +33 -14
- data/lib/active_support/notifications.rb +79 -26
- data/lib/active_support/number_helper.rb +637 -0
- data/lib/active_support/ordered_hash.rb +8 -190
- data/lib/active_support/ordered_options.rb +21 -23
- data/lib/active_support/per_thread_registry.rb +52 -0
- data/lib/active_support/proxy_object.rb +13 -0
- data/lib/active_support/rails.rb +27 -0
- data/lib/active_support/railtie.rb +12 -32
- data/lib/active_support/rescuable.rb +9 -4
- data/lib/active_support/string_inquirer.rb +13 -8
- data/lib/active_support/subscriber.rb +93 -0
- data/lib/active_support/tagged_logging.rb +51 -73
- data/lib/active_support/test_case.rb +46 -17
- data/lib/active_support/testing/assertions.rb +56 -26
- data/lib/active_support/testing/autorun.rb +5 -0
- data/lib/active_support/testing/constant_lookup.rb +54 -0
- data/lib/active_support/testing/declarative.rb +1 -1
- data/lib/active_support/testing/deprecation.rb +0 -19
- data/lib/active_support/testing/isolation.rb +29 -58
- data/lib/active_support/testing/pending.rb +5 -43
- data/lib/active_support/testing/setup_and_teardown.rb +6 -92
- data/lib/active_support/testing/tagged_logging.rb +25 -0
- data/lib/active_support/time.rb +6 -21
- data/lib/active_support/time_with_zone.rb +80 -43
- data/lib/active_support/values/time_zone.rb +81 -60
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +7 -6
- data/lib/active_support/xml_mini/jdom.rb +9 -11
- data/lib/active_support/xml_mini/libxml.rb +1 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
- data/lib/active_support/xml_mini/nokogiri.rb +1 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
- data/lib/active_support/xml_mini/rexml.rb +6 -8
- data/lib/active_support/xml_mini.rb +35 -17
- data/lib/active_support.rb +8 -21
- metadata +104 -72
- data/lib/active_support/base64.rb +0 -54
- data/lib/active_support/core_ext/array/random_access.rb +0 -30
- data/lib/active_support/core_ext/date/freeze.rb +0 -33
- 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 -18
- data/lib/active_support/core_ext/io.rb +0 -15
- data/lib/active_support/core_ext/module/method_names.rb +0 -14
- data/lib/active_support/core_ext/module/synchronization.rb +0 -45
- 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/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/memoizable.rb +0 -116
- 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/security_utils.rb +0 -27
- data/lib/active_support/testing/mochaing.rb +0 -7
- 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 -24
data/lib/active_support/cache.rb
CHANGED
|
@@ -3,7 +3,6 @@ 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/exception'
|
|
7
6
|
require 'active_support/core_ext/class/attribute_accessors'
|
|
8
7
|
require 'active_support/core_ext/numeric/bytes'
|
|
9
8
|
require 'active_support/core_ext/numeric/time'
|
|
@@ -45,8 +44,8 @@ module ActiveSupport
|
|
|
45
44
|
# Any additional arguments will be passed to the corresponding cache store
|
|
46
45
|
# class's constructor:
|
|
47
46
|
#
|
|
48
|
-
# ActiveSupport::Cache.lookup_store(:file_store,
|
|
49
|
-
# # => same as: ActiveSupport::Cache::FileStore.new(
|
|
47
|
+
# ActiveSupport::Cache.lookup_store(:file_store, '/tmp/cache')
|
|
48
|
+
# # => same as: ActiveSupport::Cache::FileStore.new('/tmp/cache')
|
|
50
49
|
#
|
|
51
50
|
# If the first argument is not a Symbol, then it will simply be returned:
|
|
52
51
|
#
|
|
@@ -57,16 +56,7 @@ module ActiveSupport
|
|
|
57
56
|
|
|
58
57
|
case store
|
|
59
58
|
when Symbol
|
|
60
|
-
|
|
61
|
-
store_class =
|
|
62
|
-
begin
|
|
63
|
-
require "active_support/cache/#{store}"
|
|
64
|
-
rescue LoadError => e
|
|
65
|
-
raise "Could not find cache store adapter for #{store} (#{e})"
|
|
66
|
-
else
|
|
67
|
-
ActiveSupport::Cache.const_get(store_class_name)
|
|
68
|
-
end
|
|
69
|
-
store_class.new(*parameters)
|
|
59
|
+
retrieve_store_class(store).new(*parameters)
|
|
70
60
|
when nil
|
|
71
61
|
ActiveSupport::Cache::MemoryStore.new
|
|
72
62
|
else
|
|
@@ -74,6 +64,18 @@ module ActiveSupport
|
|
|
74
64
|
end
|
|
75
65
|
end
|
|
76
66
|
|
|
67
|
+
# Expands out the +key+ argument into a key that can be used for the
|
|
68
|
+
# cache store. Optionally accepts a namespace, and all keys will be
|
|
69
|
+
# scoped within that namespace.
|
|
70
|
+
#
|
|
71
|
+
# If the +key+ argument provided is an array, or responds to +to_a+, then
|
|
72
|
+
# each of elements in the array will be turned into parameters/keys and
|
|
73
|
+
# concatenated into a single key. For example:
|
|
74
|
+
#
|
|
75
|
+
# expand_cache_key([:foo, :bar]) # => "foo/bar"
|
|
76
|
+
# expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
|
|
77
|
+
#
|
|
78
|
+
# The +key+ argument can also respond to +cache_key+ or +to_param+.
|
|
77
79
|
def expand_cache_key(key, namespace = nil)
|
|
78
80
|
expanded_cache_key = namespace ? "#{namespace}/" : ""
|
|
79
81
|
|
|
@@ -91,9 +93,20 @@ module ActiveSupport
|
|
|
91
93
|
case
|
|
92
94
|
when key.respond_to?(:cache_key) then key.cache_key
|
|
93
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)
|
|
94
97
|
else key.to_param
|
|
95
98
|
end.to_s
|
|
96
99
|
end
|
|
100
|
+
|
|
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
|
|
97
110
|
end
|
|
98
111
|
|
|
99
112
|
# An abstract cache store class. There are multiple cache store
|
|
@@ -109,9 +122,9 @@ module ActiveSupport
|
|
|
109
122
|
#
|
|
110
123
|
# cache = ActiveSupport::Cache::MemoryStore.new
|
|
111
124
|
#
|
|
112
|
-
# cache.read(
|
|
113
|
-
# cache.write(
|
|
114
|
-
# cache.read(
|
|
125
|
+
# cache.read('city') # => nil
|
|
126
|
+
# cache.write('city', "Duckburgh")
|
|
127
|
+
# cache.read('city') # => "Duckburgh"
|
|
115
128
|
#
|
|
116
129
|
# Keys are always translated into Strings and are case sensitive. When an
|
|
117
130
|
# object is specified as a key and has a +cache_key+ method defined, this
|
|
@@ -120,7 +133,7 @@ module ActiveSupport
|
|
|
120
133
|
# elements will be delimited by slashes, and the elements within a Hash
|
|
121
134
|
# will be sorted by key so they are consistent.
|
|
122
135
|
#
|
|
123
|
-
# cache.read(
|
|
136
|
+
# cache.read('city') == cache.read(:city) # => true
|
|
124
137
|
#
|
|
125
138
|
# Nil values can be cached.
|
|
126
139
|
#
|
|
@@ -130,14 +143,13 @@ module ActiveSupport
|
|
|
130
143
|
# is a Proc, it will be invoked when each key is evaluated so that you can
|
|
131
144
|
# use application logic to invalidate keys.
|
|
132
145
|
#
|
|
133
|
-
# cache.namespace =
|
|
146
|
+
# cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
|
|
134
147
|
# @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
|
|
135
148
|
#
|
|
136
|
-
#
|
|
137
149
|
# Caches can also store values in a compressed format to save space and
|
|
138
150
|
# reduce time spent sending data. Since there is overhead, values must be
|
|
139
151
|
# large enough to warrant compression. To turn on compression either pass
|
|
140
|
-
# <tt
|
|
152
|
+
# <tt>compress: true</tt> in the initializer or as an option to +fetch+
|
|
141
153
|
# or +write+. To specify the threshold at which to compress values, set the
|
|
142
154
|
# <tt>:compress_threshold</tt> option. The default threshold is 16K.
|
|
143
155
|
class Store
|
|
@@ -147,8 +159,9 @@ module ActiveSupport
|
|
|
147
159
|
attr_reader :silence, :options
|
|
148
160
|
alias :silence? :silence
|
|
149
161
|
|
|
150
|
-
# Create a new cache. The options will be passed to any write method calls
|
|
151
|
-
# for
|
|
162
|
+
# Create a new cache. The options will be passed to any write method calls
|
|
163
|
+
# except for <tt>:namespace</tt> which can be used to set the global
|
|
164
|
+
# namespace for the cache.
|
|
152
165
|
def initialize(options = nil)
|
|
153
166
|
@options = options ? options.dup : {}
|
|
154
167
|
end
|
|
@@ -167,7 +180,8 @@ module ActiveSupport
|
|
|
167
180
|
@silence = previous_silence
|
|
168
181
|
end
|
|
169
182
|
|
|
170
|
-
# Set to true if cache stores should be instrumented.
|
|
183
|
+
# Set to +true+ if cache stores should be instrumented.
|
|
184
|
+
# Default is +false+.
|
|
171
185
|
def self.instrument=(boolean)
|
|
172
186
|
Thread.current[:instrument_cache_store] = boolean
|
|
173
187
|
end
|
|
@@ -179,125 +193,109 @@ module ActiveSupport
|
|
|
179
193
|
# Fetches data from the cache, using the given key. If there is data in
|
|
180
194
|
# the cache with the given key, then that data is returned.
|
|
181
195
|
#
|
|
182
|
-
# If there is no such data in the cache (a cache miss), then nil will be
|
|
183
|
-
# returned. However, if a block has been passed, that block will be
|
|
184
|
-
# in the event of a cache miss. The return value of the
|
|
185
|
-
# written to the cache under the given cache key, and that
|
|
186
|
-
# will be returned.
|
|
196
|
+
# If there is no such data in the cache (a cache miss), then +nil+ will be
|
|
197
|
+
# returned. However, if a block has been passed, that block will be passed
|
|
198
|
+
# the key and executed in the event of a cache miss. The return value of the
|
|
199
|
+
# block will be written to the cache under the given cache key, and that
|
|
200
|
+
# return value will be returned.
|
|
187
201
|
#
|
|
188
|
-
# cache.write(
|
|
189
|
-
# cache.fetch(
|
|
202
|
+
# cache.write('today', 'Monday')
|
|
203
|
+
# cache.fetch('today') # => "Monday"
|
|
190
204
|
#
|
|
191
|
-
# cache.fetch(
|
|
192
|
-
# cache.fetch(
|
|
193
|
-
#
|
|
205
|
+
# cache.fetch('city') # => nil
|
|
206
|
+
# cache.fetch('city') do
|
|
207
|
+
# 'Duckburgh'
|
|
194
208
|
# end
|
|
195
|
-
# cache.fetch(
|
|
209
|
+
# cache.fetch('city') # => "Duckburgh"
|
|
196
210
|
#
|
|
197
211
|
# You may also specify additional options via the +options+ argument.
|
|
198
|
-
# Setting <tt
|
|
212
|
+
# Setting <tt>force: true</tt> will force a cache miss:
|
|
199
213
|
#
|
|
200
|
-
# cache.write(
|
|
201
|
-
# cache.fetch(
|
|
214
|
+
# cache.write('today', 'Monday')
|
|
215
|
+
# cache.fetch('today', force: true) # => nil
|
|
202
216
|
#
|
|
203
217
|
# Setting <tt>:compress</tt> will store a large cache entry set by the call
|
|
204
218
|
# in a compressed format.
|
|
205
219
|
#
|
|
206
|
-
#
|
|
207
220
|
# Setting <tt>:expires_in</tt> will set an expiration time on the cache.
|
|
208
221
|
# All caches support auto-expiring content after a specified number of
|
|
209
222
|
# seconds. This value can be specified as an option to the constructor
|
|
210
223
|
# (in which case all entries will be affected), or it can be supplied to
|
|
211
224
|
# the +fetch+ or +write+ method to effect just one entry.
|
|
212
225
|
#
|
|
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
|
-
#
|
|
226
|
+
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
|
|
227
|
+
# cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
|
|
228
|
+
#
|
|
229
|
+
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where
|
|
230
|
+
# a cache entry is used very frequently and is under heavy load. If a
|
|
231
|
+
# cache expires and due to heavy load seven different processes will try
|
|
232
|
+
# to read data natively and then they all will try to write to cache. To
|
|
233
|
+
# avoid that case the first process to find an expired cache entry will
|
|
234
|
+
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.
|
|
235
|
+
# Yes, this process is extending the time for a stale value by another few
|
|
236
|
+
# seconds. Because of extended life of the previous cache, other processes
|
|
237
|
+
# will continue to use slightly stale data for a just a big longer. In the
|
|
238
|
+
# meantime that first process will go ahead and will write into cache the
|
|
239
|
+
# new value. After that all the processes will start getting new value.
|
|
240
|
+
# The key is to keep <tt>:race_condition_ttl</tt> small.
|
|
241
|
+
#
|
|
242
|
+
# If the process regenerating the entry errors out, the entry will be
|
|
243
|
+
# regenerated after the specified number of seconds. Also note that the
|
|
244
|
+
# life of stale cache is extended only if it expired recently. Otherwise
|
|
245
|
+
# a new value is generated and <tt>:race_condition_ttl</tt> does not play
|
|
246
|
+
# any role.
|
|
231
247
|
#
|
|
232
248
|
# # Set all values to expire after one minute.
|
|
233
|
-
# cache = ActiveSupport::Cache::MemoryStore.new(:
|
|
249
|
+
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
|
|
234
250
|
#
|
|
235
|
-
# cache.write(
|
|
251
|
+
# cache.write('foo', 'original value')
|
|
236
252
|
# val_1 = nil
|
|
237
253
|
# val_2 = nil
|
|
238
254
|
# sleep 60
|
|
239
255
|
#
|
|
240
256
|
# Thread.new do
|
|
241
|
-
# val_1 = cache.fetch(
|
|
257
|
+
# val_1 = cache.fetch('foo', race_condition_ttl: 10) do
|
|
242
258
|
# sleep 1
|
|
243
|
-
#
|
|
259
|
+
# 'new value 1'
|
|
244
260
|
# end
|
|
245
261
|
# end
|
|
246
262
|
#
|
|
247
263
|
# Thread.new do
|
|
248
|
-
# val_2 = cache.fetch(
|
|
249
|
-
#
|
|
264
|
+
# val_2 = cache.fetch('foo', race_condition_ttl: 10) do
|
|
265
|
+
# 'new value 2'
|
|
250
266
|
# end
|
|
251
267
|
# end
|
|
252
268
|
#
|
|
253
269
|
# # val_1 => "new value 1"
|
|
254
270
|
# # val_2 => "original value"
|
|
255
271
|
# # sleep 10 # First thread extend the life of cache by another 10 seconds
|
|
256
|
-
# # cache.fetch(
|
|
272
|
+
# # cache.fetch('foo') => "new value 1"
|
|
257
273
|
#
|
|
258
274
|
# 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.
|
|
275
|
+
# Internally, #fetch calls #read_entry, and calls #write_entry on a cache
|
|
276
|
+
# miss. +options+ will be passed to the #read and #write calls.
|
|
261
277
|
#
|
|
262
278
|
# For example, MemCacheStore's #write method supports the +:raw+
|
|
263
279
|
# option, which tells the memcached server to store all values as strings.
|
|
264
280
|
# We can use this option with #fetch too:
|
|
265
281
|
#
|
|
266
282
|
# cache = ActiveSupport::Cache::MemCacheStore.new
|
|
267
|
-
# cache.fetch("foo", :
|
|
283
|
+
# cache.fetch("foo", force: true, raw: true) do
|
|
268
284
|
# :bar
|
|
269
285
|
# end
|
|
270
|
-
# cache.fetch(
|
|
286
|
+
# cache.fetch('foo') # => "bar"
|
|
271
287
|
def fetch(name, options = nil)
|
|
272
288
|
if block_given?
|
|
273
289
|
options = merged_options(options)
|
|
274
290
|
key = namespaced_key(name, options)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
read_entry(key, options)
|
|
279
|
-
end
|
|
280
|
-
end
|
|
281
|
-
if entry && entry.expired?
|
|
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
|
|
290
|
-
end
|
|
291
|
+
|
|
292
|
+
cached_entry = find_cached_entry(key, name, options) unless options[:force]
|
|
293
|
+
entry = handle_expired_entry(cached_entry, key, options)
|
|
291
294
|
|
|
292
295
|
if entry
|
|
293
|
-
|
|
294
|
-
entry.value
|
|
296
|
+
get_entry_value(entry, name, options)
|
|
295
297
|
else
|
|
296
|
-
|
|
297
|
-
yield
|
|
298
|
-
end
|
|
299
|
-
write(name, result, options)
|
|
300
|
-
result
|
|
298
|
+
save_block_result_to_cache(name, options) { |_name| yield _name }
|
|
301
299
|
end
|
|
302
300
|
else
|
|
303
301
|
read(name, options)
|
|
@@ -306,7 +304,7 @@ module ActiveSupport
|
|
|
306
304
|
|
|
307
305
|
# Fetches data from the cache, using the given key. If there is data in
|
|
308
306
|
# the cache with the given key, then that data is returned. Otherwise,
|
|
309
|
-
# nil is returned.
|
|
307
|
+
# +nil+ is returned.
|
|
310
308
|
#
|
|
311
309
|
# Options are passed to the underlying cache implementation.
|
|
312
310
|
def read(name, options = nil)
|
|
@@ -359,7 +357,7 @@ module ActiveSupport
|
|
|
359
357
|
# Options are passed to the underlying cache implementation.
|
|
360
358
|
def write(name, value, options = nil)
|
|
361
359
|
options = merged_options(options)
|
|
362
|
-
instrument(:write, name, options) do
|
|
360
|
+
instrument(:write, name, options) do
|
|
363
361
|
entry = Entry.new(value, options)
|
|
364
362
|
write_entry(namespaced_key(name, options), entry, options)
|
|
365
363
|
end
|
|
@@ -370,23 +368,19 @@ module ActiveSupport
|
|
|
370
368
|
# Options are passed to the underlying cache implementation.
|
|
371
369
|
def delete(name, options = nil)
|
|
372
370
|
options = merged_options(options)
|
|
373
|
-
instrument(:delete, name) do
|
|
371
|
+
instrument(:delete, name) do
|
|
374
372
|
delete_entry(namespaced_key(name, options), options)
|
|
375
373
|
end
|
|
376
374
|
end
|
|
377
375
|
|
|
378
|
-
# Return true if the cache contains an entry for the given key.
|
|
376
|
+
# Return +true+ if the cache contains an entry for the given key.
|
|
379
377
|
#
|
|
380
378
|
# Options are passed to the underlying cache implementation.
|
|
381
379
|
def exist?(name, options = nil)
|
|
382
380
|
options = merged_options(options)
|
|
383
|
-
instrument(:exist?, name) do
|
|
381
|
+
instrument(:exist?, name) do
|
|
384
382
|
entry = read_entry(namespaced_key(name, options), options)
|
|
385
|
-
|
|
386
|
-
true
|
|
387
|
-
else
|
|
388
|
-
false
|
|
389
|
-
end
|
|
383
|
+
entry && !entry.expired?
|
|
390
384
|
end
|
|
391
385
|
end
|
|
392
386
|
|
|
@@ -408,7 +402,7 @@ module ActiveSupport
|
|
|
408
402
|
raise NotImplementedError.new("#{self.class.name} does not support increment")
|
|
409
403
|
end
|
|
410
404
|
|
|
411
|
-
#
|
|
405
|
+
# Decrement an integer value in the cache.
|
|
412
406
|
#
|
|
413
407
|
# Options are passed to the underlying cache implementation.
|
|
414
408
|
#
|
|
@@ -437,9 +431,10 @@ module ActiveSupport
|
|
|
437
431
|
end
|
|
438
432
|
|
|
439
433
|
protected
|
|
440
|
-
# Add the namespace defined in the options to a pattern designed to
|
|
441
|
-
# Implementations that support delete_matched should call
|
|
442
|
-
# a pattern that matches names into one that
|
|
434
|
+
# Add the namespace defined in the options to a pattern designed to
|
|
435
|
+
# match keys. Implementations that support delete_matched should call
|
|
436
|
+
# this method to translate a pattern that matches names into one that
|
|
437
|
+
# matches namespaced keys.
|
|
443
438
|
def key_matcher(pattern, options)
|
|
444
439
|
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
|
|
445
440
|
if prefix
|
|
@@ -455,17 +450,20 @@ module ActiveSupport
|
|
|
455
450
|
end
|
|
456
451
|
end
|
|
457
452
|
|
|
458
|
-
# Read an entry from the cache implementation. Subclasses must implement
|
|
453
|
+
# Read an entry from the cache implementation. Subclasses must implement
|
|
454
|
+
# this method.
|
|
459
455
|
def read_entry(key, options) # :nodoc:
|
|
460
456
|
raise NotImplementedError.new
|
|
461
457
|
end
|
|
462
458
|
|
|
463
|
-
# Write an entry to the cache implementation. Subclasses must implement
|
|
459
|
+
# Write an entry to the cache implementation. Subclasses must implement
|
|
460
|
+
# this method.
|
|
464
461
|
def write_entry(key, entry, options) # :nodoc:
|
|
465
462
|
raise NotImplementedError.new
|
|
466
463
|
end
|
|
467
464
|
|
|
468
|
-
# Delete an entry from the cache implementation. Subclasses must
|
|
465
|
+
# Delete an entry from the cache implementation. Subclasses must
|
|
466
|
+
# implement this method.
|
|
469
467
|
def delete_entry(key, options) # :nodoc:
|
|
470
468
|
raise NotImplementedError.new
|
|
471
469
|
end
|
|
@@ -481,7 +479,7 @@ module ActiveSupport
|
|
|
481
479
|
end
|
|
482
480
|
|
|
483
481
|
# Expand key to be a consistent string value. Invoke +cache_key+ if
|
|
484
|
-
# object responds to +cache_key+. Otherwise, to_param method will be
|
|
482
|
+
# object responds to +cache_key+. Otherwise, +to_param+ method will be
|
|
485
483
|
# called. If the key is a Hash, then keys will be sorted alphabetically.
|
|
486
484
|
def expanded_key(key) # :nodoc:
|
|
487
485
|
return key.cache_key.to_s if key.respond_to?(:cache_key)
|
|
@@ -500,7 +498,8 @@ module ActiveSupport
|
|
|
500
498
|
key.to_param
|
|
501
499
|
end
|
|
502
500
|
|
|
503
|
-
# Prefix a key with the namespace. Namespace and key will be delimited
|
|
501
|
+
# Prefix a key with the namespace. Namespace and key will be delimited
|
|
502
|
+
# with a colon.
|
|
504
503
|
def namespaced_key(key, options)
|
|
505
504
|
key = expanded_key(key)
|
|
506
505
|
namespace = options[:namespace] if options
|
|
@@ -525,114 +524,160 @@ module ActiveSupport
|
|
|
525
524
|
return unless logger && logger.debug? && !silence?
|
|
526
525
|
logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
|
|
527
526
|
end
|
|
528
|
-
end
|
|
529
527
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
528
|
+
def find_cached_entry(key, name, options)
|
|
529
|
+
instrument(:read, name, options) do |payload|
|
|
530
|
+
payload[:super_operation] = :fetch if payload
|
|
531
|
+
read_entry(key, options)
|
|
532
|
+
end
|
|
533
|
+
end
|
|
536
534
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
535
|
+
def handle_expired_entry(entry, key, options)
|
|
536
|
+
if entry && entry.expired?
|
|
537
|
+
race_ttl = options[:race_condition_ttl].to_i
|
|
538
|
+
if race_ttl && (Time.now.to_f - entry.expires_at <= race_ttl)
|
|
539
|
+
# When an entry has :race_condition_ttl defined, put the stale entry back into the cache
|
|
540
|
+
# for a brief period while the entry is begin recalculated.
|
|
541
|
+
entry.expires_at = Time.now + race_ttl
|
|
542
|
+
write_entry(key, entry, :expires_in => race_ttl * 2)
|
|
543
|
+
else
|
|
544
|
+
delete_entry(key, options)
|
|
545
|
+
end
|
|
546
|
+
entry = nil
|
|
547
|
+
end
|
|
547
548
|
entry
|
|
548
549
|
end
|
|
549
|
-
|
|
550
|
+
|
|
551
|
+
def get_entry_value(entry, name, options)
|
|
552
|
+
instrument(:fetch_hit, name, options) { |payload| }
|
|
553
|
+
entry.value
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def save_block_result_to_cache(name, options)
|
|
557
|
+
result = instrument(:generate, name, options) do |payload|
|
|
558
|
+
yield(name)
|
|
559
|
+
end
|
|
560
|
+
write(name, result, options)
|
|
561
|
+
result
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# This class is used to represent cache entries. Cache entries have a value and an optional
|
|
566
|
+
# expiration time. The expiration time is used to support the :race_condition_ttl option
|
|
567
|
+
# on the cache.
|
|
568
|
+
#
|
|
569
|
+
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
|
|
570
|
+
# using short instance variable names that are lazily defined.
|
|
571
|
+
class Entry # :nodoc:
|
|
572
|
+
DEFAULT_COMPRESS_LIMIT = 16.kilobytes
|
|
550
573
|
|
|
551
574
|
# Create a new cache entry for the specified value. Options supported are
|
|
552
575
|
# +:compress+, +:compress_threshold+, and +:expires_in+.
|
|
553
576
|
def initialize(value, options = {})
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
@created_at = Time.now.to_f
|
|
558
|
-
if value.nil?
|
|
559
|
-
@value = nil
|
|
577
|
+
if should_compress?(value, options)
|
|
578
|
+
@value = compress(value)
|
|
579
|
+
@compressed = true
|
|
560
580
|
else
|
|
561
|
-
@value =
|
|
562
|
-
if should_compress?(@value, options)
|
|
563
|
-
@value = Zlib::Deflate.deflate(@value)
|
|
564
|
-
@compressed = true
|
|
565
|
-
end
|
|
581
|
+
@value = value
|
|
566
582
|
end
|
|
583
|
+
@created_at = Time.now.to_f
|
|
584
|
+
@expires_in = options[:expires_in]
|
|
585
|
+
@expires_in = @expires_in.to_f if @expires_in
|
|
567
586
|
end
|
|
568
587
|
|
|
569
|
-
# Get the raw value. This value may be serialized and compressed.
|
|
570
|
-
def raw_value
|
|
571
|
-
@value
|
|
572
|
-
end
|
|
573
|
-
|
|
574
|
-
# Get the value stored in the cache.
|
|
575
588
|
def value
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
# strings.
|
|
579
|
-
if @value
|
|
580
|
-
# In rails 3.1 and earlier values in entries did not marshaled without
|
|
581
|
-
# options[:compress] and if it's Numeric.
|
|
582
|
-
# But after commit a263f377978fc07515b42808ebc1f7894fafaa3a
|
|
583
|
-
# all values in entries are marshalled. And after that code below expects
|
|
584
|
-
# that all values in entries will be marshaled (and will be strings).
|
|
585
|
-
# So here we need a check for old ones.
|
|
586
|
-
begin
|
|
587
|
-
Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
|
|
588
|
-
rescue TypeError
|
|
589
|
-
compressed? ? Zlib::Inflate.inflate(@value) : @value
|
|
590
|
-
end
|
|
591
|
-
end
|
|
592
|
-
end
|
|
593
|
-
|
|
594
|
-
def compressed?
|
|
595
|
-
@compressed
|
|
589
|
+
convert_version_4beta1_entry! if defined?(@v)
|
|
590
|
+
compressed? ? uncompress(@value) : @value
|
|
596
591
|
end
|
|
597
592
|
|
|
598
|
-
# Check if the entry is expired. The +expires_in+ parameter can override
|
|
599
|
-
# value set when the entry was created.
|
|
593
|
+
# Check if the entry is expired. The +expires_in+ parameter can override
|
|
594
|
+
# the value set when the entry was created.
|
|
600
595
|
def expired?
|
|
596
|
+
convert_version_4beta1_entry! if defined?(@value)
|
|
601
597
|
@expires_in && @created_at + @expires_in <= Time.now.to_f
|
|
602
598
|
end
|
|
603
599
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
600
|
+
def expires_at
|
|
601
|
+
@expires_in ? @created_at + @expires_in : nil
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
def expires_at=(value)
|
|
605
|
+
if value
|
|
606
|
+
@expires_in = value.to_f - @created_at
|
|
608
607
|
else
|
|
609
608
|
@expires_in = nil
|
|
610
609
|
end
|
|
611
610
|
end
|
|
612
611
|
|
|
613
|
-
#
|
|
614
|
-
|
|
615
|
-
@expires_in ? @created_at + @expires_in : nil
|
|
616
|
-
end
|
|
617
|
-
|
|
618
|
-
# Returns the size of the cached value. This could be less than value.size
|
|
619
|
-
# if the data is compressed.
|
|
612
|
+
# Returns the size of the cached value. This could be less than
|
|
613
|
+
# <tt>value.size</tt> if the data is compressed.
|
|
620
614
|
def size
|
|
621
|
-
if @
|
|
622
|
-
|
|
615
|
+
if defined?(@s)
|
|
616
|
+
@s
|
|
623
617
|
else
|
|
624
|
-
|
|
618
|
+
case value
|
|
619
|
+
when NilClass
|
|
620
|
+
0
|
|
621
|
+
when String
|
|
622
|
+
@value.bytesize
|
|
623
|
+
else
|
|
624
|
+
@s = Marshal.dump(@value).bytesize
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
# Duplicate the value in a class. This is used by cache implementations that don't natively
|
|
630
|
+
# serialize entries to protect against accidental cache modifications.
|
|
631
|
+
def dup_value!
|
|
632
|
+
convert_version_4beta1_entry! if defined?(@v)
|
|
633
|
+
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
|
|
634
|
+
if @value.is_a?(String)
|
|
635
|
+
@value = @value.dup
|
|
636
|
+
else
|
|
637
|
+
@value = Marshal.load(Marshal.dump(@value))
|
|
638
|
+
end
|
|
625
639
|
end
|
|
626
640
|
end
|
|
627
641
|
|
|
628
642
|
private
|
|
629
|
-
def should_compress?(
|
|
630
|
-
if options[:compress]
|
|
643
|
+
def should_compress?(value, options)
|
|
644
|
+
if value && options[:compress]
|
|
631
645
|
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
|
|
632
|
-
|
|
646
|
+
serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
|
|
647
|
+
return true if serialized_value_size >= compress_threshold
|
|
633
648
|
end
|
|
634
649
|
false
|
|
635
650
|
end
|
|
651
|
+
|
|
652
|
+
def compressed?
|
|
653
|
+
defined?(@compressed) ? @compressed : false
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
def compress(value)
|
|
657
|
+
Zlib::Deflate.deflate(Marshal.dump(value))
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def uncompress(value)
|
|
661
|
+
Marshal.load(Zlib::Inflate.inflate(value))
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
# The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
|
|
665
|
+
# to ensure that cache entries created under the old version still work with the new class definition.
|
|
666
|
+
def convert_version_4beta1_entry!
|
|
667
|
+
if defined?(@v)
|
|
668
|
+
@value = @v
|
|
669
|
+
remove_instance_variable(:@v)
|
|
670
|
+
end
|
|
671
|
+
if defined?(@c)
|
|
672
|
+
@compressed = @c
|
|
673
|
+
remove_instance_variable(:@c)
|
|
674
|
+
end
|
|
675
|
+
if defined?(@x) && @x
|
|
676
|
+
@created_at ||= Time.now.to_f
|
|
677
|
+
@expires_in = @x - @created_at
|
|
678
|
+
remove_instance_variable(:@x)
|
|
679
|
+
end
|
|
680
|
+
end
|
|
636
681
|
end
|
|
637
682
|
end
|
|
638
683
|
end
|