activesupport 6.0.4.4 → 7.0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +257 -532
- 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 +5 -5
- 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 +163 -42
- data/lib/active_support/cache/memory_store.rb +57 -29
- data/lib/active_support/cache/null_store.rb +10 -2
- data/lib/active_support/cache/redis_cache_store.rb +79 -98
- data/lib/active_support/cache/strategy/local_cache.rb +49 -57
- data/lib/active_support/cache.rb +378 -179
- data/lib/active_support/callbacks.rb +230 -122
- 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 +13 -12
- 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/inquiry.rb +2 -2
- 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 +9 -22
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +9 -9
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- 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 +17 -4
- 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 +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 +164 -23
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +2 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- 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/kernel/singleton_class.rb +1 -1
- 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 +80 -73
- 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 +42 -26
- 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/overlaps.rb +1 -1
- 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/securerandom.rb +1 -1
- data/lib/active_support/core_ext/string/access.rb +5 -24
- data/lib/active_support/core_ext/string/conversions.rb +3 -2
- 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 +2 -1
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +92 -41
- 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 +25 -7
- data/lib/active_support/core_ext/time/conversions.rb +15 -12
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +7 -22
- 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 +23 -7
- 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 +4 -4
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +7 -2
- 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 +13 -2
- data/lib/active_support/encrypted_file.rb +32 -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 +72 -138
- 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 +71 -0
- data/lib/active_support/gem_version.rb +3 -3
- 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 +5 -5
- data/lib/active_support/isolated_execution_state.rb +72 -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 +23 -6
- data/lib/active_support/lazy_load_hooks.rb +28 -4
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +23 -5
- 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 +16 -13
- 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 +55 -28
- 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 +11 -18
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +9 -3
- 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 +16 -16
- 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 +13 -21
- data/lib/active_support/testing/assertions.rb +50 -13
- 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 +126 -62
- data/lib/active_support/values/time_zone.rb +54 -23
- data/lib/active_support/version.rb +1 -1
- 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 +46 -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,15 +16,15 @@ 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)
|
23
23
|
# and show as much data as possible, you can always call
|
24
|
-
#
|
24
|
+
# BacktraceCleaner#remove_silencers!, which will restore the
|
25
25
|
# backtrace to a pristine state. If you need to reconfigure an existing
|
26
26
|
# BacktraceCleaner so that it does not filter or modify the paths of any lines
|
27
|
-
# of the backtrace, you can call
|
27
|
+
# of the backtrace, you can call BacktraceCleaner#remove_filters!
|
28
28
|
# These two methods will give you a completely untouched backtrace.
|
29
29
|
#
|
30
30
|
# Inspired by the Quiet Backtrace gem by thoughtbot.
|
@@ -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 <tt>ENV['MEMCACHE_SERVERS']</tt> 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,14 +121,32 @@ 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
|
97
130
|
|
131
|
+
##
|
132
|
+
# :method: write
|
133
|
+
# :call-seq: write(name, value, options = nil)
|
134
|
+
#
|
135
|
+
# Behaves the same as ActiveSupport::Cache::Store#write, but supports
|
136
|
+
# additional options specific to memcached.
|
137
|
+
#
|
138
|
+
# ==== Additional Options
|
139
|
+
#
|
140
|
+
# * <tt>raw: true</tt> - Sends the value directly to the server as raw
|
141
|
+
# bytes. The value must be a string or number. You can use memcached
|
142
|
+
# direct operations like +increment+ and +decrement+ only on raw values.
|
143
|
+
#
|
144
|
+
# * <tt>unless_exist: true</tt> - Prevents overwriting an existing cache
|
145
|
+
# entry.
|
146
|
+
|
98
147
|
# Increment a cached value. This method uses the memcached incr atomic
|
99
|
-
# operator and can only be used on values written with the
|
100
|
-
# Calling it on a value not stored with
|
148
|
+
# operator and can only be used on values written with the +:raw+ option.
|
149
|
+
# Calling it on a value not stored with +:raw+ will initialize that value
|
101
150
|
# to zero.
|
102
151
|
def increment(name, amount = 1, options = nil)
|
103
152
|
options = merged_options(options)
|
@@ -109,8 +158,8 @@ module ActiveSupport
|
|
109
158
|
end
|
110
159
|
|
111
160
|
# Decrement a cached value. This method uses the memcached decr atomic
|
112
|
-
# operator and can only be used on values written with the
|
113
|
-
# Calling it on a value not stored with
|
161
|
+
# operator and can only be used on values written with the +:raw+ option.
|
162
|
+
# Calling it on a value not stored with +:raw+ will initialize that value
|
114
163
|
# to zero.
|
115
164
|
def decrement(name, amount = 1, options = nil)
|
116
165
|
options = merged_options(options)
|
@@ -133,34 +182,93 @@ module ActiveSupport
|
|
133
182
|
end
|
134
183
|
|
135
184
|
private
|
185
|
+
module Coders # :nodoc:
|
186
|
+
class << self
|
187
|
+
def [](version)
|
188
|
+
case version
|
189
|
+
when 6.1
|
190
|
+
Rails61Coder
|
191
|
+
when 7.0
|
192
|
+
Rails70Coder
|
193
|
+
else
|
194
|
+
raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
module Loader
|
200
|
+
def load(payload)
|
201
|
+
if payload.is_a?(Entry)
|
202
|
+
payload
|
203
|
+
else
|
204
|
+
Cache::Coders::Loader.load(payload)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
module Rails61Coder
|
210
|
+
include Loader
|
211
|
+
extend self
|
212
|
+
|
213
|
+
def dump(entry)
|
214
|
+
entry
|
215
|
+
end
|
216
|
+
|
217
|
+
def dump_compressed(entry, threshold)
|
218
|
+
entry.compressed(threshold)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
module Rails70Coder
|
223
|
+
include Cache::Coders::Rails70Coder
|
224
|
+
include Loader
|
225
|
+
extend self
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def default_coder
|
230
|
+
Coders[Cache.format_version]
|
231
|
+
end
|
232
|
+
|
136
233
|
# Read an entry from the cache.
|
137
234
|
def read_entry(key, **options)
|
138
|
-
|
235
|
+
deserialize_entry(read_serialized_entry(key, **options), **options)
|
236
|
+
end
|
237
|
+
|
238
|
+
def read_serialized_entry(key, **options)
|
239
|
+
rescue_error_with(nil) do
|
240
|
+
@data.with { |c| c.get(key, options) }
|
241
|
+
end
|
139
242
|
end
|
140
243
|
|
141
244
|
# Write an entry to the cache.
|
142
245
|
def write_entry(key, entry, **options)
|
143
|
-
|
144
|
-
|
246
|
+
write_serialized_entry(key, serialize_entry(entry, **options), **options)
|
247
|
+
end
|
248
|
+
|
249
|
+
def write_serialized_entry(key, payload, **options)
|
250
|
+
method = options[:unless_exist] ? :add : :set
|
145
251
|
expires_in = options[:expires_in].to_i
|
146
|
-
if expires_in > 0 && !options[:raw]
|
252
|
+
if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
|
147
253
|
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
148
254
|
expires_in += 5.minutes
|
149
255
|
end
|
150
256
|
rescue_error_with false do
|
151
|
-
|
257
|
+
# Don't pass compress option to Dalli since we are already dealing with compression.
|
258
|
+
options.delete(:compress)
|
259
|
+
@data.with { |c| c.send(method, key, payload, expires_in, **options) }
|
152
260
|
end
|
153
261
|
end
|
154
262
|
|
155
263
|
# Reads multiple entries from the cache implementation.
|
156
264
|
def read_multi_entries(names, **options)
|
157
|
-
keys_to_names =
|
265
|
+
keys_to_names = names.index_by { |name| normalize_key(name, options) }
|
158
266
|
|
159
267
|
raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
|
160
268
|
values = {}
|
161
269
|
|
162
270
|
raw_values.each do |key, value|
|
163
|
-
entry = deserialize_entry(value)
|
271
|
+
entry = deserialize_entry(value, raw: options[:raw])
|
164
272
|
|
165
273
|
unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
|
166
274
|
values[keys_to_names[key]] = entry.value
|
@@ -175,27 +283,40 @@ module ActiveSupport
|
|
175
283
|
rescue_error_with(false) { @data.with { |c| c.delete(key) } }
|
176
284
|
end
|
177
285
|
|
286
|
+
def serialize_entry(entry, raw: false, **options)
|
287
|
+
if raw
|
288
|
+
entry.value.to_s
|
289
|
+
else
|
290
|
+
super(entry, raw: raw, **options)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
178
294
|
# Memcache keys are binaries. So we need to force their encoding to binary
|
179
295
|
# before applying the regular expression to ensure we are escaping all
|
180
296
|
# characters properly.
|
181
297
|
def normalize_key(key, options)
|
182
|
-
key = super
|
183
|
-
|
184
|
-
|
185
|
-
|
298
|
+
key = super
|
299
|
+
if key
|
300
|
+
key = key.dup.force_encoding(Encoding::ASCII_8BIT)
|
301
|
+
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
302
|
+
key = "#{key[0, 212]}:hash:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
|
303
|
+
end
|
186
304
|
key
|
187
305
|
end
|
188
306
|
|
189
|
-
def deserialize_entry(
|
190
|
-
if
|
191
|
-
|
307
|
+
def deserialize_entry(payload, raw: false, **)
|
308
|
+
if payload && raw
|
309
|
+
Entry.new(payload)
|
310
|
+
else
|
311
|
+
super(payload)
|
192
312
|
end
|
193
313
|
end
|
194
314
|
|
195
315
|
def rescue_error_with(fallback)
|
196
316
|
yield
|
197
|
-
rescue Dalli::DalliError =>
|
198
|
-
|
317
|
+
rescue Dalli::DalliError => error
|
318
|
+
ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
|
319
|
+
logger.error("DalliError (#{error}): #{error.message}") if logger
|
199
320
|
fallback
|
200
321
|
end
|
201
322
|
end
|
@@ -11,18 +11,46 @@ module ActiveSupport
|
|
11
11
|
# to share cache data with each other and this may not be the most
|
12
12
|
# appropriate cache in that scenario.
|
13
13
|
#
|
14
|
-
# This cache has a bounded size specified by the
|
14
|
+
# This cache has a bounded size specified by the +:size+ options to the
|
15
15
|
# initializer (default is 32Mb). When the cache exceeds the allotted size,
|
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
|
|