activesupport 6.0.4.4 → 7.0.4.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.

Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +257 -532
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +2 -2
  6. data/lib/active_support/backtrace_cleaner.rb +5 -5
  7. data/lib/active_support/benchmarkable.rb +3 -3
  8. data/lib/active_support/cache/file_store.rb +16 -10
  9. data/lib/active_support/cache/mem_cache_store.rb +163 -42
  10. data/lib/active_support/cache/memory_store.rb +57 -29
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +79 -98
  13. data/lib/active_support/cache/strategy/local_cache.rb +49 -57
  14. data/lib/active_support/cache.rb +378 -179
  15. data/lib/active_support/callbacks.rb +230 -122
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +49 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  19. data/lib/active_support/concurrency/share_lock.rb +2 -2
  20. data/lib/active_support/configurable.rb +9 -6
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +1 -5
  23. data/lib/active_support/core_ext/array/conversions.rb +13 -12
  24. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  25. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  26. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  27. data/lib/active_support/core_ext/array.rb +1 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  30. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  31. data/lib/active_support/core_ext/class/subclasses.rb +9 -22
  32. data/lib/active_support/core_ext/date/blank.rb +1 -1
  33. data/lib/active_support/core_ext/date/calculations.rb +9 -9
  34. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  35. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  36. data/lib/active_support/core_ext/date.rb +1 -0
  37. data/lib/active_support/core_ext/date_and_time/calculations.rb +17 -4
  38. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  39. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  41. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  42. data/lib/active_support/core_ext/date_time.rb +1 -0
  43. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  44. data/lib/active_support/core_ext/enumerable.rb +164 -23
  45. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  46. data/lib/active_support/core_ext/hash/conversions.rb +2 -3
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  49. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  50. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  51. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  52. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  53. data/lib/active_support/core_ext/load_error.rb +1 -1
  54. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  57. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  58. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  59. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  60. data/lib/active_support/core_ext/name_error.rb +23 -2
  61. data/lib/active_support/core_ext/numeric/conversions.rb +80 -73
  62. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  63. data/lib/active_support/core_ext/numeric.rb +1 -0
  64. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  65. data/lib/active_support/core_ext/object/blank.rb +2 -2
  66. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  68. data/lib/active_support/core_ext/object/json.rb +42 -26
  69. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  70. data/lib/active_support/core_ext/object/try.rb +20 -20
  71. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  72. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  73. data/lib/active_support/core_ext/pathname.rb +3 -0
  74. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  75. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  76. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  77. data/lib/active_support/core_ext/range/each.rb +1 -1
  78. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  79. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  80. data/lib/active_support/core_ext/range.rb +1 -1
  81. data/lib/active_support/core_ext/regexp.rb +8 -1
  82. data/lib/active_support/core_ext/securerandom.rb +1 -1
  83. data/lib/active_support/core_ext/string/access.rb +5 -24
  84. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  85. data/lib/active_support/core_ext/string/filters.rb +1 -1
  86. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  87. data/lib/active_support/core_ext/string/inquiry.rb +2 -1
  88. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  89. data/lib/active_support/core_ext/string/output_safety.rb +92 -41
  90. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  92. data/lib/active_support/core_ext/symbol.rb +3 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +25 -7
  94. data/lib/active_support/core_ext/time/conversions.rb +15 -12
  95. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  96. data/lib/active_support/core_ext/time/zones.rb +7 -22
  97. data/lib/active_support/core_ext/time.rb +1 -0
  98. data/lib/active_support/core_ext/uri.rb +3 -23
  99. data/lib/active_support/core_ext.rb +2 -1
  100. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  101. data/lib/active_support/current_attributes.rb +39 -16
  102. data/lib/active_support/dependencies/interlock.rb +10 -18
  103. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  104. data/lib/active_support/dependencies.rb +58 -769
  105. data/lib/active_support/deprecation/behaviors.rb +23 -7
  106. data/lib/active_support/deprecation/disallowed.rb +56 -0
  107. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  108. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
  110. data/lib/active_support/deprecation/reporting.rb +50 -7
  111. data/lib/active_support/deprecation.rb +7 -2
  112. data/lib/active_support/descendants_tracker.rb +174 -64
  113. data/lib/active_support/digest.rb +5 -3
  114. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  115. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  116. data/lib/active_support/duration.rb +134 -55
  117. data/lib/active_support/encrypted_configuration.rb +13 -2
  118. data/lib/active_support/encrypted_file.rb +32 -3
  119. data/lib/active_support/environment_inquirer.rb +20 -0
  120. data/lib/active_support/error_reporter.rb +117 -0
  121. data/lib/active_support/evented_file_update_checker.rb +72 -138
  122. data/lib/active_support/execution_context/test_helper.rb +13 -0
  123. data/lib/active_support/execution_context.rb +53 -0
  124. data/lib/active_support/execution_wrapper.rb +43 -21
  125. data/lib/active_support/executor/test_helper.rb +7 -0
  126. data/lib/active_support/fork_tracker.rb +71 -0
  127. data/lib/active_support/gem_version.rb +3 -3
  128. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  129. data/lib/active_support/html_safe_translation.rb +43 -0
  130. data/lib/active_support/i18n.rb +1 -0
  131. data/lib/active_support/i18n_railtie.rb +14 -19
  132. data/lib/active_support/inflector/inflections.rb +24 -9
  133. data/lib/active_support/inflector/methods.rb +29 -49
  134. data/lib/active_support/inflector/transliterate.rb +5 -5
  135. data/lib/active_support/isolated_execution_state.rb +72 -0
  136. data/lib/active_support/json/decoding.rb +4 -4
  137. data/lib/active_support/json/encoding.rb +8 -4
  138. data/lib/active_support/key_generator.rb +23 -6
  139. data/lib/active_support/lazy_load_hooks.rb +28 -4
  140. data/lib/active_support/locale/en.yml +8 -4
  141. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  142. data/lib/active_support/log_subscriber.rb +23 -5
  143. data/lib/active_support/logger.rb +1 -1
  144. data/lib/active_support/logger_silence.rb +2 -26
  145. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  146. data/lib/active_support/message_encryptor.rb +16 -13
  147. data/lib/active_support/message_verifier.rb +50 -18
  148. data/lib/active_support/messages/metadata.rb +2 -2
  149. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  150. data/lib/active_support/messages/rotator.rb +6 -5
  151. data/lib/active_support/multibyte/chars.rb +13 -52
  152. data/lib/active_support/multibyte/unicode.rb +1 -87
  153. data/lib/active_support/multibyte.rb +1 -1
  154. data/lib/active_support/notifications/fanout.rb +110 -69
  155. data/lib/active_support/notifications/instrumenter.rb +37 -29
  156. data/lib/active_support/notifications.rb +55 -28
  157. data/lib/active_support/number_helper/number_converter.rb +2 -4
  158. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  159. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  160. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  161. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  162. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  163. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  164. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  165. data/lib/active_support/number_helper.rb +29 -16
  166. data/lib/active_support/option_merger.rb +11 -18
  167. data/lib/active_support/ordered_hash.rb +1 -1
  168. data/lib/active_support/ordered_options.rb +9 -3
  169. data/lib/active_support/parameter_filter.rb +21 -11
  170. data/lib/active_support/per_thread_registry.rb +6 -1
  171. data/lib/active_support/rails.rb +1 -4
  172. data/lib/active_support/railtie.rb +77 -5
  173. data/lib/active_support/reloader.rb +1 -1
  174. data/lib/active_support/rescuable.rb +16 -16
  175. data/lib/active_support/ruby_features.rb +7 -0
  176. data/lib/active_support/secure_compare_rotator.rb +51 -0
  177. data/lib/active_support/security_utils.rb +19 -12
  178. data/lib/active_support/string_inquirer.rb +2 -2
  179. data/lib/active_support/subscriber.rb +19 -25
  180. data/lib/active_support/tagged_logging.rb +31 -6
  181. data/lib/active_support/test_case.rb +13 -21
  182. data/lib/active_support/testing/assertions.rb +50 -13
  183. data/lib/active_support/testing/deprecation.rb +52 -1
  184. data/lib/active_support/testing/isolation.rb +2 -2
  185. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  186. data/lib/active_support/testing/parallelization/server.rb +82 -0
  187. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  188. data/lib/active_support/testing/parallelization.rb +16 -95
  189. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  190. data/lib/active_support/testing/stream.rb +3 -5
  191. data/lib/active_support/testing/tagged_logging.rb +1 -1
  192. data/lib/active_support/testing/time_helpers.rb +53 -5
  193. data/lib/active_support/time_with_zone.rb +126 -62
  194. data/lib/active_support/values/time_zone.rb +54 -23
  195. data/lib/active_support/version.rb +1 -1
  196. data/lib/active_support/xml_mini/jdom.rb +1 -1
  197. data/lib/active_support/xml_mini/libxml.rb +5 -5
  198. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  199. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  200. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  201. data/lib/active_support/xml_mini/rexml.rb +9 -2
  202. data/lib/active_support/xml_mini.rb +5 -4
  203. data/lib/active_support.rb +29 -1
  204. metadata +46 -45
  205. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  206. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  207. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  208. data/lib/active_support/core_ext/marshal.rb +0 -24
  209. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  210. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  211. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  212. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -5,18 +5,21 @@ begin
