activesupport 5.2.0 → 6.1.0

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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +362 -333
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +29 -3
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache/file_store.rb +33 -33
  10. data/lib/active_support/cache/mem_cache_store.rb +31 -29
  11. data/lib/active_support/cache/memory_store.rb +59 -33
  12. data/lib/active_support/cache/null_store.rb +8 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +84 -45
  14. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  15. data/lib/active_support/cache.rb +174 -113
  16. data/lib/active_support/callbacks.rb +81 -64
  17. data/lib/active_support/concern.rb +76 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  19. data/lib/active_support/concurrency/share_lock.rb +0 -1
  20. data/lib/active_support/configurable.rb +10 -14
  21. data/lib/active_support/configuration_file.rb +46 -0
  22. data/lib/active_support/core_ext/array/access.rb +18 -6
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/array/extract.rb +21 -0
  25. data/lib/active_support/core_ext/array.rb +1 -1
  26. data/lib/active_support/core_ext/benchmark.rb +2 -2
  27. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  28. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  29. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  32. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  33. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  34. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  36. data/lib/active_support/core_ext/digest.rb +3 -0
  37. data/lib/active_support/core_ext/enumerable.rb +171 -70
  38. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  39. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  40. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  41. data/lib/active_support/core_ext/hash/except.rb +2 -2
  42. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  43. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  44. data/lib/active_support/core_ext/hash.rb +1 -2
  45. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  46. data/lib/active_support/core_ext/kernel.rb +0 -1
  47. data/lib/active_support/core_ext/load_error.rb +1 -1
  48. data/lib/active_support/core_ext/marshal.rb +2 -0
  49. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  50. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  51. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  52. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  53. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  54. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  55. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  56. data/lib/active_support/core_ext/module.rb +0 -1
  57. data/lib/active_support/core_ext/name_error.rb +29 -2
  58. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  59. data/lib/active_support/core_ext/numeric.rb +0 -1
  60. data/lib/active_support/core_ext/object/blank.rb +1 -2
  61. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  62. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  63. data/lib/active_support/core_ext/object/json.rb +7 -2
  64. data/lib/active_support/core_ext/object/to_query.rb +5 -2
  65. data/lib/active_support/core_ext/object/try.rb +17 -7
  66. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  67. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  68. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  69. data/lib/active_support/core_ext/range/each.rb +0 -1
  70. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  71. data/lib/active_support/core_ext/range.rb +1 -1
  72. data/lib/active_support/core_ext/regexp.rb +8 -5
  73. data/lib/active_support/core_ext/securerandom.rb +23 -3
  74. data/lib/active_support/core_ext/string/access.rb +5 -16
  75. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  76. data/lib/active_support/core_ext/string/filters.rb +42 -1
  77. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  78. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  79. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  80. data/lib/active_support/core_ext/string/output_safety.rb +69 -12
  81. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  82. data/lib/active_support/core_ext/string/strip.rb +3 -1
  83. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  84. data/lib/active_support/core_ext/symbol.rb +3 -0
  85. data/lib/active_support/core_ext/time/calculations.rb +50 -3
  86. data/lib/active_support/core_ext/time/conversions.rb +1 -0
  87. data/lib/active_support/core_ext/uri.rb +7 -5
  88. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  89. data/lib/active_support/current_attributes.rb +15 -2
  90. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  91. data/lib/active_support/dependencies.rb +118 -35
  92. data/lib/active_support/deprecation/behaviors.rb +20 -3
  93. data/lib/active_support/deprecation/disallowed.rb +56 -0
  94. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  95. data/lib/active_support/deprecation/method_wrappers.rb +21 -13
  96. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  97. data/lib/active_support/deprecation/reporting.rb +51 -8
  98. data/lib/active_support/deprecation.rb +6 -1
  99. data/lib/active_support/descendants_tracker.rb +59 -9
  100. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  101. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  102. data/lib/active_support/duration.rb +90 -38
  103. data/lib/active_support/encrypted_configuration.rb +1 -5
  104. data/lib/active_support/encrypted_file.rb +23 -5
  105. data/lib/active_support/environment_inquirer.rb +20 -0
  106. data/lib/active_support/evented_file_update_checker.rb +82 -117
  107. data/lib/active_support/execution_wrapper.rb +1 -0
  108. data/lib/active_support/file_update_checker.rb +0 -1
  109. data/lib/active_support/fork_tracker.rb +62 -0
  110. data/lib/active_support/gem_version.rb +2 -2
  111. data/lib/active_support/hash_with_indifferent_access.rb +78 -41
  112. data/lib/active_support/i18n.rb +1 -0
  113. data/lib/active_support/i18n_railtie.rb +16 -5
  114. data/lib/active_support/inflector/inflections.rb +2 -7
  115. data/lib/active_support/inflector/methods.rb +50 -57
  116. data/lib/active_support/inflector/transliterate.rb +47 -18
  117. data/lib/active_support/json/decoding.rb +25 -26
  118. data/lib/active_support/json/encoding.rb +11 -3
  119. data/lib/active_support/key_generator.rb +1 -33
  120. data/lib/active_support/lazy_load_hooks.rb +5 -2
  121. data/lib/active_support/locale/en.rb +33 -0
  122. data/lib/active_support/locale/en.yml +7 -3
  123. data/lib/active_support/log_subscriber.rb +39 -9
  124. data/lib/active_support/logger.rb +2 -17
  125. data/lib/active_support/logger_silence.rb +11 -19
  126. data/lib/active_support/logger_thread_safe_level.rb +52 -7
  127. data/lib/active_support/message_encryptor.rb +8 -13
  128. data/lib/active_support/message_verifier.rb +10 -10
  129. data/lib/active_support/messages/metadata.rb +11 -2
  130. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  131. data/lib/active_support/messages/rotator.rb +10 -9
  132. data/lib/active_support/multibyte/chars.rb +10 -68
  133. data/lib/active_support/multibyte/unicode.rb +15 -327
  134. data/lib/active_support/notifications/fanout.rb +116 -16
  135. data/lib/active_support/notifications/instrumenter.rb +71 -9
  136. data/lib/active_support/notifications.rb +72 -8
  137. data/lib/active_support/number_helper/number_converter.rb +5 -6
  138. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  139. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  140. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  141. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  142. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  143. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  144. data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
  145. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  146. data/lib/active_support/number_helper.rb +38 -12
  147. data/lib/active_support/option_merger.rb +22 -3
  148. data/lib/active_support/ordered_hash.rb +1 -1
  149. data/lib/active_support/ordered_options.rb +13 -3
  150. data/lib/active_support/parameter_filter.rb +133 -0
  151. data/lib/active_support/per_thread_registry.rb +1 -1
  152. data/lib/active_support/rails.rb +1 -10
  153. data/lib/active_support/railtie.rb +23 -1
  154. data/lib/active_support/reloader.rb +4 -5
  155. data/lib/active_support/secure_compare_rotator.rb +51 -0
  156. data/lib/active_support/security_utils.rb +19 -12
  157. data/lib/active_support/string_inquirer.rb +4 -3
  158. data/lib/active_support/subscriber.rb +72 -24
  159. data/lib/active_support/tagged_logging.rb +42 -8
  160. data/lib/active_support/test_case.rb +92 -1
  161. data/lib/active_support/testing/assertions.rb +30 -9
  162. data/lib/active_support/testing/deprecation.rb +0 -1
  163. data/lib/active_support/testing/file_fixtures.rb +2 -0
  164. data/lib/active_support/testing/isolation.rb +2 -2
  165. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  166. data/lib/active_support/testing/parallelization/server.rb +78 -0
  167. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  168. data/lib/active_support/testing/parallelization.rb +51 -0
  169. data/lib/active_support/testing/setup_and_teardown.rb +5 -9
  170. data/lib/active_support/testing/stream.rb +1 -2
  171. data/lib/active_support/testing/time_helpers.rb +47 -12
  172. data/lib/active_support/time_with_zone.rb +81 -47
  173. data/lib/active_support/values/time_zone.rb +34 -18
  174. data/lib/active_support/xml_mini/jdom.rb +2 -3
  175. data/lib/active_support/xml_mini/libxml.rb +2 -2
  176. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  177. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  178. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  179. data/lib/active_support/xml_mini/rexml.rb +10 -3
  180. data/lib/active_support/xml_mini.rb +2 -10
  181. data/lib/active_support.rb +14 -1
  182. metadata +57 -30
  183. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  184. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  185. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  186. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  187. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  188. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  189. data/lib/active_support/core_ext/range/include_range.rb +0 -25
  190. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -3,10 +3,12 @@
