activesupport 6.1.1 → 7.0.2.3
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 +231 -383
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_support/actionable_error.rb +1 -1
- data/lib/active_support/array_inquirer.rb +0 -2
- data/lib/active_support/benchmarkable.rb +2 -2
- data/lib/active_support/cache/file_store.rb +16 -10
- data/lib/active_support/cache/mem_cache_store.rb +133 -34
- data/lib/active_support/cache/memory_store.rb +23 -15
- data/lib/active_support/cache/null_store.rb +10 -2
- data/lib/active_support/cache/redis_cache_store.rb +42 -67
- data/lib/active_support/cache/strategy/local_cache.rb +35 -61
- data/lib/active_support/cache.rb +196 -46
- data/lib/active_support/callbacks.rb +180 -81
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +5 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +6 -3
- data/lib/active_support/configuration_file.rb +7 -2
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +13 -11
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array.rb +1 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/subclasses.rb +25 -17
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +4 -4
- data/lib/active_support/core_ext/date/conversions.rb +11 -11
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date.rb +1 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +39 -13
- data/lib/active_support/core_ext/enumerable.rb +78 -26
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/keys.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
- data/lib/active_support/core_ext/module/delegation.rb +2 -8
- data/lib/active_support/core_ext/name_error.rb +2 -8
- data/lib/active_support/core_ext/numeric/conversions.rb +79 -76
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric.rb +1 -0
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +11 -0
- data/lib/active_support/core_ext/object/json.rb +29 -24
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/object/with_options.rb +20 -1
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +0 -25
- data/lib/active_support/core_ext/range/conversions.rb +8 -8
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +1 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -25
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +60 -36
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
- data/lib/active_support/core_ext/time/calculations.rb +7 -5
- data/lib/active_support/core_ext/time/conversions.rb +13 -12
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +4 -19
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -27
- data/lib/active_support/core_ext.rb +1 -0
- data/lib/active_support/current_attributes.rb +32 -14
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +58 -788
- data/lib/active_support/deprecation/behaviors.rb +4 -1
- data/lib/active_support/deprecation/method_wrappers.rb +3 -3
- data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/descendants_tracker.rb +174 -68
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +9 -1
- data/lib/active_support/duration.rb +81 -51
- data/lib/active_support/encrypted_configuration.rb +13 -2
- data/lib/active_support/encrypted_file.rb +1 -1
- data/lib/active_support/environment_inquirer.rb +1 -1
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +1 -1
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +43 -21
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/fork_tracker.rb +19 -10
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/hash_with_indifferent_access.rb +9 -2
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +1 -1
- data/lib/active_support/inflector/inflections.rb +23 -7
- data/lib/active_support/inflector/methods.rb +24 -48
- data/lib/active_support/isolated_execution_state.rb +64 -0
- data/lib/active_support/json/encoding.rb +3 -3
- data/lib/active_support/key_generator.rb +18 -1
- data/lib/active_support/locale/en.yml +2 -2
- data/lib/active_support/log_subscriber.rb +13 -3
- data/lib/active_support/logger_thread_safe_level.rb +4 -13
- data/lib/active_support/message_encryptor.rb +8 -3
- data/lib/active_support/message_verifier.rb +46 -14
- data/lib/active_support/messages/metadata.rb +2 -2
- data/lib/active_support/multibyte/chars.rb +10 -11
- data/lib/active_support/multibyte/unicode.rb +0 -12
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +91 -65
- data/lib/active_support/notifications/instrumenter.rb +32 -15
- data/lib/active_support/notifications.rb +16 -22
- data/lib/active_support/number_helper/number_converter.rb +1 -3
- data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
- data/lib/active_support/number_helper/number_to_delimited_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_phone_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
- data/lib/active_support/number_helper/rounding_helper.rb +2 -6
- data/lib/active_support/number_helper.rb +0 -2
- data/lib/active_support/option_merger.rb +8 -16
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/parameter_filter.rb +6 -1
- data/lib/active_support/per_thread_registry.rb +5 -0
- data/lib/active_support/railtie.rb +69 -19
- data/lib/active_support/reloader.rb +1 -1
- data/lib/active_support/rescuable.rb +2 -2
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +1 -1
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/string_inquirer.rb +0 -2
- data/lib/active_support/subscriber.rb +7 -18
- data/lib/active_support/tagged_logging.rb +2 -2
- data/lib/active_support/test_case.rb +9 -21
- data/lib/active_support/testing/assertions.rb +35 -5
- data/lib/active_support/testing/deprecation.rb +52 -1
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +5 -5
- data/lib/active_support/testing/parallelization/server.rb +4 -0
- data/lib/active_support/testing/parallelization/worker.rb +3 -0
- data/lib/active_support/testing/parallelization.rb +4 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +3 -5
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +13 -2
- data/lib/active_support/time_with_zone.rb +55 -14
- data/lib/active_support/values/time_zone.rb +31 -10
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +4 -4
- 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 +5 -4
- data/lib/active_support.rb +17 -1
- metadata +26 -23
- data/lib/active_support/core_ext/marshal.rb +0 -26
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -15,7 +15,7 @@ The latest version of Active Support can be installed with RubyGems:
|
|
15
15
|
|
16
16
|
Source code can be downloaded as part of the Rails project on GitHub:
|
17
17
|
|
18
|
-
* https://github.com/rails/rails/tree/
|
18
|
+
* https://github.com/rails/rails/tree/main/activesupport
|
19
19
|
|
20
20
|
|
21
21
|
== License
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
|
-
# Actionable errors
|
4
|
+
# Actionable errors lets you define actions to resolve an error.
|
5
5
|
#
|
6
6
|
# To make an error actionable, include the <tt>ActiveSupport::ActionableError</tt>
|
7
7
|
# module and invoke the +action+ class macro to define the action. An action
|
@@ -34,13 +34,13 @@ module ActiveSupport
|
|
34
34
|
# <% benchmark 'Process data files', level: :info, silence: true do %>
|
35
35
|
# <%= expensive_and_chatty_files_operation %>
|
36
36
|
# <% end %>
|
37
|
-
def benchmark(message = "Benchmarking", options = {})
|
37
|
+
def benchmark(message = "Benchmarking", options = {}, &block)
|
38
38
|
if logger
|
39
39
|
options.assert_valid_keys(:level, :silence)
|
40
40
|
options[:level] ||= :info
|
41
41
|
|
42
42
|
result = nil
|
43
|
-
ms = Benchmark.ms { result = options[:silence] ? logger.silence
|
43
|
+
ms = Benchmark.ms { result = options[:silence] ? logger.silence(&block) : yield }
|
44
44
|
logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
|
45
45
|
result
|
46
46
|
else
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/marshal"
|
4
3
|
require "active_support/core_ext/file/atomic"
|
5
4
|
require "active_support/core_ext/string/conversions"
|
6
5
|
require "uri/common"
|
@@ -12,7 +11,6 @@ module ActiveSupport
|
|
12
11
|
# FileStore implements the Strategy::LocalCache strategy which implements
|
13
12
|
# an in-memory cache inside of a block.
|
14
13
|
class FileStore < Store
|
15
|
-
prepend Strategy::LocalCache
|
16
14
|
attr_reader :cache_path
|
17
15
|
|
18
16
|
DIR_FORMATTER = "%03X"
|
@@ -20,7 +18,7 @@ module ActiveSupport
|
|
20
18
|
FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
|
21
19
|
GITKEEP_FILES = [".gitkeep", ".keep"].freeze
|
22
20
|
|
23
|
-
def initialize(cache_path, options
|
21
|
+
def initialize(cache_path, **options)
|
24
22
|
super(options)
|
25
23
|
@cache_path = cache_path.to_s
|
26
24
|
end
|
@@ -73,19 +71,27 @@ module ActiveSupport
|
|
73
71
|
|
74
72
|
private
|
75
73
|
def read_entry(key, **options)
|
76
|
-
if
|
77
|
-
entry =
|
74
|
+
if payload = read_serialized_entry(key, **options)
|
75
|
+
entry = deserialize_entry(payload)
|
78
76
|
entry if entry.is_a?(Cache::Entry)
|
79
77
|
end
|
80
|
-
|
81
|
-
|
78
|
+
end
|
79
|
+
|
80
|
+
def read_serialized_entry(key, **)
|
81
|
+
File.binread(key) if File.exist?(key)
|
82
|
+
rescue => error
|
83
|
+
logger.error("FileStoreError (#{error}): #{error.message}") if logger
|
82
84
|
nil
|
83
85
|
end
|
84
86
|
|
85
87
|
def write_entry(key, entry, **options)
|
88
|
+
write_serialized_entry(key, serialize_entry(entry, **options), **options)
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_serialized_entry(key, payload, **options)
|
86
92
|
return false if options[:unless_exist] && File.exist?(key)
|
87
93
|
ensure_cache_path(File.dirname(key))
|
88
|
-
File.atomic_write(key, cache_path) { |f| f.write(
|
94
|
+
File.atomic_write(key, cache_path) { |f| f.write(payload) }
|
89
95
|
true
|
90
96
|
end
|
91
97
|
|
@@ -95,9 +101,9 @@ module ActiveSupport
|
|
95
101
|
File.delete(key)
|
96
102
|
delete_empty_directories(File.dirname(key))
|
97
103
|
true
|
98
|
-
rescue
|
104
|
+
rescue
|
99
105
|
# Just in case the error was caused by another process deleting the file first.
|
100
|
-
raise
|
106
|
+
raise if File.exist?(key)
|
101
107
|
false
|
102
108
|
end
|
103
109
|
end
|
@@ -7,8 +7,8 @@ rescue LoadError => e
|
|
7
7
|
raise e
|
8
8
|
end
|
9
9
|
|
10
|
+
require "delegate"
|
10
11
|
require "active_support/core_ext/enumerable"
|
11
|
-
require "active_support/core_ext/marshal"
|
12
12
|
require "active_support/core_ext/array/extract_options"
|
13
13
|
|
14
14
|
module ActiveSupport
|
@@ -26,29 +26,52 @@ module ActiveSupport
|
|
26
26
|
# MemCacheStore implements the Strategy::LocalCache strategy which implements
|
27
27
|
# an in-memory cache inside of a block.
|
28
28
|
class MemCacheStore < Store
|
29
|
-
|
29
|
+
# Advertise cache versioning support.
|
30
|
+
def self.supports_cache_versioning?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
prepend Strategy::LocalCache
|
35
|
+
|
36
|
+
module DupLocalCache
|
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
|
30
62
|
|
31
|
-
# Provide support for raw values in the local cache strategy.
|
32
|
-
module LocalCacheWithRaw # :nodoc:
|
33
63
|
private
|
34
|
-
def
|
35
|
-
if
|
36
|
-
|
37
|
-
|
38
|
-
|
64
|
+
def local_cache
|
65
|
+
if ActiveSupport::Cache.format_version == 6.1
|
66
|
+
if local_cache = super
|
67
|
+
DupLocalStore.new(local_cache)
|
68
|
+
end
|
39
69
|
else
|
40
70
|
super
|
41
71
|
end
|
42
72
|
end
|
43
73
|
end
|
44
|
-
|
45
|
-
# Advertise cache versioning support.
|
46
|
-
def self.supports_cache_versioning?
|
47
|
-
true
|
48
|
-
end
|
49
|
-
|
50
|
-
prepend Strategy::LocalCache
|
51
|
-
prepend LocalCacheWithRaw
|
74
|
+
prepend DupLocalCache
|
52
75
|
|
53
76
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
|
54
77
|
|
@@ -64,7 +87,7 @@ module ActiveSupport
|
|
64
87
|
def self.build_mem_cache(*addresses) # :nodoc:
|
65
88
|
addresses = addresses.flatten
|
66
89
|
options = addresses.extract_options!
|
67
|
-
addresses = nil if addresses.empty?
|
90
|
+
addresses = nil if addresses.compact.empty?
|
68
91
|
pool_options = retrieve_pool_options(options)
|
69
92
|
|
70
93
|
if pool_options.empty?
|
@@ -86,6 +109,9 @@ module ActiveSupport
|
|
86
109
|
def initialize(*addresses)
|
87
110
|
addresses = addresses.flatten
|
88
111
|
options = addresses.extract_options!
|
112
|
+
if options.key?(:cache_nils)
|
113
|
+
options[:skip_nil] = !options.delete(:cache_nils)
|
114
|
+
end
|
89
115
|
super(options)
|
90
116
|
|
91
117
|
unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
|
@@ -95,7 +121,9 @@ module ActiveSupport
|
|
95
121
|
@data = addresses.first
|
96
122
|
else
|
97
123
|
mem_cache_options = options.dup
|
98
|
-
|
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) }
|
99
127
|
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
100
128
|
end
|
101
129
|
end
|
@@ -138,23 +166,81 @@ module ActiveSupport
|
|
138
166
|
end
|
139
167
|
|
140
168
|
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
|
+
|
141
217
|
# Read an entry from the cache.
|
142
218
|
def read_entry(key, **options)
|
143
|
-
|
219
|
+
deserialize_entry(read_serialized_entry(key, **options), **options)
|
220
|
+
end
|
221
|
+
|
222
|
+
def read_serialized_entry(key, **options)
|
223
|
+
rescue_error_with(nil) do
|
224
|
+
@data.with { |c| c.get(key, options) }
|
225
|
+
end
|
144
226
|
end
|
145
227
|
|
146
228
|
# Write an entry to the cache.
|
147
229
|
def write_entry(key, entry, **options)
|
230
|
+
write_serialized_entry(key, serialize_entry(entry, **options), **options)
|
231
|
+
end
|
232
|
+
|
233
|
+
def write_serialized_entry(key, payload, **options)
|
148
234
|
method = options[:unless_exist] ? :add : :set
|
149
|
-
value = options[:raw] ? entry.value.to_s : serialize_entry(entry)
|
150
235
|
expires_in = options[:expires_in].to_i
|
151
236
|
if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
|
152
237
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
153
238
|
expires_in += 5.minutes
|
154
239
|
end
|
155
240
|
rescue_error_with false do
|
156
|
-
#
|
157
|
-
|
241
|
+
# Don't pass compress option to Dalli since we are already dealing with compression.
|
242
|
+
options.delete(:compress)
|
243
|
+
@data.with { |c| c.send(method, key, payload, expires_in, **options) }
|
158
244
|
end
|
159
245
|
end
|
160
246
|
|
@@ -166,7 +252,7 @@ module ActiveSupport
|
|
166
252
|
values = {}
|
167
253
|
|
168
254
|
raw_values.each do |key, value|
|
169
|
-
entry = deserialize_entry(value)
|
255
|
+
entry = deserialize_entry(value, raw: options[:raw])
|
170
256
|
|
171
257
|
unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
|
172
258
|
values[keys_to_names[key]] = entry.value
|
@@ -181,27 +267,40 @@ module ActiveSupport
|
|
181
267
|
rescue_error_with(false) { @data.with { |c| c.delete(key) } }
|
182
268
|
end
|
183
269
|
|
270
|
+
def serialize_entry(entry, raw: false, **options)
|
271
|
+
if raw
|
272
|
+
entry.value.to_s
|
273
|
+
else
|
274
|
+
super(entry, raw: raw, **options)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
184
278
|
# Memcache keys are binaries. So we need to force their encoding to binary
|
185
279
|
# before applying the regular expression to ensure we are escaping all
|
186
280
|
# characters properly.
|
187
281
|
def normalize_key(key, options)
|
188
|
-
key = super
|
189
|
-
|
190
|
-
|
191
|
-
|
282
|
+
key = super
|
283
|
+
if key
|
284
|
+
key = key.dup.force_encoding(Encoding::ASCII_8BIT)
|
285
|
+
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
286
|
+
key = "#{key[0, 212]}:hash:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
|
287
|
+
end
|
192
288
|
key
|
193
289
|
end
|
194
290
|
|
195
|
-
def deserialize_entry(payload)
|
196
|
-
|
197
|
-
|
198
|
-
|
291
|
+
def deserialize_entry(payload, raw: false, **)
|
292
|
+
if payload && raw
|
293
|
+
Entry.new(payload)
|
294
|
+
else
|
295
|
+
super(payload)
|
296
|
+
end
|
199
297
|
end
|
200
298
|
|
201
299
|
def rescue_error_with(fallback)
|
202
300
|
yield
|
203
|
-
rescue Dalli::DalliError =>
|
204
|
-
|
301
|
+
rescue Dalli::DalliError => error
|
302
|
+
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
303
|
+
logger.error("DalliError (#{error}): #{error.message}") if logger
|
205
304
|
fallback
|
206
305
|
end
|
207
306
|
end
|
@@ -25,21 +25,25 @@ module ActiveSupport
|
|
25
25
|
# MemoryStore is thread-safe.
|
26
26
|
class MemoryStore < Store
|
27
27
|
module DupCoder # :nodoc:
|
28
|
-
|
29
|
-
def load(entry)
|
30
|
-
entry = entry.dup
|
31
|
-
entry.dup_value!
|
32
|
-
entry
|
33
|
-
end
|
28
|
+
extend self
|
34
29
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
30
|
+
def dump(entry)
|
31
|
+
entry.dup_value! unless entry.compressed?
|
32
|
+
entry
|
33
|
+
end
|
34
|
+
|
35
|
+
def dump_compressed(entry, threshold)
|
36
|
+
entry = entry.compressed(threshold)
|
37
|
+
entry.dup_value! unless entry.compressed?
|
38
|
+
entry
|
39
39
|
end
|
40
|
-
end
|
41
40
|
|
42
|
-
|
41
|
+
def load(entry)
|
42
|
+
entry = entry.dup
|
43
|
+
entry.dup_value!
|
44
|
+
entry
|
45
|
+
end
|
46
|
+
end
|
43
47
|
|
44
48
|
def initialize(options = nil)
|
45
49
|
options ||= {}
|
@@ -85,13 +89,13 @@ module ActiveSupport
|
|
85
89
|
return if pruning?
|
86
90
|
@pruning = true
|
87
91
|
begin
|
88
|
-
start_time =
|
92
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
89
93
|
cleanup
|
90
94
|
instrument(:prune, target_size, from: @cache_size) do
|
91
95
|
keys = synchronize { @data.keys }
|
92
96
|
keys.each do |key|
|
93
97
|
delete_entry(key, **options)
|
94
|
-
return if @cache_size <= target_size || (max_time &&
|
98
|
+
return if @cache_size <= target_size || (max_time && Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time > max_time)
|
95
99
|
end
|
96
100
|
end
|
97
101
|
ensure
|
@@ -139,6 +143,10 @@ module ActiveSupport
|
|
139
143
|
private
|
140
144
|
PER_ENTRY_OVERHEAD = 240
|
141
145
|
|
146
|
+
def default_coder
|
147
|
+
DupCoder
|
148
|
+
end
|
149
|
+
|
142
150
|
def cached_size(key, payload)
|
143
151
|
key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
|
144
152
|
end
|
@@ -156,7 +164,7 @@ module ActiveSupport
|
|
156
164
|
end
|
157
165
|
|
158
166
|
def write_entry(key, entry, **options)
|
159
|
-
payload = serialize_entry(entry)
|
167
|
+
payload = serialize_entry(entry, **options)
|
160
168
|
synchronize do
|
161
169
|
return false if options[:unless_exist] && @data.key?(key)
|
162
170
|
|
@@ -33,10 +33,18 @@ module ActiveSupport
|
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
36
|
-
def read_entry(key, **
|
36
|
+
def read_entry(key, **s)
|
37
|
+
deserialize_entry(read_serialized_entry(key))
|
37
38
|
end
|
38
39
|
|
39
|
-
def
|
40
|
+
def read_serialized_entry(_key, **)
|
41
|
+
end
|
42
|
+
|
43
|
+
def write_entry(key, entry, **)
|
44
|
+
write_serialized_entry(key, serialize_entry(entry))
|
45
|
+
end
|
46
|
+
|
47
|
+
def write_serialized_entry(_key, _payload, **)
|
40
48
|
true
|
41
49
|
end
|
42
50
|
|
@@ -15,8 +15,7 @@ begin
|
|
15
15
|
rescue LoadError
|
16
16
|
end
|
17
17
|
|
18
|
-
require "digest
|
19
|
-
require "active_support/core_ext/marshal"
|
18
|
+
require "active_support/digest"
|
20
19
|
|
21
20
|
module ActiveSupport
|
22
21
|
module Cache
|
@@ -46,7 +45,7 @@ module ActiveSupport
|
|
46
45
|
# 4.0.1+ for distributed mget support.
|
47
46
|
# * +delete_matched+ support for Redis KEYS globs.
|
48
47
|
class RedisCacheStore < Store
|
49
|
-
# Keys are truncated with
|
48
|
+
# Keys are truncated with the ActiveSupport digest if they exceed 1kB
|
50
49
|
MAX_KEY_BYTESIZE = 1024
|
51
50
|
|
52
51
|
DEFAULT_REDIS_OPTIONS = {
|
@@ -71,35 +70,7 @@ module ActiveSupport
|
|
71
70
|
true
|
72
71
|
end
|
73
72
|
|
74
|
-
# Support raw values in the local cache strategy.
|
75
|
-
module LocalCacheWithRaw # :nodoc:
|
76
|
-
private
|
77
|
-
def write_entry(key, entry, **options)
|
78
|
-
if options[:raw] && local_cache
|
79
|
-
raw_entry = Entry.new(serialize_entry(entry, raw: true))
|
80
|
-
raw_entry.expires_at = entry.expires_at
|
81
|
-
super(key, raw_entry, **options)
|
82
|
-
else
|
83
|
-
super
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def write_multi_entries(entries, **options)
|
88
|
-
if options[:raw] && local_cache
|
89
|
-
raw_entries = entries.map do |key, entry|
|
90
|
-
raw_entry = Entry.new(serialize_entry(entry, raw: true))
|
91
|
-
raw_entry.expires_at = entry.expires_at
|
92
|
-
end.to_h
|
93
|
-
|
94
|
-
super(raw_entries, **options)
|
95
|
-
else
|
96
|
-
super
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
73
|
prepend Strategy::LocalCache
|
102
|
-
prepend LocalCacheWithRaw
|
103
74
|
|
104
75
|
class << self
|
105
76
|
# Factory method to create a new Redis instance.
|
@@ -113,7 +84,7 @@ module ActiveSupport
|
|
113
84
|
# :url String -> Redis.new(url: …)
|
114
85
|
# :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
|
115
86
|
#
|
116
|
-
def build_redis(redis: nil, url: nil, **redis_options)
|
87
|
+
def build_redis(redis: nil, url: nil, **redis_options) # :nodoc:
|
117
88
|
urls = Array(url)
|
118
89
|
|
119
90
|
if redis.is_a?(Proc)
|
@@ -169,7 +140,7 @@ module ActiveSupport
|
|
169
140
|
# Race condition TTL is not set by default. This can be used to avoid
|
170
141
|
# "thundering herd" cache writes when hot cache entries are expired.
|
171
142
|
# See <tt>ActiveSupport::Cache::Store#fetch</tt> for more.
|
172
|
-
def initialize(namespace: nil, compress: true, compress_threshold: 1.kilobyte, coder:
|
143
|
+
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
144
|
@redis_options = redis_options
|
174
145
|
|
175
146
|
@max_key_bytesize = MAX_KEY_BYTESIZE
|
@@ -319,12 +290,17 @@ module ActiveSupport
|
|
319
290
|
end
|
320
291
|
end
|
321
292
|
|
322
|
-
|
293
|
+
# Get info from redis servers.
|
294
|
+
def stats
|
295
|
+
redis.with { |c| c.info }
|
296
|
+
end
|
297
|
+
|
298
|
+
def mget_capable? # :nodoc:
|
323
299
|
set_redis_capabilities unless defined? @mget_capable
|
324
300
|
@mget_capable
|
325
301
|
end
|
326
302
|
|
327
|
-
def mset_capable?
|
303
|
+
def mset_capable? # :nodoc:
|
328
304
|
set_redis_capabilities unless defined? @mset_capable
|
329
305
|
@mset_capable
|
330
306
|
end
|
@@ -344,9 +320,12 @@ module ActiveSupport
|
|
344
320
|
# Store provider interface:
|
345
321
|
# Read an entry from the cache.
|
346
322
|
def read_entry(key, **options)
|
323
|
+
deserialize_entry(read_serialized_entry(key, **options), **options)
|
324
|
+
end
|
325
|
+
|
326
|
+
def read_serialized_entry(key, raw: false, **options)
|
347
327
|
failsafe :read_entry do
|
348
|
-
|
349
|
-
deserialize_entry(redis.with { |c| c.get(key) }, raw: raw)
|
328
|
+
redis.with { |c| c.get(key) }
|
350
329
|
end
|
351
330
|
end
|
352
331
|
|
@@ -383,9 +362,11 @@ module ActiveSupport
|
|
383
362
|
# Write an entry to the cache.
|
384
363
|
#
|
385
364
|
# Requires Redis 2.6.12+ for extended SET options.
|
386
|
-
def write_entry(key, entry,
|
387
|
-
|
365
|
+
def write_entry(key, entry, raw: false, **options)
|
366
|
+
write_serialized_entry(key, serialize_entry(entry, raw: raw, **options), raw: raw, **options)
|
367
|
+
end
|
388
368
|
|
369
|
+
def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, **options)
|
389
370
|
# If race condition TTL is in use, ensure that cache entries
|
390
371
|
# stick around a bit longer after they would have expired
|
391
372
|
# so we can purposefully serve stale entries.
|
@@ -393,16 +374,14 @@ module ActiveSupport
|
|
393
374
|
expires_in += 5.minutes
|
394
375
|
end
|
395
376
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
377
|
+
modifiers = {}
|
378
|
+
if unless_exist || expires_in
|
379
|
+
modifiers[:nx] = unless_exist
|
380
|
+
modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
|
381
|
+
end
|
401
382
|
|
402
|
-
|
403
|
-
|
404
|
-
redis.with { |c| c.set key, serialized_entry }
|
405
|
-
end
|
383
|
+
failsafe :write_entry, returning: false do
|
384
|
+
redis.with { |c| c.set key, payload, **modifiers }
|
406
385
|
end
|
407
386
|
end
|
408
387
|
|
@@ -429,7 +408,10 @@ module ActiveSupport
|
|
429
408
|
if entries.any?
|
430
409
|
if mset_capable? && expires_in.nil?
|
431
410
|
failsafe :write_multi_entries do
|
432
|
-
|
411
|
+
payload = serialize_entries(entries, **options)
|
412
|
+
redis.with do |c|
|
413
|
+
c.mapped_mset(payload)
|
414
|
+
end
|
433
415
|
end
|
434
416
|
else
|
435
417
|
super
|
@@ -444,7 +426,7 @@ module ActiveSupport
|
|
444
426
|
|
445
427
|
def truncate_key(key)
|
446
428
|
if key && key.bytesize > max_key_bytesize
|
447
|
-
suffix = ":
|
429
|
+
suffix = ":hash:#{ActiveSupport::Digest.hexdigest(key)}"
|
448
430
|
truncate_at = max_key_bytesize - suffix.bytesize
|
449
431
|
"#{key.byteslice(0, truncate_at)}#{suffix}"
|
450
432
|
else
|
@@ -452,42 +434,35 @@ module ActiveSupport
|
|
452
434
|
end
|
453
435
|
end
|
454
436
|
|
455
|
-
def deserialize_entry(payload, raw:)
|
456
|
-
if
|
457
|
-
Entry.new(payload
|
437
|
+
def deserialize_entry(payload, raw: false, **)
|
438
|
+
if raw && !payload.nil?
|
439
|
+
Entry.new(payload)
|
458
440
|
else
|
459
441
|
super(payload)
|
460
442
|
end
|
461
443
|
end
|
462
444
|
|
463
|
-
def serialize_entry(entry, raw: false)
|
445
|
+
def serialize_entry(entry, raw: false, **options)
|
464
446
|
if raw
|
465
447
|
entry.value.to_s
|
466
448
|
else
|
467
|
-
super(entry)
|
449
|
+
super(entry, raw: raw, **options)
|
468
450
|
end
|
469
451
|
end
|
470
452
|
|
471
|
-
def serialize_entries(entries,
|
453
|
+
def serialize_entries(entries, **options)
|
472
454
|
entries.transform_values do |entry|
|
473
|
-
serialize_entry
|
455
|
+
serialize_entry(entry, **options)
|
474
456
|
end
|
475
457
|
end
|
476
458
|
|
477
459
|
def failsafe(method, returning: nil)
|
478
460
|
yield
|
479
|
-
rescue ::Redis::BaseError =>
|
480
|
-
|
461
|
+
rescue ::Redis::BaseError => error
|
462
|
+
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
463
|
+
@error_handler&.call(method: method, exception: error, returning: returning)
|
481
464
|
returning
|
482
465
|
end
|
483
|
-
|
484
|
-
def handle_exception(exception:, method:, returning:)
|
485
|
-
if @error_handler
|
486
|
-
@error_handler.(method: method, exception: exception, returning: returning)
|
487
|
-
end
|
488
|
-
rescue => failsafe
|
489
|
-
warn "RedisCacheStore ignored exception in handle_exception: #{failsafe.class}: #{failsafe.message}\n #{failsafe.backtrace.join("\n ")}"
|
490
|
-
end
|
491
466
|
end
|
492
467
|
end
|
493
468
|
end
|