5
5
  require "redis"
6
6
  require "redis/distributed"
7
7
  rescue LoadError
8
- warn "The Redis cache store requires the redis gem, version 4.0.1 or later. Please add it to your Gemfile: `gem \"redis\", \"~> 4.0\"`"
8
+ warn "The Redis cache store requires the redis gem, version 4.0.1 or later. Please add it to your Gemfile: `gem \"redis\", \">= 4.0.1\"`"
9
9
  raise
10
10
  end
11
11
 
12
12
  # Prefer the hiredis driver but don't require it.
13
13
  begin
14
- require "redis/connection/hiredis"
14
+ if ::Redis::VERSION < "5"
15
+ require "redis/connection/hiredis"
16
+ else
17
+ require "hiredis-client"
18
+ end
15
19
  rescue LoadError
16
20
  end
17
21
 
18
- require "digest/sha2"
19
- require "active_support/core_ext/marshal"
22
+ require "active_support/digest"
20
23
 
21
24
  module ActiveSupport
22
25
  module Cache
@@ -46,7 +49,7 @@ module ActiveSupport
46
49
  # 4.0.1+ for distributed mget support.
47
50
  # * +delete_matched+ support for Redis KEYS globs.
48
51
  class RedisCacheStore < Store
49
- # Keys are truncated with their own SHA2 digest if they exceed 1kB
52
+ # Keys are truncated with the ActiveSupport digest if they exceed 1kB
50
53
  MAX_KEY_BYTESIZE = 1024