3
3
  require "zlib"
4
4
  require "active_support/core_ext/array/extract_options"
5
5
  require "active_support/core_ext/array/wrap"
6
+ require "active_support/core_ext/enumerable"
6
7
  require "active_support/core_ext/module/attribute_accessors"
7
8
  require "active_support/core_ext/numeric/bytes"
8
9
  require "active_support/core_ext/numeric/time"
9
10
  require "active_support/core_ext/object/to_param"
11
+ require "active_support/core_ext/object/try"
10
12
  require "active_support/core_ext/string/inflections"
11
13
 
12
14
  module ActiveSupport
@@ -20,7 +22,7 @@ module ActiveSupport
20
22
 
21
23
  # These options mean something to all cache implementations. Individual cache
22
24
  # implementations may support additional options.
23
- UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
25
+ UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl, :coder]
24
26
 
25
27
  module Strategy
26
28
  autoload :LocalCache, "active_support/cache/strategy/local_cache"
@@ -52,12 +54,13 @@ module ActiveSupport
52
54
  #
53
55
  # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
54
56
  # # => returns MyOwnCacheStore.new
55
- def lookup_store(*store_option)
56
- store, *parameters = *Array.wrap(store_option).flatten
57
-
57
+ def lookup_store(store = nil, *parameters)
58
58
  case store
