activesupport 7.0.0 → 7.2.2.1
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 +156 -255
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/active_support/actionable_error.rb +3 -1
- data/lib/active_support/array_inquirer.rb +3 -1
- data/lib/active_support/backtrace_cleaner.rb +41 -9
- data/lib/active_support/benchmarkable.rb +1 -0
- data/lib/active_support/broadcast_logger.rb +251 -0
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +49 -17
- data/lib/active_support/cache/mem_cache_store.rb +111 -129
- data/lib/active_support/cache/memory_store.rb +81 -26
- data/lib/active_support/cache/null_store.rb +6 -0
- data/lib/active_support/cache/redis_cache_store.rb +175 -154
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +31 -13
- data/lib/active_support/cache.rb +457 -377
- data/lib/active_support/callbacks.rb +123 -139
- data/lib/active_support/code_generator.rb +15 -10
- data/lib/active_support/concern.rb +4 -2
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/configurable.rb +12 -2
- data/lib/active_support/core_ext/array/conversions.rb +7 -9
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/class/subclasses.rb +4 -15
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/calculations.rb +20 -5
- data/lib/active_support/core_ext/date/conversions.rb +15 -16
- data/lib/active_support/core_ext/date.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +29 -2
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +15 -15
- data/lib/active_support/core_ext/date_time.rb +0 -1
- data/lib/active_support/core_ext/digest/uuid.rb +7 -10
- data/lib/active_support/core_ext/enumerable.rb +51 -101
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/file/atomic.rb +2 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -2
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +7 -7
- data/lib/active_support/core_ext/integer/inflections.rb +12 -12
- data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +38 -20
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -119
- data/lib/active_support/core_ext/module/deprecation.rb +12 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +77 -75
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
- data/lib/active_support/core_ext/object/duplicable.rb +25 -16
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
- data/lib/active_support/core_ext/object/json.rb +17 -7
- data/lib/active_support/core_ext/object/to_query.rb +0 -2
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +9 -9
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +2 -0
- data/lib/active_support/core_ext/pathname.rb +1 -0
- data/lib/active_support/core_ext/range/conversions.rb +32 -11
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +2 -6
- data/lib/active_support/core_ext/string/conversions.rb +3 -3
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +16 -9
- data/lib/active_support/core_ext/string/inquiry.rb +1 -1
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +39 -150
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +42 -32
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +13 -15
- data/lib/active_support/core_ext/time/zones.rb +8 -9
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +53 -46
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +9 -16
- data/lib/active_support/deprecation/behaviors.rb +65 -42
- data/lib/active_support/deprecation/constant_accessor.rb +47 -25
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +6 -8
- data/lib/active_support/deprecation/method_wrappers.rb +6 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
- data/lib/active_support/deprecation/reporting.rb +49 -27
- data/lib/active_support/deprecation.rb +39 -9
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +66 -175
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -4
- data/lib/active_support/duration.rb +13 -7
- data/lib/active_support/encrypted_configuration.rb +63 -10
- data/lib/active_support/encrypted_file.rb +29 -13
- data/lib/active_support/environment_inquirer.rb +22 -2
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +160 -36
- data/lib/active_support/evented_file_update_checker.rb +19 -7
- data/lib/active_support/execution_wrapper.rb +23 -28
- data/lib/active_support/file_update_checker.rb +5 -3
- data/lib/active_support/fork_tracker.rb +4 -32
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +41 -25
- data/lib/active_support/html_safe_translation.rb +19 -6
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +20 -13
- data/lib/active_support/inflector/inflections.rb +2 -0
- data/lib/active_support/inflector/methods.rb +28 -18
- data/lib/active_support/inflector/transliterate.rb +4 -2
- data/lib/active_support/isolated_execution_state.rb +39 -19
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +25 -43
- data/lib/active_support/key_generator.rb +13 -5
- data/lib/active_support/lazy_load_hooks.rb +33 -7
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +76 -36
- data/lib/active_support/logger.rb +22 -60
- data/lib/active_support/logger_thread_safe_level.rb +10 -32
- data/lib/active_support/message_encryptor.rb +200 -55
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +305 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +220 -89
- data/lib/active_support/message_verifiers.rb +135 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +111 -45
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +4 -2
- data/lib/active_support/multibyte/unicode.rb +9 -37
- data/lib/active_support/notifications/fanout.rb +248 -87
- data/lib/active_support/notifications/instrumenter.rb +93 -25
- data/lib/active_support/notifications.rb +38 -31
- data/lib/active_support/number_helper/number_converter.rb +16 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
- data/lib/active_support/number_helper.rb +379 -317
- data/lib/active_support/option_merger.rb +4 -4
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +68 -16
- data/lib/active_support/parameter_filter.rb +103 -84
- data/lib/active_support/proxy_object.rb +8 -3
- data/lib/active_support/railtie.rb +30 -25
- data/lib/active_support/reloader.rb +13 -5
- data/lib/active_support/rescuable.rb +12 -10
- data/lib/active_support/secure_compare_rotator.rb +17 -10
- data/lib/active_support/string_inquirer.rb +4 -2
- data/lib/active_support/subscriber.rb +10 -27
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +64 -25
- data/lib/active_support/test_case.rb +160 -7
- data/lib/active_support/testing/assertions.rb +29 -13
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/deprecation.rb +20 -27
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +46 -33
- data/lib/active_support/testing/method_call_assertions.rb +7 -8
- data/lib/active_support/testing/parallelization/server.rb +3 -0
- data/lib/active_support/testing/parallelize_executor.rb +8 -3
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/strict_warnings.rb +43 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +38 -16
- data/lib/active_support/time_with_zone.rb +28 -54
- data/lib/active_support/values/time_zone.rb +26 -15
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -10
- data/lib/active_support/xml_mini/nokogiri.rb +1 -1
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +1 -1
- data/lib/active_support/xml_mini.rb +13 -4
- data/lib/active_support.rb +15 -3
- metadata +142 -21
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -26
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -22
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -26
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -7
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -22
- data/lib/active_support/core_ext/uri.rb +0 -5
- data/lib/active_support/deprecation/instance_delegator.rb +0 -38
- data/lib/active_support/per_thread_registry.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
@@ -6,10 +6,9 @@ require "uri/common"
|
|
6
6
|
|
7
7
|
module ActiveSupport
|
8
8
|
module Cache
|
9
|
-
#
|
9
|
+
# = \File \Cache \Store
|
10
10
|
#
|
11
|
-
#
|
12
|
-
# an in-memory cache inside of a block.
|
11
|
+
# A cache store implementation which stores everything on the filesystem.
|
13
12
|
class FileStore < Store
|
14
13
|
attr_reader :cache_path
|
15
14
|
|
@@ -46,22 +45,42 @@ module ActiveSupport
|
|
46
45
|
end
|
47
46
|
end
|
48
47
|
|
49
|
-
#
|
50
|
-
#
|
48
|
+
# Increment a cached integer value. Returns the updated value.
|
49
|
+
#
|
50
|
+
# If the key is unset, it starts from +0+:
|
51
|
+
#
|
52
|
+
# cache.increment("foo") # => 1
|
53
|
+
# cache.increment("bar", 100) # => 100
|
54
|
+
#
|
55
|
+
# To set a specific value, call #write:
|
56
|
+
#
|
57
|
+
# cache.write("baz", 5)
|
58
|
+
# cache.increment("baz") # => 6
|
59
|
+
#
|
51
60
|
def increment(name, amount = 1, options = nil)
|
52
61
|
modify_value(name, amount, options)
|
53
62
|
end
|
54
63
|
|
55
|
-
#
|
56
|
-
#
|
64
|
+
# Decrement a cached integer value. Returns the updated value.
|
65
|
+
#
|
66
|
+
# If the key is unset, it will be set to +-amount+.
|
67
|
+
#
|
68
|
+
# cache.decrement("foo") # => -1
|
69
|
+
#
|
70
|
+
# To set a specific value, call #write:
|
71
|
+
#
|
72
|
+
# cache.write("baz", 5)
|
73
|
+
# cache.decrement("baz") # => 4
|
74
|
+
#
|
57
75
|
def decrement(name, amount = 1, options = nil)
|
58
76
|
modify_value(name, -amount, options)
|
59
77
|
end
|
60
78
|
|
61
79
|
def delete_matched(matcher, options = nil)
|
62
80
|
options = merged_options(options)
|
81
|
+
matcher = key_matcher(matcher, options)
|
82
|
+
|
63
83
|
instrument(:delete_matched, matcher.inspect) do
|
64
|
-
matcher = key_matcher(matcher, options)
|
65
84
|
search_dir(cache_path) do |path|
|
66
85
|
key = file_path_key(path)
|
67
86
|
delete_entry(path, **options) if key.match(matcher)
|
@@ -69,6 +88,10 @@ module ActiveSupport
|
|
69
88
|
end
|
70
89
|
end
|
71
90
|
|
91
|
+
def inspect # :nodoc:
|
92
|
+
"#<#{self.class.name} cache_path=#{@cache_path}, options=#{@options.inspect}>"
|
93
|
+
end
|
94
|
+
|
72
95
|
private
|
73
96
|
def read_entry(key, **options)
|
74
97
|
if payload = read_serialized_entry(key, **options)
|
@@ -106,6 +129,8 @@ module ActiveSupport
|
|
106
129
|
raise if File.exist?(key)
|
107
130
|
false
|
108
131
|
end
|
132
|
+
else
|
133
|
+
false
|
109
134
|
end
|
110
135
|
end
|
111
136
|
|
@@ -152,7 +177,7 @@ module ActiveSupport
|
|
152
177
|
|
153
178
|
# Translate a file path into a key.
|
154
179
|
def file_path_key(path)
|
155
|
-
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
|
180
|
+
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last.delete(File::SEPARATOR)
|
156
181
|
URI.decode_www_form_component(fname, Encoding::UTF_8)
|
157
182
|
end
|
158
183
|
|
@@ -182,17 +207,24 @@ module ActiveSupport
|
|
182
207
|
end
|
183
208
|
end
|
184
209
|
|
185
|
-
# Modifies the amount of an
|
186
|
-
# If the key is not found
|
210
|
+
# Modifies the amount of an integer value that is stored in the cache.
|
211
|
+
# If the key is not found it is created and set to +amount+.
|
187
212
|
def modify_value(name, amount, options)
|
188
|
-
|
213
|
+
options = merged_options(options)
|
214
|
+
key = normalize_key(name, options)
|
215
|
+
version = normalize_version(name, options)
|
216
|
+
amount = Integer(amount)
|
189
217
|
|
190
|
-
lock_file(
|
191
|
-
|
218
|
+
lock_file(key) do
|
219
|
+
entry = read_entry(key, **options)
|
192
220
|
|
193
|
-
if
|
194
|
-
|
195
|
-
|
221
|
+
if !entry || entry.expired? || entry.mismatched?(version)
|
222
|
+
write(name, amount, options)
|
223
|
+
amount
|
224
|
+
else
|
225
|
+
num = entry.value.to_i + amount
|
226
|
+
entry = Entry.new(num, expires_at: entry.expires_at, version: entry.version)
|
227
|
+
write_entry(key, entry)
|
196
228
|
num
|
197
229
|
end
|
198
230
|
end
|
@@ -3,16 +3,20 @@
|
|
3
3
|
begin
|
4
4
|
require "dalli"
|
5
5
|
rescue LoadError => e
|
6
|
-
|
6
|
+
warn "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
|
7
7
|
raise e
|
8
8
|
end
|
9
9
|
|
10
|
+
require "connection_pool"
|
10
11
|
require "delegate"
|
11
12
|
require "active_support/core_ext/enumerable"
|
12
13
|
require "active_support/core_ext/array/extract_options"
|
14
|
+
require "active_support/core_ext/numeric/time"
|
13
15
|
|
14
16
|
module ActiveSupport
|
15
17
|
module Cache
|
18
|
+
# = Memcached \Cache \Store
|
19
|
+
#
|
16
20
|
# A cache store implementation which stores data in Memcached:
|
17
21
|
# https://memcached.org
|
18
22
|
#
|
@@ -20,12 +24,16 @@ module ActiveSupport
|
|
20
24
|
#
|
21
25
|
# Special features:
|
22
26
|
# - Clustering and load balancing. One can specify multiple memcached servers,
|
23
|
-
# and MemCacheStore will load balance between all available servers. If a
|
24
|
-
# server goes down, then MemCacheStore will ignore it until it comes back up.
|
27
|
+
# and +MemCacheStore+ will load balance between all available servers. If a
|
28
|
+
# server goes down, then +MemCacheStore+ will ignore it until it comes back up.
|
25
29
|
#
|
26
|
-
# MemCacheStore implements the Strategy::LocalCache strategy which
|
27
|
-
# an in-memory cache inside of a block.
|
30
|
+
# +MemCacheStore+ implements the Strategy::LocalCache strategy which
|
31
|
+
# implements an in-memory cache inside of a block.
|
28
32
|
class MemCacheStore < Store
|
33
|
+
# These options represent behavior overridden by this implementation and should
|
34
|
+
# not be allowed to get down to the Dalli client
|
35
|
+
OVERRIDDEN_OPTIONS = UNIVERSAL_OPTIONS
|
36
|
+
|
29
37
|
# Advertise cache versioning support.
|
30
38
|
def self.supports_cache_versioning?
|
31
39
|
true
|
@@ -33,46 +41,7 @@ module ActiveSupport
|
|
33
41
|
|
34
42
|
prepend Strategy::LocalCache
|
35
43
|
|
36
|
-
|
37
|
-
class DupLocalStore < DelegateClass(Strategy::LocalCache::LocalStore)
|
38
|
-
def write_entry(_key, entry)
|
39
|
-
if entry.is_a?(Entry)
|
40
|
-
entry.dup_value!
|
41
|
-
end
|
42
|
-
super
|
43
|
-
end
|
44
|
-
|
45
|
-
def fetch_entry(key)
|
46
|
-
entry = super do
|
47
|
-
new_entry = yield
|
48
|
-
if entry.is_a?(Entry)
|
49
|
-
new_entry.dup_value!
|
50
|
-
end
|
51
|
-
new_entry
|
52
|
-
end
|
53
|
-
entry = entry.dup
|
54
|
-
|
55
|
-
if entry.is_a?(Entry)
|
56
|
-
entry.dup_value!
|
57
|
-
end
|
58
|
-
|
59
|
-
entry
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
def local_cache
|
65
|
-
if ActiveSupport::Cache.format_version == 6.1
|
66
|
-
if local_cache = super
|
67
|
-
DupLocalStore.new(local_cache)
|
68
|
-
end
|
69
|
-
else
|
70
|
-
super
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
prepend DupLocalCache
|
75
|
-
|
44
|
+
KEY_MAX_SIZE = 250
|
76
45
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
|
77
46
|
|
78
47
|
# Creates a new Dalli::Client instance with specified addresses and options.
|
@@ -90,22 +59,21 @@ module ActiveSupport
|
|
90
59
|
addresses = nil if addresses.compact.empty?
|
91
60
|
pool_options = retrieve_pool_options(options)
|
92
61
|
|
93
|
-
if pool_options
|
94
|
-
Dalli::Client.new(addresses, options)
|
95
|
-
else
|
96
|
-
ensure_connection_pool_added!
|
62
|
+
if pool_options
|
97
63
|
ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
|
64
|
+
else
|
65
|
+
Dalli::Client.new(addresses, options)
|
98
66
|
end
|
99
67
|
end
|
100
68
|
|
101
|
-
# Creates a new MemCacheStore object, with the given memcached server
|
69
|
+
# Creates a new +MemCacheStore+ object, with the given memcached server
|
102
70
|
# addresses. Each address is either a host name, or a host-with-port string
|
103
71
|
# in the form of "host_name:port". For example:
|
104
72
|
#
|
105
73
|
# ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
|
106
74
|
#
|
107
|
-
# If no addresses are provided, but ENV['MEMCACHE_SERVERS'] is defined, it will be used instead. Otherwise,
|
108
|
-
# MemCacheStore will connect to localhost:11211 (the default memcached port).
|
75
|
+
# If no addresses are provided, but <tt>ENV['MEMCACHE_SERVERS']</tt> is defined, it will be used instead. Otherwise,
|
76
|
+
# +MemCacheStore+ will connect to localhost:11211 (the default memcached port).
|
109
77
|
def initialize(*addresses)
|
110
78
|
addresses = addresses.flatten
|
111
79
|
options = addresses.extract_options!
|
@@ -115,41 +83,85 @@ module ActiveSupport
|
|
115
83
|
super(options)
|
116
84
|
|
117
85
|
unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
|
118
|
-
raise ArgumentError, "First argument must be an empty array,
|
119
|
-
end
|
120
|
-
if addresses.first.is_a?(Dalli::Client)
|
121
|
-
@data = addresses.first
|
122
|
-
else
|
123
|
-
mem_cache_options = options.dup
|
124
|
-
# The value "compress: false" prevents duplicate compression within Dalli.
|
125
|
-
mem_cache_options[:compress] = false
|
126
|
-
(UNIVERSAL_OPTIONS - %i(compress)).each { |name| mem_cache_options.delete(name) }
|
127
|
-
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
86
|
+
raise ArgumentError, "First argument must be an empty array, address, or array of addresses."
|
128
87
|
end
|
88
|
+
|
89
|
+
@mem_cache_options = options.dup
|
90
|
+
# The value "compress: false" prevents duplicate compression within Dalli.
|
91
|
+
@mem_cache_options[:compress] = false
|
92
|
+
(OVERRIDDEN_OPTIONS - %i(compress)).each { |name| @mem_cache_options.delete(name) }
|
93
|
+
@data = self.class.build_mem_cache(*(addresses + [@mem_cache_options]))
|
94
|
+
end
|
95
|
+
|
96
|
+
def inspect
|
97
|
+
instance = @data || @mem_cache_options
|
98
|
+
"#<#{self.class} options=#{options.inspect} mem_cache=#{instance.inspect}>"
|
129
99
|
end
|
130
100
|
|
131
|
-
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
101
|
+
##
|
102
|
+
# :method: write
|
103
|
+
# :call-seq: write(name, value, options = nil)
|
104
|
+
#
|
105
|
+
# Behaves the same as ActiveSupport::Cache::Store#write, but supports
|
106
|
+
# additional options specific to memcached.
|
107
|
+
#
|
108
|
+
# ==== Additional Options
|
109
|
+
#
|
110
|
+
# * <tt>raw: true</tt> - Sends the value directly to the server as raw
|
111
|
+
# bytes. The value must be a string or number. You can use memcached
|
112
|
+
# direct operations like +increment+ and +decrement+ only on raw values.
|
113
|
+
#
|
114
|
+
# * <tt>unless_exist: true</tt> - Prevents overwriting an existing cache
|
115
|
+
# entry.
|
116
|
+
|
117
|
+
# Increment a cached integer value using the memcached incr atomic operator.
|
118
|
+
# Returns the updated value.
|
119
|
+
#
|
120
|
+
# If the key is unset or has expired, it will be set to +amount+:
|
121
|
+
#
|
122
|
+
# cache.increment("foo") # => 1
|
123
|
+
# cache.increment("bar", 100) # => 100
|
124
|
+
#
|
125
|
+
# To set a specific value, call #write passing <tt>raw: true</tt>:
|
126
|
+
#
|
127
|
+
# cache.write("baz", 5, raw: true)
|
128
|
+
# cache.increment("baz") # => 6
|
129
|
+
#
|
130
|
+
# Incrementing a non-numeric value, or a value written without
|
131
|
+
# <tt>raw: true</tt>, will fail and return +nil+.
|
135
132
|
def increment(name, amount = 1, options = nil)
|
136
133
|
options = merged_options(options)
|
137
|
-
|
134
|
+
key = normalize_key(name, options)
|
135
|
+
|
136
|
+
instrument(:increment, key, amount: amount) do
|
138
137
|
rescue_error_with nil do
|
139
|
-
@data.with { |c| c.incr(
|
138
|
+
@data.with { |c| c.incr(key, amount, options[:expires_in], amount) }
|
140
139
|
end
|
141
140
|
end
|
142
141
|
end
|
143
142
|
|
144
|
-
# Decrement a cached value
|
145
|
-
#
|
146
|
-
#
|
147
|
-
# to
|
143
|
+
# Decrement a cached integer value using the memcached decr atomic operator.
|
144
|
+
# Returns the updated value.
|
145
|
+
#
|
146
|
+
# If the key is unset or has expired, it will be set to 0. Memcached
|
147
|
+
# does not support negative counters.
|
148
|
+
#
|
149
|
+
# cache.decrement("foo") # => 0
|
150
|
+
#
|
151
|
+
# To set a specific value, call #write passing <tt>raw: true</tt>:
|
152
|
+
#
|
153
|
+
# cache.write("baz", 5, raw: true)
|
154
|
+
# cache.decrement("baz") # => 4
|
155
|
+
#
|
156
|
+
# Decrementing a non-numeric value, or a value written without
|
157
|
+
# <tt>raw: true</tt>, will fail and return +nil+.
|
148
158
|
def decrement(name, amount = 1, options = nil)
|
149
159
|
options = merged_options(options)
|
150
|
-
|
160
|
+
key = normalize_key(name, options)
|
161
|
+
|
162
|
+
instrument(:decrement, key, amount: amount) do
|
151
163
|
rescue_error_with nil do
|
152
|
-
@data.with { |c| c.decr(
|
164
|
+
@data.with { |c| c.decr(key, amount, options[:expires_in], 0) }
|
153
165
|
end
|
154
166
|
end
|
155
167
|
end
|
@@ -166,54 +178,6 @@ module ActiveSupport
|
|
166
178
|
end
|
167
179
|
|
168
180
|
private
|
169
|
-
module Coders # :nodoc:
|
170
|
-
class << self
|
171
|
-
def [](version)
|
172
|
-
case version
|
173
|
-
when 6.1
|
174
|
-
Rails61Coder
|
175
|
-
when 7.0
|
176
|
-
Rails70Coder
|
177
|
-
else
|
178
|
-
raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}"
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
module Loader
|
184
|
-
def load(payload)
|
185
|
-
if payload.is_a?(Entry)
|
186
|
-
payload
|
187
|
-
else
|
188
|
-
Cache::Coders::Loader.load(payload)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
module Rails61Coder
|
194
|
-
include Loader
|
195
|
-
extend self
|
196
|
-
|
197
|
-
def dump(entry)
|
198
|
-
entry
|
199
|
-
end
|
200
|
-
|
201
|
-
def dump_compressed(entry, threshold)
|
202
|
-
entry.compressed(threshold)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
module Rails70Coder
|
207
|
-
include Cache::Coders::Rails70Coder
|
208
|
-
include Loader
|
209
|
-
extend self
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def default_coder
|
214
|
-
Coders[Cache.format_version]
|
215
|
-
end
|
216
|
-
|
217
181
|
# Read an entry from the cache.
|
218
182
|
def read_entry(key, **options)
|
219
183
|
deserialize_entry(read_serialized_entry(key, **options), **options)
|
@@ -237,10 +201,10 @@ module ActiveSupport
|
|
237
201
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
238
202
|
expires_in += 5.minutes
|
239
203
|
end
|
240
|
-
rescue_error_with
|
204
|
+
rescue_error_with nil do
|
241
205
|
# Don't pass compress option to Dalli since we are already dealing with compression.
|
242
206
|
options.delete(:compress)
|
243
|
-
@data.with { |c| c.send(method, key, payload, expires_in, **options) }
|
207
|
+
@data.with { |c| !!c.send(method, key, payload, expires_in, **options) }
|
244
208
|
end
|
245
209
|
end
|
246
210
|
|
@@ -248,14 +212,22 @@ module ActiveSupport
|
|
248
212
|
def read_multi_entries(names, **options)
|
249
213
|
keys_to_names = names.index_by { |name| normalize_key(name, options) }
|
250
214
|
|
251
|
-
raw_values =
|
215
|
+
raw_values = begin
|
216
|
+
@data.with { |c| c.get_multi(keys_to_names.keys) }
|
217
|
+
rescue Dalli::UnmarshalError
|
218
|
+
{}
|
219
|
+
end
|
220
|
+
|
252
221
|
values = {}
|
253
222
|
|
254
223
|
raw_values.each do |key, value|
|
255
224
|
entry = deserialize_entry(value, raw: options[:raw])
|
256
225
|
|
257
|
-
unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
|
258
|
-
|
226
|
+
unless entry.nil? || entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
|
227
|
+
begin
|
228
|
+
values[keys_to_names[key]] = entry.value
|
229
|
+
rescue DeserializationError
|
230
|
+
end
|
259
231
|
end
|
260
232
|
end
|
261
233
|
|
@@ -283,7 +255,13 @@ module ActiveSupport
|
|
283
255
|
if key
|
284
256
|
key = key.dup.force_encoding(Encoding::ASCII_8BIT)
|
285
257
|
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
286
|
-
|
258
|
+
|
259
|
+
if key.size > KEY_MAX_SIZE
|
260
|
+
key_separator = ":hash:"
|
261
|
+
key_hash = ActiveSupport::Digest.hexdigest(key)
|
262
|
+
key_trim_size = KEY_MAX_SIZE - key_separator.size - key_hash.size
|
263
|
+
key = "#{key[0, key_trim_size]}#{key_separator}#{key_hash}"
|
264
|
+
end
|
287
265
|
end
|
288
266
|
key
|
289
267
|
end
|
@@ -299,8 +277,12 @@ module ActiveSupport
|
|
299
277
|
def rescue_error_with(fallback)
|
300
278
|
yield
|
301
279
|
rescue Dalli::DalliError => error
|
302
|
-
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
303
280
|
logger.error("DalliError (#{error}): #{error.message}") if logger
|
281
|
+
ActiveSupport.error_reporter&.report(
|
282
|
+
error,
|
283
|
+
severity: :warning,
|
284
|
+
source: "mem_cache_store.active_support",
|
285
|
+
)
|
304
286
|
fallback
|
305
287
|
end
|
306
288
|
end
|
@@ -4,49 +4,75 @@ require "monitor"
|
|
4
4
|
|
5
5
|
module ActiveSupport
|
6
6
|
module Cache
|
7
|
+
# = Memory \Cache \Store
|
8
|
+
#
|
7
9
|
# A cache store implementation which stores everything into memory in the
|
8
|
-
# same process. If you're running multiple Ruby on Rails server processes
|
10
|
+
# same process. If you're running multiple Ruby on \Rails server processes
|
9
11
|
# (which is the case if you're using Phusion Passenger or puma clustered mode),
|
10
|
-
# then this means that Rails server process instances won't be able
|
12
|
+
# then this means that \Rails server process instances won't be able
|
11
13
|
# to share cache data with each other and this may not be the most
|
12
14
|
# appropriate cache in that scenario.
|
13
15
|
#
|
14
|
-
# This cache has a bounded size specified by the
|
16
|
+
# This cache has a bounded size specified by the +:size+ options to the
|
15
17
|
# initializer (default is 32Mb). When the cache exceeds the allotted size,
|
16
18
|
# a cleanup will occur which tries to prune the cache down to three quarters
|
17
19
|
# of the maximum size by removing the least recently used entries.
|
18
20
|
#
|
19
|
-
# Unlike other Cache store implementations, MemoryStore does not compress
|
20
|
-
# values by default. MemoryStore does not benefit from compression as much
|
21
|
+
# Unlike other Cache store implementations, +MemoryStore+ does not compress
|
22
|
+
# values by default. +MemoryStore+ does not benefit from compression as much
|
21
23
|
# as other Store implementations, as it does not send data over a network.
|
22
24
|
# However, when compression is enabled, it still pays the full cost of
|
23
25
|
# compression in terms of cpu use.
|
24
26
|
#
|
25
|
-
# MemoryStore is thread-safe.
|
27
|
+
# +MemoryStore+ is thread-safe.
|
26
28
|
class MemoryStore < Store
|
27
29
|
module DupCoder # :nodoc:
|
28
30
|
extend self
|
29
31
|
|
30
32
|
def dump(entry)
|
31
|
-
entry.
|
32
|
-
|
33
|
+
if entry.value && entry.value != true && !entry.value.is_a?(Numeric)
|
34
|
+
Cache::Entry.new(dump_value(entry.value), expires_at: entry.expires_at, version: entry.version)
|
35
|
+
else
|
36
|
+
entry
|
37
|
+
end
|
33
38
|
end
|
34
39
|
|
35
40
|
def dump_compressed(entry, threshold)
|
36
|
-
|
37
|
-
|
38
|
-
entry
|
41
|
+
compressed_entry = entry.compressed(threshold)
|
42
|
+
compressed_entry.compressed? ? compressed_entry : dump(entry)
|
39
43
|
end
|
40
44
|
|
41
45
|
def load(entry)
|
42
|
-
entry
|
43
|
-
|
44
|
-
|
46
|
+
if !entry.compressed? && entry.value.is_a?(String)
|
47
|
+
Cache::Entry.new(load_value(entry.value), expires_at: entry.expires_at, version: entry.version)
|
48
|
+
else
|
49
|
+
entry
|
50
|
+
end
|
45
51
|
end
|
52
|
+
|
53
|
+
private
|
54
|
+
MARSHAL_SIGNATURE = "\x04\x08".b.freeze
|
55
|
+
|
56
|
+
def dump_value(value)
|
57
|
+
if value.is_a?(String) && !value.start_with?(MARSHAL_SIGNATURE)
|
58
|
+
value.dup
|
59
|
+
else
|
60
|
+
Marshal.dump(value)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def load_value(string)
|
65
|
+
if string.start_with?(MARSHAL_SIGNATURE)
|
66
|
+
Marshal.load(string)
|
67
|
+
else
|
68
|
+
string.dup
|
69
|
+
end
|
70
|
+
end
|
46
71
|
end
|
47
72
|
|
48
73
|
def initialize(options = nil)
|
49
74
|
options ||= {}
|
75
|
+
options[:coder] = DupCoder unless options.key?(:coder) || options.key?(:serializer)
|
50
76
|
# Disable compression by default.
|
51
77
|
options[:compress] ||= false
|
52
78
|
super(options)
|
@@ -74,7 +100,7 @@ module ActiveSupport
|
|
74
100
|
# Preemptively iterates through all stored keys and removes the ones which have expired.
|
75
101
|
def cleanup(options = nil)
|
76
102
|
options = merged_options(options)
|
77
|
-
|
103
|
+
_instrument(:cleanup, size: @data.size) do
|
78
104
|
keys = synchronize { @data.keys }
|
79
105
|
keys.each do |key|
|
80
106
|
entry = @data[key]
|
@@ -108,12 +134,33 @@ module ActiveSupport
|
|
108
134
|
@pruning
|
109
135
|
end
|
110
136
|
|
111
|
-
# Increment
|
137
|
+
# Increment a cached integer value. Returns the updated value.
|
138
|
+
#
|
139
|
+
# If the key is unset, it will be set to +amount+:
|
140
|
+
#
|
141
|
+
# cache.increment("foo") # => 1
|
142
|
+
# cache.increment("bar", 100) # => 100
|
143
|
+
#
|
144
|
+
# To set a specific value, call #write:
|
145
|
+
#
|
146
|
+
# cache.write("baz", 5)
|
147
|
+
# cache.increment("baz") # => 6
|
148
|
+
#
|
112
149
|
def increment(name, amount = 1, options = nil)
|
113
150
|
modify_value(name, amount, options)
|
114
151
|
end
|
115
152
|
|
116
|
-
# Decrement
|
153
|
+
# Decrement a cached integer value. Returns the updated value.
|
154
|
+
#
|
155
|
+
# If the key is unset or has expired, it will be set to +-amount+.
|
156
|
+
#
|
157
|
+
# cache.decrement("foo") # => -1
|
158
|
+
#
|
159
|
+
# To set a specific value, call #write:
|
160
|
+
#
|
161
|
+
# cache.write("baz", 5)
|
162
|
+
# cache.decrement("baz") # => 4
|
163
|
+
#
|
117
164
|
def decrement(name, amount = 1, options = nil)
|
118
165
|
modify_value(name, -amount, options)
|
119
166
|
end
|
@@ -121,8 +168,9 @@ module ActiveSupport
|
|
121
168
|
# Deletes cache entries if the cache key matches a given pattern.
|
122
169
|
def delete_matched(matcher, options = nil)
|
123
170
|
options = merged_options(options)
|
171
|
+
matcher = key_matcher(matcher, options)
|
172
|
+
|
124
173
|
instrument(:delete_matched, matcher.inspect) do
|
125
|
-
matcher = key_matcher(matcher, options)
|
126
174
|
keys = synchronize { @data.keys }
|
127
175
|
keys.each do |key|
|
128
176
|
delete_entry(key, **options) if key.match(matcher)
|
@@ -143,10 +191,6 @@ module ActiveSupport
|
|
143
191
|
private
|
144
192
|
PER_ENTRY_OVERHEAD = 240
|
145
193
|
|
146
|
-
def default_coder
|
147
|
-
DupCoder
|
148
|
-
end
|
149
|
-
|
150
194
|
def cached_size(key, payload)
|
151
195
|
key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
|
152
196
|
end
|
@@ -166,7 +210,7 @@ module ActiveSupport
|
|
166
210
|
def write_entry(key, entry, **options)
|
167
211
|
payload = serialize_entry(entry, **options)
|
168
212
|
synchronize do
|
169
|
-
return false if options[:unless_exist] &&
|
213
|
+
return false if options[:unless_exist] && exist?(key, namespace: nil)
|
170
214
|
|
171
215
|
old_payload = @data[key]
|
172
216
|
if old_payload
|
@@ -188,12 +232,23 @@ module ActiveSupport
|
|
188
232
|
end
|
189
233
|
end
|
190
234
|
|
235
|
+
# Modifies the amount of an integer value that is stored in the cache.
|
236
|
+
# If the key is not found it is created and set to +amount+.
|
191
237
|
def modify_value(name, amount, options)
|
192
238
|
options = merged_options(options)
|
239
|
+
key = normalize_key(name, options)
|
240
|
+
version = normalize_version(name, options)
|
241
|
+
|
193
242
|
synchronize do
|
194
|
-
|
195
|
-
|
196
|
-
|
243
|
+
entry = read_entry(key, **options)
|
244
|
+
|
245
|
+
if !entry || entry.expired? || entry.mismatched?(version)
|
246
|
+
write(name, Integer(amount), options)
|
247
|
+
amount
|
248
|
+
else
|
249
|
+
num = entry.value.to_i + amount
|
250
|
+
entry = Entry.new(num, expires_at: entry.expires_at, version: entry.version)
|
251
|
+
write_entry(key, entry)
|
197
252
|
num
|
198
253
|
end
|
199
254
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
4
|
module Cache
|
5
|
+
# = Null \Cache \Store
|
6
|
+
#
|
5
7
|
# A cache store implementation which doesn't actually store anything. Useful in
|
6
8
|
# development and test environments where you don't want caching turned on but
|
7
9
|
# need to go through the caching interface.
|
@@ -32,6 +34,10 @@ module ActiveSupport
|
|
32
34
|
def delete_matched(matcher, options = nil)
|
33
35
|
end
|
34
36
|
|
37
|
+
def inspect # :nodoc:
|
38
|
+
"#<#{self.class.name} options=#{@options.inspect}>"
|
39
|
+
end
|
40
|
+
|
35
41
|
private
|
36
42
|
def read_entry(key, **s)
|
37
43
|
deserialize_entry(read_serialized_entry(key))
|