51
54
 
52
55
  DEFAULT_REDIS_OPTIONS = {
@@ -71,35 +74,7 @@ module ActiveSupport
71
74
  true
72
75
  end
73
76
 
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
77
  prepend Strategy::LocalCache
102
- prepend LocalCacheWithRaw
103
78
 
104
79
  class << self
105
80
  # Factory method to create a new Redis instance.
@@ -113,7 +88,7 @@ module ActiveSupport
113
88
  # :url String -> Redis.new(url: …)
114
89
  # :url Array -> Redis::Distributed.new([{ url: … }, { url: … }, …])
115
90
  #
116
- def build_redis(redis: nil, url: nil, **redis_options) #:nodoc:
91
+ def build_redis(redis: nil, url: nil, **redis_options) # :nodoc:
117
92
  urls = Array(url)
118
93
 
119
94
  if redis.is_a?(Proc)
@@ -121,9 +96,11 @@ module ActiveSupport
121
96
  elsif redis
122
97
  redis
123
98
  elsif urls.size > 1
124
- build_redis_distributed_client urls: urls, **redis_options
99
+ build_redis_distributed_client(urls: urls, **redis_options)
100
+ elsif urls.empty?
101
+ build_redis_client(**redis_options)
125
102
  else
126
- build_redis_client url: urls.first, **redis_options
103
+ build_redis_client(url: urls.first, **redis_options)
127
104
  end
128
105
  end
129
106
 
@@ -134,8 +111,8 @@ module ActiveSupport
134
111
  end
135
112
  end
136
113
 
137
- def build_redis_client(url:, **redis_options)
138
- ::Redis.new DEFAULT_REDIS_OPTIONS.merge(redis_options.merge(url: url))
114
+ def build_redis_client(**redis_options)
115
+ ::Redis.new(DEFAULT_REDIS_OPTIONS.merge(redis_options))
139
116
  end
140
117
  end
141
118
 
@@ -168,8 +145,8 @@ module ActiveSupport
168
145
  #
169
146
  # Race condition TTL is not set by default. This can be used to avoid
170
147
  # "thundering herd" cache writes when hot cache entries are expired.
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)
148
+ # See ActiveSupport::Cache::Store#fetch for more.
149
+ 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
150
  @redis_options = redis_options
