activesupport 6.1.3.1 → 7.0.0.alpha1

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