59
59
  when Symbol
60
- retrieve_store_class(store).new(*parameters)
60
+ options = parameters.extract_options!
61
+ retrieve_store_class(store).new(*parameters, **options)
62
+ when Array
63
+ lookup_store(*store)
61
64
  when nil
62
65
  ActiveSupport::Cache::MemoryStore.new
63
66
  else
@@ -78,7 +81,7 @@ module ActiveSupport
78
81
  #
79
82
  # The +key+ argument can also respond to +cache_key+ or +to_param+.
80
83
  def expand_cache_key(key, namespace = nil)
81
- expanded_cache_key = (namespace ? "#{namespace}/" : "").dup
84
+ expanded_cache_key = namespace ? +"#{namespace}/" : +""
82
85
 
83
86
  if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
84
87
  expanded_cache_key << "#{prefix}/"
@@ -155,6 +158,8 @@ module ActiveSupport
155
158
  # threshold is configurable with the <tt>:compress_threshold</tt> option,
156
159
  # specified in bytes.
157
160
  class Store
161
+ DEFAULT_CODER = Marshal
162
+
158
163
  cattr_accessor :logger, instance_writer: true
159
164
 
160
165
  attr_reader :silence, :options
@@ -182,6 +187,7 @@ module ActiveSupport
182
187
  # namespace for the cache.
183
188
  def initialize(options = nil)
184
189
  @options = options ? options.dup : {}
190
+ @coder = @options.delete(:coder) { self.class::DEFAULT_CODER } || NullCoder
185
191
  end
186
192
 
187
193
  # Silences the logger.
@@ -229,6 +235,14 @@ module ActiveSupport
229
235
  # ask whether you should force a cache write. Otherwise, it's clearer to
230
236
  # just call <tt>Cache#write</tt>.
231
237
  #