174
151
 
175
152
  @max_key_bytesize = MAX_KEY_BYTESIZE
@@ -177,7 +154,8 @@ module ActiveSupport
177
154
 
178
155
  super namespace: namespace,
179
156
  compress: compress, compress_threshold: compress_threshold,
180
- expires_in: expires_in, race_condition_ttl: race_condition_ttl
157
+ expires_in: expires_in, race_condition_ttl: race_condition_ttl,
158
+ coder: coder
181
159
  end
182
160
 
183
161
  def redis
@@ -195,7 +173,7 @@ module ActiveSupport
195
173
 
196
174
  def inspect
197
175
  instance = @redis || @redis_options
198
- "<##{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
176
+ "#<#{self.class} options=#{options.inspect} redis=#{instance.inspect}>"
199
177
  end
200
178
 
201
179
  # Cache Store API implementation.
@@ -238,10 +216,14 @@ module ActiveSupport
238
216
  pattern = namespace_key(matcher, options)
239
217
  cursor = "0"
240
218
  # 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"
219
+ nodes = c.respond_to?(:nodes) ? c.nodes : [c]
220
+
221
+ nodes.each do |node|
222
+ begin
223
+ cursor, keys = node.scan(cursor, match: pattern, count: SCAN_BATCH_SIZE)
224
+ node.del(*keys) unless keys.empty?
225
+ end until cursor == "0"
226
+ end
245
227
  end
246
228
  end
247
229
  end
@@ -249,8 +231,8 @@ module ActiveSupport
249
231
  # Cache Store API implementation.
250
232
  #
251
233
  # Increment a cached value. This method uses the Redis incr atomic
252
- # operator and can only be used on values written with the :raw option.
253
- # Calling it on a value not stored with :raw will initialize that value
234
+ # operator and can only be used on values written with the +:raw+ option.
235
+ # Calling it on a value not stored with +:raw+ will initialize that value
254
236
  # to zero.
255
237
  #
256
238
  # Failsafe: Raises errors.
@@ -272,8 +254,8 @@ module ActiveSupport
272
254
  # Cache Store API implementation.
273
255
  #
274
256
  # Decrement a cached value. This method uses the Redis decr atomic
275
- # operator and can only be used on values written with the :raw option.
276
- # Calling it on a value not stored with :raw will initialize that value
257
+ # operator and can only be used on values written with the +:raw+ option.
258
+ # Calling it on a value not stored with +:raw+ will initialize that value
277
259
  # to zero.
278
260
  #
279
261
  # Failsafe: Raises errors.
@@ -314,12 +296,17 @@ module ActiveSupport
314
296
  end
315
297
  end
316
298
 
317
- def mget_capable? #:nodoc:
299
+ # Get info from redis servers.
300
+ def stats
301
+ redis.with { |c| c.info }
302
+ end
303
+
304
+ def mget_capable? # :nodoc:
318
305
  set_redis_capabilities unless defined? @mget_capable
319
306
  @mget_capable
320
307
  end
321
308
 
322
- def mset_capable? #:nodoc:
309
+ def mset_capable? # :nodoc:
323
310
  set_redis_capabilities unless defined? @mset_capable
324
311
  @mset_capable
325
312
  end
@@ -339,9 +326,12 @@ module ActiveSupport
339
326
  # Store provider interface:
340
327
  # Read an entry from the cache.
341
328
  def read_entry(key, **options)
