activesupport 6.0.0 → 6.1.3

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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +381 -349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_support.rb +13 -1
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +3 -4
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache.rb +101 -59
  10. data/lib/active_support/cache/file_store.rb +11 -11
  11. data/lib/active_support/cache/mem_cache_store.rb +34 -33
  12. data/lib/active_support/cache/memory_store.rb +52 -31
  13. data/lib/active_support/cache/null_store.rb +3 -3
  14. data/lib/active_support/cache/redis_cache_store.rb +38 -33
  15. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  16. data/lib/active_support/callbacks.rb +65 -59
  17. data/lib/active_support/concern.rb +46 -2
  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 +3 -3
  21. data/lib/active_support/configuration_file.rb +46 -0
  22. data/lib/active_support/core_ext.rb +1 -1
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/benchmark.rb +2 -2
  25. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  26. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  27. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  28. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  29. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  30. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  31. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  32. data/lib/active_support/core_ext/enumerable.rb +76 -4
  33. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  34. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  35. data/lib/active_support/core_ext/hash/except.rb +1 -1
  36. data/lib/active_support/core_ext/hash/keys.rb +1 -1
  37. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  38. data/lib/active_support/core_ext/load_error.rb +1 -1
  39. data/lib/active_support/core_ext/marshal.rb +2 -0
  40. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  41. data/lib/active_support/core_ext/module/attribute_accessors.rb +23 -29
  42. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +8 -4
  43. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  44. data/lib/active_support/core_ext/module/delegation.rb +46 -29
  45. data/lib/active_support/core_ext/module/introspection.rb +2 -25
  46. data/lib/active_support/core_ext/name_error.rb +29 -2
  47. data/lib/active_support/core_ext/numeric/conversions.rb +22 -18
  48. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  49. data/lib/active_support/core_ext/object/json.rb +13 -2
  50. data/lib/active_support/core_ext/object/try.rb +4 -2
  51. data/lib/active_support/core_ext/range/compare_range.rb +15 -3
  52. data/lib/active_support/core_ext/range/each.rb +0 -1
  53. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  54. data/lib/active_support/core_ext/regexp.rb +8 -1
  55. data/lib/active_support/core_ext/string/access.rb +5 -24
  56. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  57. data/lib/active_support/core_ext/string/inflections.rb +38 -4
  58. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  59. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  60. data/lib/active_support/core_ext/string/output_safety.rb +12 -11
  61. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  62. data/lib/active_support/core_ext/symbol.rb +3 -0
  63. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  64. data/lib/active_support/core_ext/time/calculations.rb +27 -3
  65. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  66. data/lib/active_support/core_ext/uri.rb +5 -1
  67. data/lib/active_support/current_attributes.rb +7 -2
  68. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  69. data/lib/active_support/dependencies.rb +42 -20
  70. data/lib/active_support/dependencies/zeitwerk_integration.rb +9 -2
  71. data/lib/active_support/deprecation.rb +6 -1
  72. data/lib/active_support/deprecation/behaviors.rb +15 -2
  73. data/lib/active_support/deprecation/disallowed.rb +56 -0
  74. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  75. data/lib/active_support/deprecation/method_wrappers.rb +13 -6
  76. data/lib/active_support/deprecation/proxy_wrappers.rb +6 -2
  77. data/lib/active_support/deprecation/reporting.rb +50 -7
  78. data/lib/active_support/descendants_tracker.rb +6 -3
  79. data/lib/active_support/duration.rb +86 -35
  80. data/lib/active_support/duration/iso8601_parser.rb +0 -1
  81. data/lib/active_support/duration/iso8601_serializer.rb +15 -10
  82. data/lib/active_support/encrypted_file.rb +20 -3
  83. data/lib/active_support/environment_inquirer.rb +20 -0
  84. data/lib/active_support/evented_file_update_checker.rb +69 -134
  85. data/lib/active_support/file_update_checker.rb +0 -1
  86. data/lib/active_support/fork_tracker.rb +62 -0
  87. data/lib/active_support/gem_version.rb +2 -2
  88. data/lib/active_support/hash_with_indifferent_access.rb +43 -24
  89. data/lib/active_support/i18n_railtie.rb +15 -16
  90. data/lib/active_support/inflector/inflections.rb +1 -3
  91. data/lib/active_support/inflector/methods.rb +36 -33
  92. data/lib/active_support/inflector/transliterate.rb +4 -4
  93. data/lib/active_support/json/decoding.rb +4 -5
  94. data/lib/active_support/json/encoding.rb +5 -1
  95. data/lib/active_support/key_generator.rb +1 -1
  96. data/lib/active_support/lazy_load_hooks.rb +0 -1
  97. data/lib/active_support/locale/en.rb +4 -2
  98. data/lib/active_support/locale/en.yml +7 -3
  99. data/lib/active_support/log_subscriber.rb +8 -1
  100. data/lib/active_support/logger.rb +2 -2
  101. data/lib/active_support/logger_silence.rb +2 -26
  102. data/lib/active_support/logger_thread_safe_level.rb +34 -12
  103. data/lib/active_support/message_encryptor.rb +5 -8
  104. data/lib/active_support/message_verifier.rb +7 -7
  105. data/lib/active_support/messages/metadata.rb +11 -2
  106. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  107. data/lib/active_support/messages/rotator.rb +10 -9
  108. data/lib/active_support/multibyte/chars.rb +5 -44
  109. data/lib/active_support/multibyte/unicode.rb +9 -84
  110. data/lib/active_support/notifications.rb +32 -5
  111. data/lib/active_support/notifications/fanout.rb +23 -8
  112. data/lib/active_support/notifications/instrumenter.rb +7 -16
  113. data/lib/active_support/number_helper.rb +33 -14
  114. data/lib/active_support/number_helper/number_converter.rb +5 -6
  115. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -7
  116. data/lib/active_support/number_helper/number_to_delimited_converter.rb +0 -1
  117. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -2
  118. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -2
  119. data/lib/active_support/number_helper/number_to_phone_converter.rb +0 -1
  120. data/lib/active_support/number_helper/number_to_rounded_converter.rb +3 -4
  121. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  122. data/lib/active_support/option_merger.rb +22 -3
  123. data/lib/active_support/ordered_hash.rb +1 -1
  124. data/lib/active_support/ordered_options.rb +13 -3
  125. data/lib/active_support/parameter_filter.rb +17 -13
  126. data/lib/active_support/per_thread_registry.rb +1 -1
  127. data/lib/active_support/rails.rb +1 -4
  128. data/lib/active_support/railtie.rb +23 -1
  129. data/lib/active_support/rescuable.rb +4 -4
  130. data/lib/active_support/secure_compare_rotator.rb +51 -0
  131. data/lib/active_support/security_utils.rb +19 -12
  132. data/lib/active_support/string_inquirer.rb +4 -3
  133. data/lib/active_support/subscriber.rb +12 -7
  134. data/lib/active_support/tagged_logging.rb +29 -4
  135. data/lib/active_support/testing/assertions.rb +18 -11
  136. data/lib/active_support/testing/parallelization.rb +12 -89
  137. data/lib/active_support/testing/parallelization/server.rb +78 -0
  138. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  139. data/lib/active_support/testing/stream.rb +0 -1
  140. data/lib/active_support/testing/time_helpers.rb +40 -5
  141. data/lib/active_support/time_with_zone.rb +67 -43
  142. data/lib/active_support/values/time_zone.rb +20 -10
  143. data/lib/active_support/xml_mini.rb +0 -1
  144. data/lib/active_support/xml_mini/jdom.rb +0 -1
  145. data/lib/active_support/xml_mini/rexml.rb +8 -1
  146. metadata +39 -38
  147. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  148. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  149. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  150. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  151. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  152. data/lib/active_support/core_ext/range/include_range.rb +0 -9
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2005-2019 David Heinemeier Hansson
1
+ Copyright (c) 2005-2020 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -15,7 +15,7 @@ The latest version of Active Support can be installed with RubyGems:
15
15
 