238
+ # Setting <tt>skip_nil: true</tt> will not cache nil result:
239
+ #
240
+ # cache.fetch('foo') { nil }
241
+ # cache.fetch('bar', skip_nil: true) { nil }
242
+ # cache.exist?('foo') # => true
243
+ # cache.exist?('bar') # => false
244
+ #
245
+ #
232
246
  # Setting <tt>compress: false</tt> disables compression of the cache entry.
233
247
  #
234
248
  # Setting <tt>:expires_in</tt> will set an expiration time on the cache.
@@ -303,14 +317,14 @@ module ActiveSupport
303
317
  # :bar
304
318
  # end
305
319
  # cache.fetch('foo') # => "bar"
306
- def fetch(name, options = nil)
320
+ def fetch(name, options = nil, &block)
307
321
  if block_given?
308
322
  options = merged_options(options)
309
323
  key = normalize_key(name, options)
310
324
 
311
325
  entry = nil
312
326
  instrument(:read, name, options) do |payload|
313
- cached_entry = read_entry(key, options) unless options[:force]
327
+ cached_entry = read_entry(key, **options, event: payload) unless options[:force]
314
328
  entry = handle_expired_entry(cached_entry, key, options)
315
329
  entry = nil if entry && entry.mismatched?(normalize_version(name, options))
316
330
  payload[:super_operation] = :fetch if payload
@@ -320,7 +334,7 @@ module ActiveSupport
320
334
  if entry
321
335
  get_entry_value(entry, name, options)
322
336
  else
323
- save_block_result_to_cache(name, options) { |_name| yield _name }
337
+ save_block_result_to_cache(name, options, &block)
324
338
  end
325
339
  elsif options && options[:force]
326
340
  raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
@@ -333,8 +347,9 @@ module ActiveSupport
333
347
  # the cache with the given key, then that data is returned. Otherwise,
334
348
  # +nil+ is returned.
335
349
  #
336
- # Note, if data was written with the <tt>:expires_in<tt> or <tt>:version</tt> options,
337
- # both of these conditions are applied before the data is returned.
350
+ # Note, if data was written with the <tt>:expires_in</tt> or
351
+ # <tt>:version</tt> options, both of these conditions are applied before
352
+ # the data is returned.
338
353
  #
339
354
  # Options are passed to the underlying cache implementation.
340
355
  def read(name, options = nil)
@@ -343,11 +358,11 @@ module ActiveSupport
343
358
  version = normalize_version(name, options)
344
359
 
345
360
  instrument(:read, name, options) do |payload|
346
- entry = read_entry(key, options)
361
+ entry = read_entry(key, **options, event: payload)
347
362
 
348
363
  if entry
349
364
  if entry.expired?
350
- delete_entry(key, options)
365
+ delete_entry(key, **options)
351
366
  payload[:hit] = false if payload
352
367
  nil
353
368
  elsif entry.mismatched?(version)
@@ -375,7 +390,7 @@ module ActiveSupport
375
390
  options = merged_options(options)
376
391
 
377
392
  instrument :read_multi, names, options do |payload|
378
- read_multi_entries(names, options).tap do |results|
393
+ read_multi_entries(names, **options, event: payload).tap do |results|
379
394
  payload[:hits] = results.keys
380
395
  end
381
396
  end
@@ -387,10 +402,10 @@ module ActiveSupport
387
402
 
388
403
  instrument :write_multi, hash, options do |payload|
389
404
  entries = hash.each_with_object({}) do |(name, value), memo|
390
- memo[normalize_key(name, options)] = Entry.new(value, options.merge(version: normalize_version(name, options)))
405
+ memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
391
406
  end
392
407
 
393
- write_multi_entries entries, options
408
+ write_multi_entries entries, **options
394
409
  end
395
410
  end
396
411
 
@@ -402,8 +417,6 @@ module ActiveSupport
402
417
  # to the cache. If you do not want to write the cache when the cache is
403
418
  # not found, use #read_multi.