329
+ deserialize_entry(read_serialized_entry(key, **options), **options)
330
+ end
331
+
332
+ def read_serialized_entry(key, raw: false, **options)
342
333
  failsafe :read_entry do
343
- raw = options&.fetch(:raw, false)
344
- deserialize_entry(redis.with { |c| c.get(key) }, raw: raw)
334
+ redis.with { |c| c.get(key) }
345
335
  end
346
336
  end
347
337
 
@@ -378,9 +368,11 @@ module ActiveSupport
378
368
  # Write an entry to the cache.
379
369
  #
380
370
  # Requires Redis 2.6.12+ for extended SET options.
381
- def write_entry(key, entry, unless_exist: false, raw: false, expires_in: nil, race_condition_ttl: nil, **options)
382
- serialized_entry = serialize_entry(entry, raw: raw)
371
+ def write_entry(key, entry, raw: false, **options)
372
+ write_serialized_entry(key, serialize_entry(entry, raw: raw, **options), raw: raw, **options)
373
+ end
383
374
 
375
+ def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, **options)
384
376
  # If race condition TTL is in use, ensure that cache entries
385
377
  # stick around a bit longer after they would have expired
386
378
  # so we can purposefully serve stale entries.
@@ -388,16 +380,14 @@ module ActiveSupport
388
380
  expires_in += 5.minutes
389
381
  end
390
382
 
391
- failsafe :write_entry, returning: false do
392
- if unless_exist || expires_in
393
- modifiers = {}
394
- modifiers[:nx] = unless_exist
395
- modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
383
+ modifiers = {}
384
+ if unless_exist || expires_in
385
+ modifiers[:nx] = unless_exist
386
+ modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
387
+ end
396
388
 
397
- redis.with { |c| c.set key, serialized_entry, **modifiers }
398
- else
399
- redis.with { |c| c.set key, serialized_entry }
400
- end
389
+ failsafe :write_entry, returning: false do
390
+ redis.with { |c| c.set key, payload, **modifiers }
401
391
  end
402
392
  end
403
393
 
@@ -414,12 +404,20 @@ module ActiveSupport
414
404
  end
415
405
  end
416
406
 
407
+ # Deletes multiple entries in the cache. Returns the number of entries deleted.
408
+ def delete_multi_entries(entries, **_options)
409
+ redis.with { |c| c.del(entries) }
410
+ end
411
+
417
412
  # Nonstandard store provider API to write multiple values at once.
418
413
  def write_multi_entries(entries, expires_in: nil, **options)
419
414
  if entries.any?
420
415
  if mset_capable? && expires_in.nil?
421
416
  failsafe :write_multi_entries do
422
- redis.with { |c| c.mapped_mset(serialize_entries(entries, raw: options[:raw])) }
417
+ payload = serialize_entries(entries, **options)
418
+ redis.with do |c|
419
+ c.mapped_mset(payload)
420
+ end
423
421
  end
424
422
  else
425
423
  super
@@ -429,12 +427,12 @@ module ActiveSupport
429
427
 
430
428
  # Truncate keys that exceed 1kB.
431
429
  def normalize_key(key, options)
432
- truncate_key super.b
430
+ truncate_key super&.b
433
431
  end
434
432
 
435
433
  def truncate_key(key)
436
- if key.bytesize > max_key_bytesize
437
- suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
434
+ if key && key.bytesize > max_key_bytesize
435
+ suffix = ":hash:#{ActiveSupport::Digest.hexdigest(key)}"
438
436
  truncate_at = max_key_bytesize - suffix.bytesize
439
437
  "#{key.byteslice(0, truncate_at)}#{suffix}"
440
438
  else
@@ -442,52 +440,35 @@ module ActiveSupport
442
440
  end
443
441
  end
444
442
 
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)
443
+ def deserialize_entry(payload, raw: false, **)
444
+ if raw && !payload.nil?
445
+ Entry.new(payload)
446
+ else
447
+ super(payload)
460
448
  end
461
449
  end
462
450
 
463
- def serialize_entry(entry, raw: false)
451
+ def serialize_entry(entry, raw: false, **options)
464
452
  if raw
465
453
  entry.value.to_s
