activesupport 6.0.6 → 7.0.1
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 +224 -608
- data/MIT-LICENSE +1 -1
- data/lib/active_support/actionable_error.rb +1 -1
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +3 -3
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/cache/file_store.rb +16 -10
- data/lib/active_support/cache/mem_cache_store.rb +143 -38
- data/lib/active_support/cache/memory_store.rb +56 -28
- data/lib/active_support/cache/null_store.rb +10 -2
- data/lib/active_support/cache/redis_cache_store.rb +62 -87
- data/lib/active_support/cache/strategy/local_cache.rb +46 -57
- data/lib/active_support/cache.rb +268 -77
- data/lib/active_support/callbacks.rb +226 -118
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +49 -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 +9 -6
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +9 -7
- 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/benchmark.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +34 -44
- data/lib/active_support/core_ext/class/subclasses.rb +11 -24
- 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 +5 -4
- 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/calculations.rb +13 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
- 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 +139 -15
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- 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 +2 -2
- data/lib/active_support/core_ext/hash/slice.rb +3 -2
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +40 -36
- data/lib/active_support/core_ext/module/introspection.rb +1 -25
- data/lib/active_support/core_ext/name_error.rb +23 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +79 -72
- 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 +41 -25
- 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 +6 -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 -20
- data/lib/active_support/core_ext/range.rb +1 -1
- 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/filters.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +39 -5
- 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 +62 -67
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +23 -5
- data/lib/active_support/core_ext/time/conversions.rb +6 -3
- 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 -23
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +39 -16
- 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 -769
- data/lib/active_support/deprecation/behaviors.rb +19 -3
- 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 +6 -5
- data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +174 -64
- 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 +24 -10
- data/lib/active_support/duration.rb +134 -55
- data/lib/active_support/encrypted_configuration.rb +11 -1
- data/lib/active_support/encrypted_file.rb +20 -3
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +70 -134
- 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 +41 -18
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +51 -25
- 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 +14 -19
- data/lib/active_support/inflector/inflections.rb +24 -9
- data/lib/active_support/inflector/methods.rb +29 -49
- data/lib/active_support/inflector/transliterate.rb +4 -4
- data/lib/active_support/isolated_execution_state.rb +56 -0
- data/lib/active_support/json/decoding.rb +4 -4
- data/lib/active_support/json/encoding.rb +8 -4
- data/lib/active_support/key_generator.rb +19 -2
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber.rb +21 -3
- 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 -21
- data/lib/active_support/message_encryptor.rb +12 -10
- data/lib/active_support/message_verifier.rb +50 -18
- data/lib/active_support/messages/metadata.rb +2 -2
- 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 +13 -52
- data/lib/active_support/multibyte/unicode.rb +1 -87
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +110 -69
- data/lib/active_support/notifications/instrumenter.rb +37 -29
- data/lib/active_support/notifications.rb +47 -26
- data/lib/active_support/number_helper/number_converter.rb +2 -4
- 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_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
- data/lib/active_support/number_helper/number_to_phone_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 -32
- data/lib/active_support/number_helper.rb +29 -16
- data/lib/active_support/option_merger.rb +9 -16
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +8 -2
- data/lib/active_support/parameter_filter.rb +21 -11
- data/lib/active_support/per_thread_registry.rb +6 -1
- data/lib/active_support/rails.rb +1 -4
- data/lib/active_support/railtie.rb +77 -5
- data/lib/active_support/reloader.rb +1 -1
- data/lib/active_support/rescuable.rb +6 -6
- data/lib/active_support/ruby_features.rb +7 -0
- 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 +2 -2
- data/lib/active_support/subscriber.rb +19 -25
- data/lib/active_support/tagged_logging.rb +31 -6
- data/lib/active_support/test_case.rb +9 -21
- data/lib/active_support/testing/assertions.rb +49 -12
- 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 +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +16 -95
- 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 +53 -5
- data/lib/active_support/time_with_zone.rb +120 -55
- data/lib/active_support/values/time_zone.rb +49 -18
- 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 +9 -2
- data/lib/active_support/xml_mini.rb +5 -4
- data/lib/active_support.rb +29 -1
- metadata +45 -45
- 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/marshal.rb +0 -24
- 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/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
data/MIT-LICENSE
CHANGED
@@ -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,11 +34,11 @@ module ActiveSupport
|
|
34
34
|
|
35
35
|
private
|
36
36
|
def respond_to_missing?(name, include_private = false)
|
37
|
-
(
|
37
|
+
name.end_with?("?") || super
|
38
38
|
end
|
39
39
|
|
40
40
|
def method_missing(name, *args)
|
41
|
-
if name
|
41
|
+
if name.end_with?("?")
|
42
42
|
any?(name[0..-2])
|
43
43
|
else
|
44
44
|
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
|
@@ -34,14 +34,14 @@ 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
|
44
|
-
logger.
|
43
|
+
ms = Benchmark.ms { result = options[:silence] ? logger.silence(&block) : yield }
|
44
|
+
logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
|
45
45
|
result
|
46
46
|
else
|
47
47
|
yield
|
@@ -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|
|
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,7 +7,8 @@ rescue LoadError => e
|
|
7
7
|
raise e
|
8
8
|
end
|
9
9
|
|
10
|
-
require "
|
10
|
+
require "delegate"
|
11
|
+
require "active_support/core_ext/enumerable"
|
11
12
|
require "active_support/core_ext/array/extract_options"
|
12
13
|
|
13
14
|
module ActiveSupport
|
@@ -25,41 +26,68 @@ 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
|
28
|
-
# Provide support for raw values in the local cache strategy.
|
29
|
-
module LocalCacheWithRaw # :nodoc:
|
30
|
-
private
|
31
|
-
def write_entry(key, entry, **options)
|
32
|
-
if options[:raw] && local_cache
|
33
|
-
raw_entry = Entry.new(entry.value.to_s)
|
34
|
-
raw_entry.expires_at = entry.expires_at
|
35
|
-
super(key, raw_entry, **options)
|
36
|
-
else
|
37
|
-
super
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
29
|
# Advertise cache versioning support.
|
43
30
|
def self.supports_cache_versioning?
|
44
31
|
true
|
45
32
|
end
|
46
33
|
|
47
34
|
prepend Strategy::LocalCache
|
48
|
-
|
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
|
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
|
49
75
|
|
50
76
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
|
51
77
|
|
52
78
|
# Creates a new Dalli::Client instance with specified addresses and options.
|
53
|
-
#
|
79
|
+
# If no addresses are provided, we give nil to Dalli::Client, so it uses its fallbacks:
|
80
|
+
# - ENV["MEMCACHE_SERVERS"] (if defined)
|
81
|
+
# - "127.0.0.1:11211" (otherwise)
|
54
82
|
#
|
55
83
|
# ActiveSupport::Cache::MemCacheStore.build_mem_cache
|
56
|
-
# # => #<Dalli::Client:0x007f98a47d2028 @servers=["
|
84
|
+
# # => #<Dalli::Client:0x007f98a47d2028 @servers=["127.0.0.1:11211"], @options={}, @ring=nil>
|
57
85
|
# ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
|
58
86
|
# # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
|
59
87
|
def self.build_mem_cache(*addresses) # :nodoc:
|
60
88
|
addresses = addresses.flatten
|
61
89
|
options = addresses.extract_options!
|
62
|
-
addresses =
|
90
|
+
addresses = nil if addresses.compact.empty?
|
63
91
|
pool_options = retrieve_pool_options(options)
|
64
92
|
|
65
93
|
if pool_options.empty?
|
@@ -76,11 +104,14 @@ module ActiveSupport
|
|
76
104
|
#
|
77
105
|
# ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
|
78
106
|
#
|
79
|
-
# If no addresses are
|
80
|
-
#
|
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).
|
81
109
|
def initialize(*addresses)
|
82
110
|
addresses = addresses.flatten
|
83
111
|
options = addresses.extract_options!
|
112
|
+
if options.key?(:cache_nils)
|
113
|
+
options[:skip_nil] = !options.delete(:cache_nils)
|
114
|
+
end
|
84
115
|
super(options)
|
85
116
|
|
86
117
|
unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
|
@@ -90,7 +121,9 @@ module ActiveSupport
|
|
90
121
|
@data = addresses.first
|
91
122
|
else
|
92
123
|
mem_cache_options = options.dup
|
93
|
-
|
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) }
|
94
127
|
@data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
|
95
128
|
end
|
96
129
|
end
|
@@ -133,34 +166,93 @@ module ActiveSupport
|
|
133
166
|
end
|
134
167
|
|
135
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
|
+
|
136
217
|
# Read an entry from the cache.
|
137
218
|
def read_entry(key, **options)
|
138
|
-
|
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
|
139
226
|
end
|
140
227
|
|
141
228
|
# Write an entry to the cache.
|
142
229
|
def write_entry(key, entry, **options)
|
143
|
-
|
144
|
-
|
230
|
+
write_serialized_entry(key, serialize_entry(entry, **options), **options)
|
231
|
+
end
|
232
|
+
|
233
|
+
def write_serialized_entry(key, payload, **options)
|
234
|
+
method = options[:unless_exist] ? :add : :set
|
145
235
|
expires_in = options[:expires_in].to_i
|
146
|
-
if expires_in > 0 && !options[:raw]
|
236
|
+
if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
|
147
237
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
148
238
|
expires_in += 5.minutes
|
149
239
|
end
|
150
240
|
rescue_error_with false do
|
151
|
-
|
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) }
|
152
244
|
end
|
153
245
|
end
|
154
246
|
|
155
247
|
# Reads multiple entries from the cache implementation.
|
156
248
|
def read_multi_entries(names, **options)
|
157
|
-
keys_to_names =
|
249
|
+
keys_to_names = names.index_by { |name| normalize_key(name, options) }
|
158
250
|
|
159
251
|
raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
|
160
252
|
values = {}
|
161
253
|
|
162
254
|
raw_values.each do |key, value|
|
163
|
-
entry = deserialize_entry(value)
|
255
|
+
entry = deserialize_entry(value, raw: options[:raw])
|
164
256
|
|
165
257
|
unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
|
166
258
|
values[keys_to_names[key]] = entry.value
|
@@ -175,27 +267,40 @@ module ActiveSupport
|
|
175
267
|
rescue_error_with(false) { @data.with { |c| c.delete(key) } }
|
176
268
|
end
|
177
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
|
+
|
178
278
|
# Memcache keys are binaries. So we need to force their encoding to binary
|
179
279
|
# before applying the regular expression to ensure we are escaping all
|
180
280
|
# characters properly.
|
181
281
|
def normalize_key(key, options)
|
182
|
-
key = super
|
183
|
-
|
184
|
-
|
185
|
-
|
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
|
186
288
|
key
|
187
289
|
end
|
188
290
|
|
189
|
-
def deserialize_entry(
|
190
|
-
if
|
191
|
-
|
291
|
+
def deserialize_entry(payload, raw: false, **)
|
292
|
+
if payload && raw
|
293
|
+
Entry.new(payload)
|
294
|
+
else
|
295
|
+
super(payload)
|
192
296
|
end
|
193
297
|
end
|
194
298
|
|
195
299
|
def rescue_error_with(fallback)
|
196
300
|
yield
|
197
|
-
rescue Dalli::DalliError =>
|
198
|
-
|
301
|
+
rescue Dalli::DalliError => error
|
302
|
+
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
303
|
+
logger.error("DalliError (#{error}): #{error.message}") if logger
|
199
304
|
fallback
|
200
305
|
end
|
201
306
|
end
|
@@ -16,13 +16,41 @@ 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
|
+
extend self
|
29
|
+
|
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
|
+
end
|
40
|
+
|
41
|
+
def load(entry)
|
42
|
+
entry = entry.dup
|
43
|
+
entry.dup_value!
|
44
|
+
entry
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
21
48
|
def initialize(options = nil)
|
22
49
|
options ||= {}
|
50
|
+
# Disable compression by default.
|
51
|
+
options[:compress] ||= false
|
23
52
|
super(options)
|
24
53
|
@data = {}
|
25
|
-
@key_access = {}
|
26
54
|
@max_size = options[:size] || 32.megabytes
|
27
55
|
@max_prune_time = options[:max_prune_time] || 2
|
28
56
|
@cache_size = 0
|
@@ -39,7 +67,6 @@ module ActiveSupport
|
|
39
67
|
def clear(options = nil)
|
40
68
|
synchronize do
|
41
69
|
@data.clear
|
42
|
-
@key_access.clear
|
43
70
|
@cache_size = 0
|
44
71
|
end
|
45
72
|
end
|
@@ -62,13 +89,13 @@ module ActiveSupport
|
|
62
89
|
return if pruning?
|
63
90
|
@pruning = true
|
64
91
|
begin
|
65
|
-
start_time =
|
92
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
66
93
|
cleanup
|
67
94
|
instrument(:prune, target_size, from: @cache_size) do
|
68
|
-
keys = synchronize { @
|
95
|
+
keys = synchronize { @data.keys }
|
69
96
|
keys.each do |key|
|
70
97
|
delete_entry(key, **options)
|
71
|
-
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)
|
72
99
|
end
|
73
100
|
end
|
74
101
|
ensure
|
@@ -104,7 +131,7 @@ module ActiveSupport
|
|
104
131
|
end
|
105
132
|
|
106
133
|
def inspect # :nodoc:
|
107
|
-
"
|
134
|
+
"#<#{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
|
108
135
|
end
|
109
136
|
|
110
137
|
# Synchronize calls to the cache. This should be called wherever the underlying cache implementation
|
@@ -116,36 +143,38 @@ module ActiveSupport
|
|
116
143
|
private
|
117
144
|
PER_ENTRY_OVERHEAD = 240
|
118
145
|
|
119
|
-
def
|
120
|
-
|
146
|
+
def default_coder
|
147
|
+
DupCoder
|
148
|
+
end
|
149
|
+
|
150
|
+
def cached_size(key, payload)
|
151
|
+
key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
|
121
152
|
end
|
122
153
|
|
123
154
|
def read_entry(key, **options)
|
124
|
-
entry =
|
155
|
+
entry = nil
|
125
156
|
synchronize do
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
else
|
131
|
-
@key_access.delete(key)
|
157
|
+
payload = @data.delete(key)
|
158
|
+
if payload
|
159
|
+
@data[key] = payload
|
160
|
+
entry = deserialize_entry(payload)
|
132
161
|
end
|
133
162
|
end
|
134
163
|
entry
|
135
164
|
end
|
136
165
|
|
137
166
|
def write_entry(key, entry, **options)
|
138
|
-
entry
|
167
|
+
payload = serialize_entry(entry, **options)
|
139
168
|
synchronize do
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
169
|
+
return false if options[:unless_exist] && @data.key?(key)
|
170
|
+
|
171
|
+
old_payload = @data[key]
|
172
|
+
if old_payload
|
173
|
+
@cache_size -= (old_payload.bytesize - payload.bytesize)
|
144
174
|
else
|
145
|
-
@cache_size += cached_size(key,
|
175
|
+
@cache_size += cached_size(key, payload)
|
146
176
|
end
|
147
|
-
@
|
148
|
-
@data[key] = entry
|
177
|
+
@data[key] = payload
|
149
178
|
prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
|
150
179
|
true
|
151
180
|
end
|
@@ -153,16 +182,15 @@ module ActiveSupport
|
|
153
182
|
|
154
183
|
def delete_entry(key, **options)
|
155
184
|
synchronize do
|
156
|
-
@
|
157
|
-
|
158
|
-
|
159
|
-
!!entry
|
185
|
+
payload = @data.delete(key)
|
186
|
+
@cache_size -= cached_size(key, payload) if payload
|
187
|
+
!!payload
|
160
188
|
end
|
161
189
|
end
|
162
190
|
|
163
191
|
def modify_value(name, amount, options)
|
192
|
+
options = merged_options(options)
|
164
193
|
synchronize do
|
165
|
-
options = merged_options(options)
|
166
194
|
if num = read(name, options)
|
167
195
|
num = num.to_i + amount
|
168
196
|
write(name, num, options)
|
@@ -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
|
|