404
419
  #
405
- # Options are passed to the underlying cache implementation.
406
- #
407
420
  # Returns a hash with the data for each of the names. For example:
408
421
  #
409
422
  # cache.write("bim", "bam")
@@ -413,6 +426,17 @@ module ActiveSupport
413
426
  # # => { "bim" => "bam",
414
427
  # # "unknown_key" => "Fallback value for key: unknown_key" }
415
428
  #
429
+ # Options are passed to the underlying cache implementation. For example:
430
+ #
431
+ # cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
432
+ # "buzz"
433
+ # end
434
+ # # => {"fizz"=>"buzz"}
435
+ # cache.read("fizz")
436
+ # # => "buzz"
437
+ # sleep(6)
438
+ # cache.read("fizz")
439
+ # # => nil
416
440
  def fetch_multi(*names)
417
441
  raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
418
442
 
@@ -420,18 +444,18 @@ module ActiveSupport
420
444
  options = merged_options(options)
421
445
 
422
446
  instrument :read_multi, names, options do |payload|
423
- read_multi_entries(names, options).tap do |results|
424
- payload[:hits] = results.keys
425
- payload[:super_operation] = :fetch_multi
447
+ reads = read_multi_entries(names, **options)
448
+ writes = {}
449
+ ordered = names.index_with do |name|
450
+ reads.fetch(name) { writes[name] = yield(name) }
451
+ end
426
452
 
427
- writes = {}
453
+ payload[:hits] = reads.keys
454
+ payload[:super_operation] = :fetch_multi
428
455
 
429
- (names - results.keys).each do |name|
430
- results[name] = writes[name] = yield(name)
431
- end
456
+ write_multi(writes, options)
432
457
 
433
- write_multi writes, options
434
- end
458
+ ordered
435
459
  end
436
460
  end
437
461
 
@@ -442,8 +466,8 @@ module ActiveSupport
442
466
  options = merged_options(options)
443
467
 
444
468
  instrument(:write, name, options) do
445
- entry = Entry.new(value, options.merge(version: normalize_version(name, options)))
446
- write_entry(normalize_key(name, options), entry, options)
469
+ entry = Entry.new(value, **options.merge(version: normalize_version(name, options)))
470
+ write_entry(normalize_key(name, options), entry, **options)
447
471
  end
448
472
  end
449
473
 
@@ -454,7 +478,19 @@ module ActiveSupport
454
478
  options = merged_options(options)
455
479
 
456
480
  instrument(:delete, name) do
457
- delete_entry(normalize_key(name, options), options)
481
+ delete_entry(normalize_key(name, options), **options)
482
+ end
483
+ end
484
+
485
+ # Deletes multiple entries in the cache.
486
+ #
487
+ # Options are passed to the underlying cache implementation.
488
+ def delete_multi(names, options = nil)
489
+ options = merged_options(options)
490
+ names.map! { |key| normalize_key(key, options) }
491
+
492
+ instrument :delete_multi, names do
493
+ delete_multi_entries(names, **options)
458
494
  end
459
495
  end
460
496
 
@@ -464,8 +500,8 @@ module ActiveSupport
464
500
  def exist?(name, options = nil)
465
501
  options = merged_options(options)
466
502
 