16
16
  Source code can be downloaded as part of the Rails project on GitHub:
17
17
 
18
- * https://github.com/rails/rails/tree/master/activesupport
18
+ * https://github.com/rails/rails/tree/main/activesupport
19
19
 
20
20
 
21
21
  == License
@@ -37,4 +37,4 @@ Bug reports for the Ruby on Rails project can be filed here:
37
37
 
38
38
  Feature requests should be discussed on the rails-core mailing list here:
39
39
 
40
- * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
40
+ * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2005-2019 David Heinemeier Hansson
4
+ # Copyright (c) 2005-2020 David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -35,6 +35,7 @@ module ActiveSupport
35
35
 
36
36
  autoload :Concern
37
37
  autoload :ActionableError
38
+ autoload :ConfigurationFile
38
39
  autoload :CurrentAttributes
39
40
  autoload :Dependencies
40
41
  autoload :DescendantsTracker
@@ -42,9 +43,11 @@ module ActiveSupport
42
43
  autoload :Executor
43
44
  autoload :FileUpdateChecker
44
45
  autoload :EventedFileUpdateChecker
46
+ autoload :ForkTracker
45
47
  autoload :LogSubscriber
46
48
  autoload :Notifications
47
49
  autoload :Reloader
50
+ autoload :SecureCompareRotator
48
51
 
