activesupport 6.0.6.1 → 6.1.0.rc1

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.

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