activesupport 6.0.6.1 → 6.1.7.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +441 -455
- data/MIT-LICENSE +1 -1
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +3 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache/file_store.rb +3 -3
- data/lib/active_support/cache/mem_cache_store.rb +28 -18
- data/lib/active_support/cache/memory_store.rb +46 -26
- data/lib/active_support/cache/redis_cache_store.rb +25 -25
- data/lib/active_support/cache/strategy/local_cache.rb +20 -5
- data/lib/active_support/cache.rb +87 -40
- data/lib/active_support/callbacks.rb +65 -56
- data/lib/active_support/concern.rb +46 -2
- data/lib/active_support/configurable.rb +3 -3
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/digest/uuid.rb +1 -0
- data/lib/active_support/core_ext/enumerable.rb +76 -4
- data/lib/active_support/core_ext/hash/conversions.rb +2 -2
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +1 -1
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +38 -28
- data/lib/active_support/core_ext/module/introspection.rb +1 -25
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/json.rb +12 -1
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/range/compare_range.rb +9 -3
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/regexp.rb +8 -1
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/inflections.rb +38 -4
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +7 -4
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +19 -0
- data/lib/active_support/core_ext/time/conversions.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +5 -1
- data/lib/active_support/core_ext.rb +1 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +9 -2
- data/lib/active_support/dependencies/zeitwerk_integration.rb +4 -1
- data/lib/active_support/dependencies.rb +37 -18
- data/lib/active_support/deprecation/behaviors.rb +15 -2
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +3 -2
- data/lib/active_support/deprecation/proxy_wrappers.rb +2 -2
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +6 -2
- data/lib/active_support/digest.rb +2 -0
- data/lib/active_support/duration/iso8601_serializer.rb +15 -9
- data/lib/active_support/duration.rb +75 -25
- data/lib/active_support/encrypted_file.rb +27 -11
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +69 -133
- data/lib/active_support/fork_tracker.rb +64 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +48 -24
- data/lib/active_support/i18n_railtie.rb +14 -19
- data/lib/active_support/inflector/inflections.rb +1 -2
- data/lib/active_support/inflector/methods.rb +36 -33
- data/lib/active_support/inflector/transliterate.rb +4 -4
- data/lib/active_support/json/decoding.rb +4 -4
- data/lib/active_support/json/encoding.rb +5 -1
- data/lib/active_support/key_generator.rb +1 -1
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +8 -0
- data/lib/active_support/logger.rb +1 -1
- data/lib/active_support/logger_silence.rb +2 -26
- data/lib/active_support/logger_thread_safe_level.rb +34 -12
- data/lib/active_support/message_encryptor.rb +4 -7
- data/lib/active_support/message_verifier.rb +5 -5
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +6 -5
- data/lib/active_support/multibyte/chars.rb +4 -42
- data/lib/active_support/multibyte/unicode.rb +9 -83
- data/lib/active_support/notifications/fanout.rb +23 -8
- data/lib/active_support/notifications/instrumenter.rb +6 -15
- data/lib/active_support/notifications.rb +32 -5
- data/lib/active_support/number_helper/number_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/number_helper.rb +29 -14
- data/lib/active_support/option_merger.rb +2 -1
- data/lib/active_support/ordered_options.rb +8 -2
- data/lib/active_support/parameter_filter.rb +16 -11
- data/lib/active_support/per_thread_registry.rb +2 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/rescuable.rb +4 -4
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -2
- data/lib/active_support/subscriber.rb +12 -7
- data/lib/active_support/tagged_logging.rb +30 -5
- data/lib/active_support/testing/assertions.rb +18 -11
- data/lib/active_support/testing/parallelization/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/parallelization.rb +12 -95
- data/lib/active_support/testing/time_helpers.rb +40 -3
- data/lib/active_support/time_with_zone.rb +67 -43
- data/lib/active_support/values/time_zone.rb +22 -10
- data/lib/active_support/xml_mini/rexml.rb +8 -1
- data/lib/active_support.rb +13 -1
- metadata +34 -36
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
- data/lib/active_support/core_ext/hash/compact.rb +0 -5
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
- data/lib/active_support/core_ext/module/reachable.rb +0 -6
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
- data/lib/active_support/core_ext/range/include_range.rb +0 -9
data/MIT-LICENSE
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/symbol/starts_ends_with"
|
4
|
+
|
3
5
|
module ActiveSupport
|
4
6
|
# Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
|
5
7
|
# its string-like contents:
|
@@ -34,11 +36,11 @@ module ActiveSupport
|
|
34
36
|
|
35
37
|
private
|
36
38
|
def respond_to_missing?(name, include_private = false)
|
37
|
-
(
|
39
|
+
name.end_with?("?") || super
|
38
40
|
end
|
39
41
|
|
40
42
|
def method_missing(name, *args)
|
41
|
-
if name
|
43
|
+
if name.end_with?("?")
|
42
44
|
any?(name[0..-2])
|
43
45
|
else
|
44
46
|
super
|
@@ -16,7 +16,7 @@ module ActiveSupport
|
|
16
16
|
#
|
17
17
|
# bc = ActiveSupport::BacktraceCleaner.new
|
18
18
|
# bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
|
19
|
-
# bc.add_silencer { |line|
|
19
|
+
# bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems
|
20
20
|
# bc.clean(exception.backtrace) # perform the cleanup
|
21
21
|
#
|
22
22
|
# To reconfigure an existing BacktraceCleaner (like the default one in Rails)
|
@@ -65,7 +65,7 @@ module ActiveSupport
|
|
65
65
|
# for a given line, it will be excluded from the clean backtrace.
|
66
66
|
#
|
67
67
|
# # Will reject all lines that include the word "puma", like "/gems/puma/server.rb" or "/app/my_puma_server/rb"
|
68
|
-
# backtrace_cleaner.add_silencer { |line|
|
68
|
+
# backtrace_cleaner.add_silencer { |line| /puma/.match?(line) }
|
69
69
|
def add_silencer(&block)
|
70
70
|
@silencers << block
|
71
71
|
end
|
@@ -91,7 +91,7 @@ module ActiveSupport
|
|
91
91
|
gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
|
92
92
|
return if gems_paths.empty?
|
93
93
|
|
94
|
-
gems_regexp = %r{(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
|
94
|
+
gems_regexp = %r{\A(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
|
95
95
|
gems_result = '\3 (\4) \5'
|
96
96
|
add_filter { |line| line.sub(gems_regexp, gems_result) }
|
97
97
|
end
|
@@ -41,7 +41,7 @@ module ActiveSupport
|
|
41
41
|
|
42
42
|
result = nil
|
43
43
|
ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
|
44
|
-
logger.
|
44
|
+
logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
|
45
45
|
result
|
46
46
|
else
|
47
47
|
yield
|
@@ -20,7 +20,7 @@ module ActiveSupport
|
|
20
20
|
FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
|
21
21
|
GITKEEP_FILES = [".gitkeep", ".keep"].freeze
|
22
22
|
|
23
|
-
def initialize(cache_path, options
|
23
|
+
def initialize(cache_path, **options)
|
24
24
|
super(options)
|
25
25
|
@cache_path = cache_path.to_s
|
26
26
|
end
|
@@ -74,7 +74,7 @@ module ActiveSupport
|
|
74
74
|
private
|
75
75
|
def read_entry(key, **options)
|
76
76
|
if File.exist?(key)
|
77
|
-
entry = File.open(key) { |f|
|
77
|
+
entry = File.open(key) { |f| deserialize_entry(f.read) }
|
78
78
|
entry if entry.is_a?(Cache::Entry)
|
79
79
|
end
|
80
80
|
rescue => e
|
@@ -85,7 +85,7 @@ module ActiveSupport
|
|
85
85
|
def write_entry(key, entry, **options)
|
86
86
|
return false if options[:unless_exist] && File.exist?(key)
|
87
87
|
ensure_cache_path(File.dirname(key))
|
88
|
-
File.atomic_write(key, cache_path) { |f|
|
88
|
+
File.atomic_write(key, cache_path) { |f| f.write(serialize_entry(entry)) }
|
89
89
|
true
|
90
90
|
end
|
91
91
|
|
@@ -7,6 +7,7 @@ rescue LoadError => e
|
|
7
7
|
raise e
|
8
8
|
end
|
9
9
|
|
10
|
+
require "active_support/core_ext/enumerable"
|
10
11
|
require "active_support/core_ext/marshal"
|
11
12
|
require "active_support/core_ext/array/extract_options"
|
12
13
|
|
@@ -25,6 +26,8 @@ module ActiveSupport
|
|
25
26
|
# MemCacheStore implements the Strategy::LocalCache strategy which implements
|
26
27
|
# an in-memory cache inside of a block.
|
27
28
|
class MemCacheStore < Store
|
29
|
+
DEFAULT_CODER = NullCoder # Dalli automatically Marshal values
|
30
|
+
|
28
31
|
# Provide support for raw values in the local cache strategy.
|
29
32
|
module LocalCacheWithRaw # :nodoc:
|
30
33
|
private
|
@@ -50,16 +53,18 @@ module ActiveSupport
|
|
50
53
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
|
51
54
|
|
52
55
|
# Creates a new Dalli::Client instance with specified addresses and options.
|
53
|
-
#
|
56
|
+
# If no addresses are provided, we give nil to Dalli::Client, so it uses its fallbacks:
|
57
|
+
# - ENV["MEMCACHE_SERVERS"] (if defined)
|
58
|
+
# - "127.0.0.1:11211" (otherwise)
|
54
59
|
#
|
55
60
|
# ActiveSupport::Cache::MemCacheStore.build_mem_cache
|
56
|
-
# # => #<Dalli::Client:0x007f98a47d2028 @servers=["
|
61
|
+
# # => #<Dalli::Client:0x007f98a47d2028 @servers=["127.0.0.1:11211"], @options={}, @ring=nil>
|
57
62
|
# ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
|
58
63
|
# # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
|
59
64
|
def self.build_mem_cache(*addresses) # :nodoc:
|
60
65
|
addresses = addresses.flatten
|
61
66
|
options = addresses.extract_options!
|
62
|
-
addresses =
|
67
|
+
addresses = nil if addresses.compact.empty?
|
63
68
|
pool_options = retrieve_pool_options(options)
|
64
69
|
|
65
70
|
if pool_options.empty?
|
@@ -76,8 +81,8 @@ module ActiveSupport
|
|
76
81
|
#
|
77
82
|
# ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
|
78
83
|
#
|
79
|
-
# If no addresses are
|
80
|
-
#
|
84
|
+
# If no addresses are provided, but ENV['MEMCACHE_SERVERS'] is defined, it will be used instead. Otherwise,
|
85
|
+
# MemCacheStore will connect to localhost:11211 (the default memcached port).
|
81
86
|
def initialize(*addresses)
|
82
87
|
addresses = addresses.flatten
|
83
88
|
options = addresses.extract_options!
|
@@ -140,21 +145,22 @@ module ActiveSupport
|
|
140
145
|
|
141
146
|
# Write an entry to the cache.
|
142
147
|
def write_entry(key, entry, **options)
|
143
|
-
method = options
|
144
|
-
value = options[:raw] ? entry.value.to_s : entry
|
148
|
+
method = options[:unless_exist] ? :add : :set
|
149
|
+
value = options[:raw] ? entry.value.to_s : serialize_entry(entry)
|
145
150
|
expires_in = options[:expires_in].to_i
|
146
|
-
if expires_in > 0 && !options[:raw]
|
151
|
+
if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
|
147
152
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
148
153
|
expires_in += 5.minutes
|
149
154
|
end
|
150
155
|
rescue_error_with false do
|
151
|
-
|
156
|
+
# The value "compress: false" prevents duplicate compression within Dalli.
|
157
|
+
@data.with { |c| c.send(method, key, value, expires_in, **options, compress: false) }
|
152
158
|
end
|
153
159
|
end
|
154
160
|
|
155
161
|
# Reads multiple entries from the cache implementation.
|
156
162
|
def read_multi_entries(names, **options)
|
157
|
-
keys_to_names =
|
163
|
+
keys_to_names = names.index_by { |name| normalize_key(name, options) }
|
158
164
|
|
159
165
|
raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
|
160
166
|
values = {}
|
@@ -179,17 +185,21 @@ module ActiveSupport
|
|
179
185
|
# before applying the regular expression to ensure we are escaping all
|
180
186
|
# characters properly.
|
181
187
|
def normalize_key(key, options)
|
182
|
-
key = super
|
183
|
-
|
184
|
-
|
185
|
-
|
188
|
+
key = super
|
189
|
+
|
190
|
+
if key
|
191
|
+
key = key.dup.force_encoding(Encoding::ASCII_8BIT)
|
192
|
+
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
193
|
+
key = "#{key[0, 213]}:md5:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
|
194
|
+
end
|
195
|
+
|
186
196
|
key
|
187
197
|
end
|
188
198
|
|
189
|
-
def deserialize_entry(
|
190
|
-
|
191
|
-
|
192
|
-
|
199
|
+
def deserialize_entry(payload)
|
200
|
+
entry = super
|
201
|
+
entry = Entry.new(entry, compress: false) unless entry.nil? || entry.is_a?(Entry)
|
202
|
+
entry
|
193
203
|
end
|
194
204
|
|
195
205
|
def rescue_error_with(fallback)
|
@@ -16,13 +16,37 @@ module ActiveSupport
|
|
16
16
|
# a cleanup will occur which tries to prune the cache down to three quarters
|
17
17
|
# of the maximum size by removing the least recently used entries.
|
18
18
|
#
|
19
|
+
# Unlike other Cache store implementations, MemoryStore does not compress
|
20
|
+
# values by default. MemoryStore does not benefit from compression as much
|
21
|
+
# as other Store implementations, as it does not send data over a network.
|
22
|
+
# However, when compression is enabled, it still pays the full cost of
|
23
|
+
# compression in terms of cpu use.
|
24
|
+
#
|
19
25
|
# MemoryStore is thread-safe.
|
20
26
|
class MemoryStore < Store
|
27
|
+
module DupCoder # :nodoc:
|
28
|
+
class << self
|
29
|
+
def load(entry)
|
30
|
+
entry = entry.dup
|
31
|
+
entry.dup_value!
|
32
|
+
entry
|
33
|
+
end
|
34
|
+
|
35
|
+
def dump(entry)
|
36
|
+
entry.dup_value!
|
37
|
+
entry
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
DEFAULT_CODER = DupCoder
|
43
|
+
|
21
44
|
def initialize(options = nil)
|
22
45
|
options ||= {}
|
46
|
+
# Disable compression by default.
|
47
|
+
options[:compress] ||= false
|
23
48
|
super(options)
|
24
49
|
@data = {}
|
25
|
-
@key_access = {}
|
26
50
|
@max_size = options[:size] || 32.megabytes
|
27
51
|
@max_prune_time = options[:max_prune_time] || 2
|
28
52
|
@cache_size = 0
|
@@ -39,7 +63,6 @@ module ActiveSupport
|
|
39
63
|
def clear(options = nil)
|
40
64
|
synchronize do
|
41
65
|
@data.clear
|
42
|
-
@key_access.clear
|
43
66
|
@cache_size = 0
|
44
67
|
end
|
45
68
|
end
|
@@ -65,7 +88,7 @@ module ActiveSupport
|
|
65
88
|
start_time = Concurrent.monotonic_time
|
66
89
|
cleanup
|
67
90
|
instrument(:prune, target_size, from: @cache_size) do
|
68
|
-
keys = synchronize { @
|
91
|
+
keys = synchronize { @data.keys }
|
69
92
|
keys.each do |key|
|
70
93
|
delete_entry(key, **options)
|
71
94
|
return if @cache_size <= target_size || (max_time && Concurrent.monotonic_time - start_time > max_time)
|
@@ -104,7 +127,7 @@ module ActiveSupport
|
|
104
127
|
end
|
105
128
|
|
106
129
|
def inspect # :nodoc:
|
107
|
-
"
|
130
|
+
"#<#{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
|
108
131
|
end
|
109
132
|
|
110
133
|
# Synchronize calls to the cache. This should be called wherever the underlying cache implementation
|
@@ -116,36 +139,34 @@ module ActiveSupport
|
|
116
139
|
private
|
117
140
|
PER_ENTRY_OVERHEAD = 240
|
118
141
|
|
119
|
-
def cached_size(key,
|
120
|
-
key.to_s.bytesize +
|
142
|
+
def cached_size(key, payload)
|
143
|
+
key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
|
121
144
|
end
|
122
145
|
|
123
146
|
def read_entry(key, **options)
|
124
|
-
entry =
|
147
|
+
entry = nil
|
125
148
|
synchronize do
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
else
|
131
|
-
@key_access.delete(key)
|
149
|
+
payload = @data.delete(key)
|
150
|
+
if payload
|
151
|
+
@data[key] = payload
|
152
|
+
entry = deserialize_entry(payload)
|
132
153
|
end
|
133
154
|
end
|
134
155
|
entry
|
135
156
|
end
|
136
157
|
|
137
158
|
def write_entry(key, entry, **options)
|
138
|
-
entry
|
159
|
+
payload = serialize_entry(entry)
|
139
160
|
synchronize do
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
161
|
+
return false if options[:unless_exist] && @data.key?(key)
|
162
|
+
|
163
|
+
old_payload = @data[key]
|
164
|
+
if old_payload
|
165
|
+
@cache_size -= (old_payload.bytesize - payload.bytesize)
|
144
166
|
else
|
145
|
-
@cache_size += cached_size(key,
|
167
|
+
@cache_size += cached_size(key, payload)
|
146
168
|
end
|
147
|
-
@
|
148
|
-
@data[key] = entry
|
169
|
+
@data[key] = payload
|
149
170
|
prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
|
150
171
|
true
|
151
172
|
end
|
@@ -153,16 +174,15 @@ module ActiveSupport
|
|
153
174
|
|
154
175
|
def delete_entry(key, **options)
|
155
176
|
synchronize do
|
156
|
-
@
|
157
|
-
|
158
|
-
|
159
|
-
!!entry
|
177
|
+
payload = @data.delete(key)
|
178
|
+
@cache_size -= cached_size(key, payload) if payload
|
179
|
+
!!payload
|
160
180
|
end
|
161
181
|
end
|
162
182
|
|
163
183
|
def modify_value(name, amount, options)
|
184
|
+
options = merged_options(options)
|
164
185
|
synchronize do
|
165
|
-
options = merged_options(options)
|
166
186
|
if num = read(name, options)
|
167
187
|
num = num.to_i + amount
|
168
188
|
write(name, num, options)
|
@@ -169,7 +169,7 @@ module ActiveSupport
|
|
169
169
|
# Race condition TTL is not set by default. This can be used to avoid
|
170
170
|
# "thundering herd" cache writes when hot cache entries are expired.
|
171
171
|
# See <tt>ActiveSupport::Cache::Store#fetch</tt> for more.
|
172
|
-
def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
|
172
|
+
def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, coder: DEFAULT_CODER, expires_in: nil, race_condition_ttl: nil, error_handler: DEFAULT_ERROR_HANDLER, **redis_options)
|
173
173
|
@redis_options = redis_options
|
174
174
|
|
175
175
|
@max_key_bytesize = MAX_KEY_BYTESIZE
|
@@ -177,7 +177,8 @@ module ActiveSupport
|
|
177
177
|
|
178
178
|
super namespace: namespace,
|
179
179
|
compress: compress, compress_threshold: compress_threshold,
|
180
|
-
expires_in: expires_in, race_condition_ttl: race_condition_ttl
|
180
|
+
expires_in: expires_in, race_condition_ttl: race_condition_ttl,
|
181
|
+
coder: coder
|
181
182
|
end
|
182
183
|
|
183
184
|
def redis
|
@@ -195,7 +196,7 @@ module ActiveSupport
|
|
195
196
|
|
196
197
|
def inspect
|
197
198
|
instance = @redis || @redis_options
|
198
|
-
"
|
199
|
+
"#<#{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
|
199
200
|
end
|
200
201
|
|
201
202
|
# Cache Store API implementation.
|
@@ -238,10 +239,14 @@ module ActiveSupport
|
|
238
239
|
pattern = namespace_key(matcher, options)
|
239
240
|
cursor = "0"
|
240
241
|
# Fetch keys in batches using SCAN to avoid blocking the Redis server.
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
242
|
+
nodes = c.respond_to?(:nodes) ? c.nodes : [c]
|
243
|
+
|
244
|
+
nodes.each do |node|
|
245
|
+
begin
|
246
|
+
cursor, keys = node.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
|
247
|
+
node.del(*keys) unless keys.empty?
|
248
|
+
end until cursor == "0"
|
249
|
+
end
|
245
250
|
end
|
246
251
|
end
|
247
252
|
end
|
@@ -414,6 +419,11 @@ module ActiveSupport
|
|
414
419
|
end
|
415
420
|
end
|
416
421
|
|
422
|
+
# Deletes multiple entries in the cache. Returns the number of entries deleted.
|
423
|
+
def delete_multi_entries(entries, **_options)
|
424
|
+
redis.with { |c| c.del(entries) }
|
425
|
+
end
|
426
|
+
|
417
427
|
# Nonstandard store provider API to write multiple values at once.
|
418
428
|
def write_multi_entries(entries, expires_in: nil, **options)
|
419
429
|
if entries.any?
|
@@ -429,11 +439,11 @@ module ActiveSupport
|
|
429
439
|
|
430
440
|
# Truncate keys that exceed 1kB.
|
431
441
|
def normalize_key(key, options)
|
432
|
-
truncate_key super
|
442
|
+
truncate_key super&.b
|
433
443
|
end
|
434
444
|
|
435
445
|
def truncate_key(key)
|
436
|
-
if key.bytesize > max_key_bytesize
|
446
|
+
if key && key.bytesize > max_key_bytesize
|
437
447
|
suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
|
438
448
|
truncate_at = max_key_bytesize - suffix.bytesize
|
439
449
|
"#{key.byteslice(0, truncate_at)}#{suffix}"
|
@@ -442,21 +452,11 @@ module ActiveSupport
|
|
442
452
|
end
|
443
453
|
end
|
444
454
|
|
445
|
-
def deserialize_entry(
|
446
|
-
if
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
if raw != written_raw
|
451
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
452
|
-
Using a different value for the raw option when reading and writing
|
453
|
-
to a cache key is deprecated for :redis_cache_store and Rails 6.0
|
454
|
-
will stop automatically detecting the format when reading to avoid
|
455
|
-
marshal loading untrusted raw strings.
|
456
|
-
MSG
|
457
|
-
end
|
458
|
-
|
459
|
-
entry.is_a?(Entry) ? entry : Entry.new(entry)
|
455
|
+
def deserialize_entry(payload, raw:)
|
456
|
+
if payload && raw
|
457
|
+
Entry.new(payload, compress: false)
|
458
|
+
else
|
459
|
+
super(payload)
|
460
460
|
end
|
461
461
|
end
|
462
462
|
|
@@ -464,7 +464,7 @@ module ActiveSupport
|
|
464
464
|
if raw
|
465
465
|
entry.value.to_s
|
466
466
|
else
|
467
|
-
|
467
|
+
super(entry)
|
468
468
|
end
|
469
469
|
end
|
470
470
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/object/duplicable"
|
4
3
|
require "active_support/core_ext/string/inflections"
|
5
4
|
require "active_support/per_thread_registry"
|
6
5
|
|
@@ -65,8 +64,9 @@ module ActiveSupport
|
|
65
64
|
values
|
66
65
|
end
|
67
66
|
|
68
|
-
def write_entry(key,
|
69
|
-
|
67
|
+
def write_entry(key, entry, **options)
|
68
|
+
entry.dup_value!
|
69
|
+
@data[key] = entry
|
70
70
|
true
|
71
71
|
end
|
72
72
|
|
@@ -75,7 +75,10 @@ module ActiveSupport
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def fetch_entry(key, options = nil) # :nodoc:
|
78
|
-
@data.fetch(key) { @data[key] = yield }
|
78
|
+
entry = @data.fetch(key) { @data[key] = yield }
|
79
|
+
dup_entry = entry.dup
|
80
|
+
dup_entry&.dup_value!
|
81
|
+
dup_entry
|
79
82
|
end
|
80
83
|
end
|
81
84
|
|
@@ -104,6 +107,12 @@ module ActiveSupport
|
|
104
107
|
super
|
105
108
|
end
|
106
109
|
|
110
|
+
def delete_matched(matcher, options = nil) # :nodoc:
|
111
|
+
return super unless cache = local_cache
|
112
|
+
cache.clear
|
113
|
+
super
|
114
|
+
end
|
115
|
+
|
107
116
|
def increment(name, amount = 1, **options) # :nodoc:
|
108
117
|
return super unless local_cache
|
109
118
|
value = bypass_local_cache { super }
|
@@ -121,7 +130,13 @@ module ActiveSupport
|
|
121
130
|
private
|
122
131
|
def read_entry(key, **options)
|
123
132
|
if cache = local_cache
|
124
|
-
|
133
|
+
hit = true
|
134
|
+
value = cache.fetch_entry(key) do
|
135
|
+
hit = false
|
136
|
+
super
|
137
|
+
end
|
138
|
+
options[:event][:store] = cache.class.name if hit && options[:event]
|
139
|
+
value
|
125
140
|
else
|
126
141
|
super
|
127
142
|
end
|