49
52
  eager_autoload do
50
53
  autoload :BacktraceCleaner
@@ -67,6 +70,7 @@ module ActiveSupport
67
70
  autoload :OrderedHash
68
71
  autoload :OrderedOptions
69
72
  autoload :StringInquirer
73
+ autoload :EnvironmentInquirer
70
74
  autoload :TaggedLogging
71
75
  autoload :XmlMini
72
76
  autoload :ArrayInquirer
@@ -91,6 +95,14 @@ module ActiveSupport
91
95
  def self.to_time_preserves_timezone=(value)
92
96
  DateAndTime::Compatibility.preserve_timezone = value
93
97
  end
98
+
99
+ def self.utc_to_local_returns_utc_offset_times
100
+ DateAndTime::Compatibility.utc_to_local_returns_utc_offset_times
101
+ end
102
+
103
+ def self.utc_to_local_returns_utc_offset_times=(value)
104
+ DateAndTime::Compatibility.utc_to_local_returns_utc_offset_times = value
105
+ end
94
106
  end
95
107
 
96
108
  autoload :I18n, "active_support/i18n"
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/symbol/starts_ends_with"
4
+
3
5
  module ActiveSupport
4
6
  # Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
5
7
  # its string-like contents:
@@ -34,11 +36,11 @@ module ActiveSupport
34
36
 
35
37
  private
36
38
  def respond_to_missing?(name, include_private = false)
37
- (name[-1] == "?") || super
39
+ name.end_with?("?") || super
38
40
  end
39
41
 
40
42
  def method_missing(name, *args)
41
- if name[-1] == "?"
43
+ if name.end_with?("?")
42
44
  any?(name[0..-2])
43
45
  else
44
46
  super
@@ -16,7 +16,7 @@ module ActiveSupport
16
16
  #
17
17
  # bc = ActiveSupport::BacktraceCleaner.new
18
18
  # bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
19
- # bc.add_silencer { |line| line =~ /puma|rubygems/ } # skip any lines from puma or rubygems
19
+ # bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems
20
20
  # bc.clean(exception.backtrace) # perform the cleanup
21
21
  #
22
22
  # To reconfigure an existing BacktraceCleaner (like the default one in Rails)
@@ -65,7 +65,7 @@ module ActiveSupport
65
65
  # for a given line, it will be excluded from the clean backtrace.
66
66
  #
67
67
  # # Will reject all lines that include the word "puma", like "/gems/puma/server.rb" or "/app/my_puma_server/rb"
68
- # backtrace_cleaner.add_silencer { |line| line =~ /puma/ }
68
+ # backtrace_cleaner.add_silencer { |line| /puma/.match?(line) }
69
69
  def add_silencer(&block)
70
70
  @silencers << block
71
71
  end
@@ -85,14 +85,13 @@ module ActiveSupport
85
85
  end
86
86
 
87
87
  private
88
-
89
88
  FORMATTED_GEMS_PATTERN = /\A[^\/]+ \([\w.]+\) /
90
89
 
91
90
  def add_gem_filter
92
91
  gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
93
92
  return if gems_paths.empty?
94
93
 