467
- instrument(:exist?, name) do
468
- entry = read_entry(normalize_key(name, options), options)
503
+ instrument(:exist?, name) do |payload|
504
+ entry = read_entry(normalize_key(name, options), **options, event: payload)
469
505
  (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
470
506
  end
471
507
  end
@@ -474,7 +510,7 @@ module ActiveSupport
474
510
  #
475
511
  # Options are passed to the underlying cache implementation.
476
512
  #
477
- # All implementations may not support this method.
513
+ # Some implementations may not support this method.
478
514
  def delete_matched(matcher, options = nil)
479
515
  raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
480
516
  end
@@ -483,7 +519,7 @@ module ActiveSupport
483
519
  #
484
520
  # Options are passed to the underlying cache implementation.
485
521
  #
486
- # All implementations may not support this method.
522
+ # Some implementations may not support this method.
487
523
  def increment(name, amount = 1, options = nil)
488
524
  raise NotImplementedError.new("#{self.class.name} does not support increment")
489
525
  end
@@ -492,7 +528,7 @@ module ActiveSupport
492
528
  #
493
529
  # Options are passed to the underlying cache implementation.
494
530
  #
495
- # All implementations may not support this method.
531
+ # Some implementations may not support this method.
496
532
  def decrement(name, amount = 1, options = nil)
497
533
  raise NotImplementedError.new("#{self.class.name} does not support decrement")
498
534
  end
@@ -501,7 +537,7 @@ module ActiveSupport
501
537
  #
502
538
  # Options are passed to the underlying cache implementation.
503
539
  #
504
- # All implementations may not support this method.
540
+ # Some implementations may not support this method.
505
541
  def cleanup(options = nil)
506
542
  raise NotImplementedError.new("#{self.class.name} does not support cleanup")
507
543
  end
@@ -511,7 +547,7 @@ module ActiveSupport
511
547
  #
512
548
  # The options hash is passed to the underlying cache implementation.
513
549
  #
514
- # All implementations may not support this method.
550
+ # Some implementations may not support this method.
515
551
  def clear(options = nil)
516
552
  raise NotImplementedError.new("#{self.class.name} does not support clear")
517
553
  end
@@ -538,58 +574,73 @@ module ActiveSupport
538
574
 
539
575
  # Reads an entry from the cache implementation. Subclasses must implement
540
576
  # this method.
541
- def read_entry(key, options)
577
+ def read_entry(key, **options)
542
578
  raise NotImplementedError.new
543
579
  end
544
580
 
545
581
  # Writes an entry to the cache implementation. Subclasses must implement
546
582
  # this method.
547
- def write_entry(key, entry, options)
583
+ def write_entry(key, entry, **options)
548
584
  raise NotImplementedError.new
549
585
  end
550
586
 
587
+ def serialize_entry(entry)
588
+ @coder.dump(entry)
589
+ end
590
+
591
+ def deserialize_entry(payload)
592
+ payload.nil? ? nil : @coder.load(payload)
593
+ end
594
+
551
595
  # Reads multiple entries from the cache implementation. Subclasses MAY
552
596
  # implement this method.
553
- def read_multi_entries(names, options)
554
- results = {}
555
- names.each do |name|
556
- key = normalize_key(name, options)
597
+ def read_multi_entries(names, **options)
598
+ names.each_with_object({}) do |name, results|
599
+ key = normalize_key(name, options)
600
+ entry = read_entry(key, **options)
601
+
602
+ next unless entry
603
+
557
604
  version = normalize_version(name, options)
558
- entry = read_entry(key, options)
559
-
560
- if entry
561
- if entry.expired?
562
- delete_entry(key, options)
563
- elsif entry.mismatched?(version)
564
- # Skip mismatched versions
565
- else
566
- results[name] = entry.value
567
- end
605
+
606
+ if entry.expired?
607
+ delete_entry(key, **options)
608
+ elsif !entry.mismatched?(version)
609
+ results[name] = entry.value
568
610
  end
569
611
  end
570
- results
571
612
  end
572
613
 
573
614
  # Writes multiple entries to the cache implementation. Subclasses MAY
574
615
  # implement this method.
575
- def write_multi_entries(hash, options)
616
+ def write_multi_entries(hash, **options)
576
617
  hash.each do |key, entry|
577
- write_entry key, entry, options
618
+ write_entry key, entry, **options
578
619
  end
579
620
  end
580
621
 
581
622
  # Deletes an entry from the cache implementation. Subclasses must
582
623
  # implement this method.
583
- def delete_entry(key, options)
624
+ def delete_entry(key, **options)
584
625
  raise NotImplementedError.new
585
626
  end
586
627
 
628
+ # Deletes multiples entries in the cache implementation. Subclasses MAY
629
+ # implement this method.
630
+ def delete_multi_entries(entries, **options)
631
+ entries.count { |key| delete_entry(key, **options) }
632
+ end
633
+
587
634
  # Merges the default options with ones specific to a method call.
588
635
  def merged_options(call_options)
589
636
  if call_options
590
- options.merge(call_options)
637
+ if options.empty?
638
+ call_options
639
+ else
640
+ options.merge(call_options)
641
+ end
591
642
  else
592
- options.dup
643
+ options
593
644
  end
594
645
  end
595
646
 
@@ -616,6 +667,10 @@ module ActiveSupport
616
667
  namespace = namespace.call
617
668
  end
618
669
 
670
+ if key && key.encoding != Encoding::UTF_8
671
+ key = key.dup.force_encoding(Encoding::UTF_8)
672
+ end
673
+
619
674
  if namespace
620
675
  "#{namespace}:#{key}"
621
676
  else
@@ -632,15 +687,15 @@ module ActiveSupport
632
687
  case key
633
688
  when Array
634
689
  if key.size > 1
635
- key = key.collect { |element| expanded_key(element) }
690
+ key.collect { |element| expanded_key(element) }
636
691
  else
637
- key = key.first
692
+ expanded_key(key.first)
638
693
  end
639
694
  when Hash
640
- key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
641
- end
642
-
643
- key.to_param
695
+ key.collect { |k, v| "#{k}=#{v}" }.sort!
696
+ else
697
+ key
698
+ end.to_param
644
699
  end
645
700
 
646
701
  def normalize_version(key, options = nil)
@@ -650,24 +705,21 @@ module ActiveSupport
650
705
  def expanded_version(key)
651
706
  case
652
707
  when key.respond_to?(:cache_version) then key.cache_version.to_param
653
- when key.is_a?(Array) then key.map { |element| expanded_version(element) }.compact.to_param
708
+ when key.is_a?(Array) then key.map { |element| expanded_version(element) }.tap(&:compact!).to_param
654
709
  when key.respond_to?(:to_a) then expanded_version(key.to_a)
655
710
  end
656
711
  end
657
712
 
658
713
  def instrument(operation, key, options = nil)
659
- log { "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" }
714
+ if logger && logger.debug? && !silence?
715
+ logger.debug "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}"
716
+ end
660
717
 
661
- payload = { key: key }
718
+ payload = { key: key, store: self.class.name }
662
719
  payload.merge!(options) if options.is_a?(Hash)
663
720
  ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
664
721
  end
665
722
 
666
- def log
667
- return unless logger && logger.debug? && !silence?
668
- logger.debug(yield)
669
- end
670
-
671
723
  def handle_expired_entry(entry, key, options)
672
724
  if entry && entry.expired?
673
725
  race_ttl = options[:race_condition_ttl].to_i
@@ -677,7 +729,7 @@ module ActiveSupport
677
729
  entry.expires_at = Time.now + race_ttl
678
730
  write_entry(key, entry, expires_in: race_ttl * 2)
679
731
  else
680
- delete_entry(key, options)
732
+ delete_entry(key, **options)
681
733
  end
682
734
  entry = nil
683
735
  end
@@ -685,7 +737,7 @@ module ActiveSupport
685
737
  end
686
738
 
687
739
  def get_entry_value(entry, name, options)
688
- instrument(:fetch_hit, name, options) {}
740
+ instrument(:fetch_hit, name, options) { }
689
741
  entry.value
690
742
  end
691
743
 
@@ -694,11 +746,23 @@ module ActiveSupport
694
746
  yield(name)
695
747
  end
696
748
 
697
- write(name, result, options)
749
+ write(name, result, options) unless result.nil? && options[:skip_nil]
698
750
  result
699
751
  end
700
752
  end
701
753
 
754
+ module NullCoder # :nodoc:
755
+ class << self
756
+ def load(payload)
757
+ payload
758
+ end
759
+
760
+ def dump(entry)
761
+ entry
762
+ end
763
+ end
764
+ end
765
+
702
766
  # This class is used to represent cache entries. Cache entries have a value, an optional
703
767
  # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
704
768
  # on the cache. The version is used to support the :version option on the cache for rejecting
@@ -712,17 +776,14 @@ module ActiveSupport
712
776
  DEFAULT_COMPRESS_LIMIT = 1.kilobyte
713
777
 
714
778
  # Creates a new cache entry for the specified value. Options supported are
715
- # +:compress+, +:compress_threshold+, and +:expires_in+.
716
- def initialize(value, options = {})
717
- @value = value
718
- if should_compress?(options)
719
- compress!
720
- end
721
-
722
- @version = options[:version]
779
+ # +:compress+, +:compress_threshold+, +:version+ and +:expires_in+.
780
+ def initialize(value, compress: true, compress_threshold: DEFAULT_COMPRESS_LIMIT, version: nil, expires_in: nil, **)
781
+ @value = value
782
+ @version = version
723
783
  @created_at = Time.now.to_f
724
- @expires_in = options[:expires_in]
725
- @expires_in = @expires_in.to_f if @expires_in
784
+ @expires_in = expires_in && expires_in.to_f
785
+
786
+ compress!(compress_threshold) if compress
726
787
  end
727
788
 
728
789
  def value
@@ -752,19 +813,15 @@ module ActiveSupport
752
813
  end
753
814
 
754
815
  # Returns the size of the cached value. This could be less than
755
- # <tt>value.size</tt> if the data is compressed.
756
- def size
757
- if defined?(@s)
758
- @s
816
+ # <tt>value.bytesize</tt> if the data is compressed.
817
+ def bytesize
818
+ case value
819
+ when NilClass
820
+ 0
821
+ when String
822
+ @value.bytesize
759
823
  else
760
- case value
761
- when NilClass
762
- 0
763
- when String
764
- @value.bytesize
765
- else
766
- @s = Marshal.dump(@value).bytesize
767
- end
824
+ @s ||= Marshal.dump(@value).bytesize
768
825
  end
769
826
  end
770
827
 
@@ -781,31 +838,35 @@ module ActiveSupport
781
838
  end
782
839
 
783
840
  private
784
- def should_compress?(options)
785
- if @value && options.fetch(:compress, true)
786
- compress_threshold = options.fetch(:compress_threshold, DEFAULT_COMPRESS_LIMIT)
787
- serialized_value_size = (@value.is_a?(String) ? @value : marshaled_value).bytesize
841
+ def compress!(compress_threshold)
842
+ case @value
843
+ when nil, true, false, Numeric
844
+ uncompressed_size = 0
845
+ when String
846
+ uncompressed_size = @value.bytesize
847
+ else
848
+ serialized = Marshal.dump(@value)
849
+ uncompressed_size = serialized.bytesize
850
+ end
788
851
 
789
- serialized_value_size >= compress_threshold
852
+ if uncompressed_size >= compress_threshold
853
+ serialized ||= Marshal.dump(@value)
854
+ compressed = Zlib::Deflate.deflate(serialized)
855
+
856
+ if compressed.bytesize < uncompressed_size
857
+ @value = compressed
858
+ @compressed = true
859
+ end
790
860
  end
791
861
  end
792
862
 
793
863
  def compressed?
794
- defined?(@compressed) ? @compressed : false
795
- end
796
-
797
- def compress!
798
- @value = Zlib::Deflate.deflate(marshaled_value)
799
- @compressed = true
864
+ defined?(@compressed)
800
865
  end
801
866
 
802
867
  def uncompress(value)
803
868
  Marshal.load(Zlib::Inflate.inflate(value))
804
869
  end
805
-
806
- def marshaled_value
807
- @marshaled_value ||= Marshal.dump(@value)
808
- end
809
870
  end
810
871
  end
811
872
  end