466
454
  else
467
- Marshal.dump(entry)
455
+ super(entry, raw: raw, **options)
468
456
  end
469
457
  end
470
458
 
471
- def serialize_entries(entries, raw: false)
459
+ def serialize_entries(entries, **options)
472
460
  entries.transform_values do |entry|
473
- serialize_entry entry, raw: raw
461
+ serialize_entry(entry, **options)
474
462
  end
475
463
  end
476
464
 
477
465
  def failsafe(method, returning: nil)
478
466
  yield
479
- rescue ::Redis::BaseError => e
480
- handle_exception exception: e, method: method, returning: returning
467
+ rescue ::Redis::BaseError => error
468
+ ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
469
+ @error_handler&.call(method: method, exception: error, returning: returning)
481
470
  returning
482
471
  end
483
-
484
- def handle_exception(exception:, method:, returning:)
485
- if @error_handler
486
- @error_handler.(method: method, exception: exception, returning: returning)
487
- end
488
- rescue => failsafe
489
- warn "RedisCacheStore ignored exception in handle_exception: #{failsafe.class}: #{failsafe.message}\n #{failsafe.backtrace.join("\n ")}"
490
- end
491
472
  end
492
473
  end
493
474
  end
@@ -1,8 +1,6 @@
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
- require "active_support/per_thread_registry"
6
4
 
7
5
  module ActiveSupport
8
6
  module Cache
@@ -14,74 +12,56 @@ module ActiveSupport
14
12
  autoload :Middleware, "active_support/cache/strategy/local_cache_middleware"
15
13
 
16
14
  # Class for storing and registering the local caches.
17
- class LocalCacheRegistry # :nodoc:
18
- extend ActiveSupport::PerThreadRegistry
19
-
20
- def initialize
21
- @registry = {}
22
- end
15
+ module LocalCacheRegistry # :nodoc:
16
+ extend self
23
17
 
24
18
  def cache_for(local_cache_key)
25
- @registry[local_cache_key]
19
+ registry = ActiveSupport::IsolatedExecutionState[:active_support_local_cache_registry] ||= {}
20
+ registry[local_cache_key]
26
21
  end
27
22
 
28
23
  def set_cache_for(local_cache_key, value)
29
- @registry[local_cache_key] = value
24
+ registry = ActiveSupport::IsolatedExecutionState[:active_support_local_cache_registry] ||= {}
25
+ registry[local_cache_key] = value
30
26
  end
31
-
32
- def self.set_cache_for(l, v); instance.set_cache_for l, v; end
33
- def self.cache_for(l); instance.cache_for l; end
34
27
  end
35
28
 
36
29
  # Simple memory backed cache. This cache is not thread safe and is intended only
37
30
  # for serving as a temporary memory cache for a single thread.
38
- class LocalStore < Store
31
+ class LocalStore
39
32
  def initialize
40
- super
41
33
  @data = {}
42
34
  end
43
35
 
44
- # Don't allow synchronizing since it isn't thread safe.
45
- def synchronize # :nodoc:
46
- yield
47
- end
48
-
49
36
  def clear(options = nil)
50
37
  @data.clear
51
38
  end
52
39
 
53
- def read_entry(key, **options)
40
+ def read_entry(key)
54
41
  @data[key]
55
42
  end
56
43
 
57
- def read_multi_entries(keys, **options)
58
- values = {}
59
-
60
- keys.each do |name|
61
- entry = read_entry(name, **options)
62
- values[name] = entry.value if entry
63
- end
64
-
65
- values
44
+ def read_multi_entries(keys)
45
+ @data.slice(*keys)
66
46
  end
67
47
 
68
- def write_entry(key, value, **options)
69
- @data[key] = value
48
+ def write_entry(key, entry)
49
+ @data[key] = entry
70
50
  true
71
51
  end
72
52
 
73
- def delete_entry(key, **options)
53
+ def delete_entry(key)
74
54
  !!@data.delete(key)
75
55
  end
76
56
 
77
- def fetch_entry(key, options = nil) # :nodoc:
57
+ def fetch_entry(key) # :nodoc:
78
58
  @data.fetch(key) { @data[key] = yield }