95
- gems_regexp = %r{(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
94
+ gems_regexp = %r{\A(#{gems_paths.join('|')})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
96
95
  gems_result = '\3 (\4) \5'
97
96
  add_filter { |line| line.sub(gems_regexp, gems_result) }
98
97
  end
@@ -41,7 +41,7 @@ module ActiveSupport
41
41
 
42
42
  result = nil
43
43
  ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
44
- logger.send(options[:level], "%s (%.1fms)" % [ message, ms ])
44
+ logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
45
45
  result
46
46
  else
47
47
  yield
@@ -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.
@@ -311,14 +317,14 @@ module ActiveSupport
311
317
  # :bar
312
318
  # end
313
319
  # cache.fetch('foo') # => "bar"
314
- def fetch(name, options = nil)
320
+ def fetch(name, options = nil, &block)
315
321
  if block_given?
316
322
  options = merged_options(options)
317
323
  key = normalize_key(name, options)
318
324
 
319
325
  entry = nil
320
326
  instrument(:read, name, options) do |payload|
321
- cached_entry = read_entry(key, options) unless options[:force]
327
+ cached_entry = read_entry(key, **options, event: payload) unless options[:force]
322
328
  entry = handle_expired_entry(cached_entry, key, options)
323
329
  entry = nil if entry && entry.mismatched?(normalize_version(name, options))
324
330
  payload[:super_operation] = :fetch if payload
@@ -328,7 +334,7 @@ module ActiveSupport
328
334
  if entry
329
335
  get_entry_value(entry, name, options)
330
336
  else
331
- save_block_result_to_cache(name, options) { |_name| yield _name }
337
+ save_block_result_to_cache(name, options, &block)
332
338
  end
333
339
  elsif options && options[:force]
334
340
  raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
@@ -352,11 +358,11 @@ module ActiveSupport
352
358
  version = normalize_version(name, options)
353
359
 
354
360
  instrument(:read, name, options) do |payload|
355
- entry = read_entry(key, options)
361
+ entry = read_entry(key, **options, event: payload)
356
362
 
357
363
  if entry
358
364
  if entry.expired?
359
- delete_entry(key, options)
365
+ delete_entry(key, **options)
360
366
  payload[:hit] = false if payload
361
367
  nil
362
368
  elsif entry.mismatched?(version)
@@ -384,7 +390,7 @@ module ActiveSupport
384
390
  options = merged_options(options)
385
391
 
386
392
  instrument :read_multi, names, options do |payload|
387
- read_multi_entries(names, options).tap do |results|
393
+ read_multi_entries(names, **options, event: payload).tap do |results|
388
394
  payload[:hits] = results.keys
389
395
  end
390
396
  end
@@ -396,10 +402,10 @@ module ActiveSupport
396
402
 
397
403
  instrument :write_multi, hash, options do |payload|
398
404
  entries = hash.each_with_object({}) do |(name, value), memo|
399
- 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)))
400
406
  end
401
407
 
402
- write_multi_entries entries, options
408
+ write_multi_entries entries, **options
403
409
  end
404
410
  end
405
411
 
@@ -438,10 +444,10 @@ module ActiveSupport
438
444
  options = merged_options(options)
439
445
 
440
446
  instrument :read_multi, names, options do |payload|
441
- reads = read_multi_entries(names, options)
447
+ reads = read_multi_entries(names, **options)
442
448
  writes = {}
443
- ordered = names.each_with_object({}) do |name, hash|
444
- hash[name] = reads.fetch(name) { writes[name] = yield(name) }
449
+ ordered = names.index_with do |name|
450
+ reads.fetch(name) { writes[name] = yield(name) }
445
451
  end
446
452
 
447
453
  payload[:hits] = reads.keys
@@ -460,8 +466,8 @@ module ActiveSupport
460
466
  options = merged_options(options)
461
467
 
462
468
  instrument(:write, name, options) do
463
- entry = Entry.new(value, options.merge(version: normalize_version(name, options)))
464
- 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)
465
471
  end
466
472
  end
467
473
 
@@ -472,7 +478,19 @@ module ActiveSupport
472
478
  options = merged_options(options)
473
479
 
474
480
  instrument(:delete, name) do
475
- 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)
476
494
  end
477
495
  end
478
496
 
@@ -482,8 +500,8 @@ module ActiveSupport
482
500
  def exist?(name, options = nil)
483
501
  options = merged_options(options)
484
502
 
