activesupport 6.0.4 → 6.1.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +388 -460
- 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/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 +3 -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 +17 -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 +8 -2
- 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/duration/iso8601_serializer.rb +15 -9
- data/lib/active_support/duration.rb +71 -22
- data/lib/active_support/encrypted_file.rb +19 -2
- 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 +1 -1
- 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 +35 -31
- 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 +1 -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 +29 -4
- 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 +20 -10
- data/lib/active_support/xml_mini/rexml.rb +8 -1
- data/lib/active_support.rb +13 -1
- metadata +33 -35
- 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
|