79
59
  end
80
60
  end
81
61
 
82
62
  # Use a local cache for the duration of block.
83
- def with_local_cache
84
- use_temporary_local_cache(LocalStore.new) { yield }
63
+ def with_local_cache(&block)
64
+ use_temporary_local_cache(LocalStore.new, &block)
85
65
  end
86
66
 
87
67
  # Middleware class can be inserted as a Rack handler to be local cache for the
@@ -104,24 +84,36 @@ module ActiveSupport
104
84
  super
105
85
  end
106
86
 
87
+ def delete_matched(matcher, options = nil) # :nodoc:
88
+ return super unless cache = local_cache
89
+ cache.clear
90
+ super
91
+ end
92
+
107
93
  def increment(name, amount = 1, **options) # :nodoc:
108
94
  return super unless local_cache
109
95
  value = bypass_local_cache { super }
110
- write_cache_value(name, value, **options)
96
+ write_cache_value(name, value, raw: true, **options)
111
97
  value
112
98
  end
113
99
 
114
100
  def decrement(name, amount = 1, **options) # :nodoc:
115
101
  return super unless local_cache
116
102
  value = bypass_local_cache { super }
117
- write_cache_value(name, value, **options)
103
+ write_cache_value(name, value, raw: true, **options)
118
104
  value
119
105
  end
120
106
 
121
107
  private
122
- def read_entry(key, **options)
108
+ def read_serialized_entry(key, raw: false, **options)
123
109
  if cache = local_cache
124
- cache.fetch_entry(key) { super }
110
+ hit = true
111
+ entry = cache.fetch_entry(key) do
112
+ hit = false
113
+ super
114
+ end
115
+ options[:event][:store] = cache.class.name if hit && options[:event]
116
+ entry
125
117
  else
126
118
  super
127
119
  end
@@ -130,7 +122,10 @@ module ActiveSupport
130
122
  def read_multi_entries(keys, **options)
131
123
  return super unless local_cache
132
124
 
133
- local_entries = local_cache.read_multi_entries(keys, **options)
125
+ local_entries = local_cache.read_multi_entries(keys)
126
+ local_entries.transform_values! do |payload|
127
+ deserialize_entry(payload).value
128
+ end
134
129
  missed_keys = keys - local_entries.keys
135
130
 
136
131
  if missed_keys.any?
@@ -140,30 +135,27 @@ module ActiveSupport
140
135
  end
141
136
  end
142
137
 
143
- def write_entry(key, entry, **options)
144
- if options[:unless_exist]
145
- local_cache.delete_entry(key, **options) if local_cache
138
+ def write_serialized_entry(key, payload, **)
139
+ if return_value = super
140
+ local_cache.write_entry(key, payload) if local_cache
146
141
  else
147
- local_cache.write_entry(key, entry, **options) if local_cache
142
+ local_cache.delete_entry(key) if local_cache
148
143
  end
149
-
150
- super
144
+ return_value
151
145
  end
152
146
 
153
- def delete_entry(key, **options)
154
- local_cache.delete_entry(key, **options) if local_cache
147
+ def delete_entry(key, **)
148
+ local_cache.delete_entry(key) if local_cache
155
149
  super
156
150
  end
157
151
 
158
152
  def write_cache_value(name, value, **options)
159
153
  name = normalize_key(name, options)
160
154
  cache = local_cache
161
- cache.mute do
162
- if value
163
- cache.write(name, value, options)
164
- else
165
- cache.delete(name, **options)
166
- end
155
+ if value
156
+ cache.write_entry(name, serialize_entry(new_entry(value, **options), **options))
157
+ else
158
+ cache.delete_entry(name)
167
159
  end
168
160
  end
169
161
 
@@ -175,8 +167,8 @@ module ActiveSupport
175
167
  LocalCacheRegistry.cache_for(local_cache_key)
176
168
  end
177
169
 
178
- def bypass_local_cache
179
- use_temporary_local_cache(nil) { yield }
170
+ def bypass_local_cache(&block)
171
+ use_temporary_local_cache(nil, &block)
180
172
  end
181
173
 
182
174
  def use_temporary_local_cache(temporary_cache)