485
- instrument(:exist?, name) do
486
- 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)
487
505
  (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
488
506
  end
489
507
  end
@@ -556,52 +574,63 @@ module ActiveSupport
556
574
 
557
575
  # Reads an entry from the cache implementation. Subclasses must implement
558
576
  # this method.
559
- def read_entry(key, options)
577
+ def read_entry(key, **options)
560
578
  raise NotImplementedError.new
561
579
  end
562
580
 
563
581
  # Writes an entry to the cache implementation. Subclasses must implement
564
582
  # this method.
565
- def write_entry(key, entry, options)
583
+ def write_entry(key, entry, **options)
566
584
  raise NotImplementedError.new
567
585
  end
568
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
+
569
595
  # Reads multiple entries from the cache implementation. Subclasses MAY
570
596
  # implement this method.
571
- def read_multi_entries(names, options)
572
- results = {}
573
- names.each do |name|
574
- 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
+
575
604
  version = normalize_version(name, options)
576
- entry = read_entry(key, options)
577
-
578
- if entry
579
- if entry.expired?
580
- delete_entry(key, options)
581
- elsif entry.mismatched?(version)
582
- # Skip mismatched versions
583
- else
584
- results[name] = entry.value
585
- end
605
+
606
+ if entry.expired?
607
+ delete_entry(key, **options)
608
+ elsif !entry.mismatched?(version)
609
+ results[name] = entry.value
586
610
  end
587
611
  end
588
- results
589
612
  end
590
613
 
591
614
  # Writes multiple entries to the cache implementation. Subclasses MAY
592
615
  # implement this method.
593
- def write_multi_entries(hash, options)
616
+ def write_multi_entries(hash, **options)
594
617
  hash.each do |key, entry|
595
- write_entry key, entry, options
618
+ write_entry key, entry, **options
596
619
  end
597
620
  end
598
621
 
599
622
  # Deletes an entry from the cache implementation. Subclasses must
600
623
  # implement this method.
601
- def delete_entry(key, options)
624
+ def delete_entry(key, **options)
602
625
  raise NotImplementedError.new
603
626
  end
604
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
+
605
634
  # Merges the default options with ones specific to a method call.
606
635
  def merged_options(call_options)
607
636
  if call_options
@@ -638,6 +667,10 @@ module ActiveSupport
638
667
  namespace = namespace.call
639
668
  end
640
669
 
670
+ if key && key.encoding != Encoding::UTF_8
671
+ key = key.dup.force_encoding(Encoding::UTF_8)
672
+ end
673
+
641
674
  if namespace
642
675
  "#{namespace}:#{key}"
643
676
  else
@@ -654,15 +687,15 @@ module ActiveSupport
654
687
  case key
655
688
  when Array
656
689
  if key.size > 1
657
- key = key.collect { |element| expanded_key(element) }
690
+ key.collect { |element| expanded_key(element) }
658
691
  else
659
- key = expanded_key(key.first)
692
+ expanded_key(key.first)
660
693
  end
661
694
  when Hash
662
- key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
663
- end
664
-
665
- key.to_param
695
+ key.collect { |k, v| "#{k}=#{v}" }.sort!
696
+ else
697
+ key
698
+ end.to_param
666
699
  end
667
700
 
668
701
  def normalize_version(key, options = nil)
@@ -672,24 +705,21 @@ module ActiveSupport
672
705
  def expanded_version(key)
673
706
  case
674
707
  when key.respond_to?(:cache_version) then key.cache_version.to_param
675
- 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
676
709
  when key.respond_to?(:to_a) then expanded_version(key.to_a)
677
710
  end
678
711
  end
679
712
 
680
713
  def instrument(operation, key, options = nil)
681
- 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
682
717
 
683
- payload = { key: key }
718
+ payload = { key: key, store: self.class.name }
684
719
  payload.merge!(options) if options.is_a?(Hash)
685
720
  ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
686
721
  end
687
722
 
688
- def log
689
- return unless logger && logger.debug? && !silence?
690
- logger.debug(yield)
691
- end
692
-
693
723
  def handle_expired_entry(entry, key, options)
694
724
  if entry && entry.expired?
695
725
  race_ttl = options[:race_condition_ttl].to_i
@@ -699,7 +729,7 @@ module ActiveSupport
699
729
  entry.expires_at = Time.now + race_ttl
700
730
  write_entry(key, entry, expires_in: race_ttl * 2)
701
731
  else
702
- delete_entry(key, options)
732
+ delete_entry(key, **options)
703
733
  end
704
734
  entry = nil
705
735
  end
@@ -721,6 +751,18 @@ module ActiveSupport
721
751
  end
722
752
  end
723
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
+
724
766
  # This class is used to represent cache entries. Cache entries have a value, an optional
725
767
  # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
726
768
  # on the cache. The version is used to support the :version option on the cache for rejecting
@@ -771,8 +813,8 @@ module ActiveSupport
771
813
  end
772
814
 
773
815
  # Returns the size of the cached value. This could be less than
774
- # <tt>value.size</tt> if the data is compressed.
775
- def size
816
+ # <tt>value.bytesize</tt> if the data is compressed.
817
+ def bytesize
776
818
  case value
777
819
  when NilClass
778
820
  0