activesupport 6.1.7.2 → 7.0.7

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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +286 -491
  3. data/README.rdoc +2 -2
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +0 -2
  6. data/lib/active_support/backtrace_cleaner.rb +2 -2
  7. data/lib/active_support/benchmarkable.rb +2 -2
  8. data/lib/active_support/cache/file_store.rb +15 -9
  9. data/lib/active_support/cache/mem_cache_store.rb +148 -37
  10. data/lib/active_support/cache/memory_store.rb +24 -16
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +59 -78
  13. data/lib/active_support/cache/strategy/local_cache.rb +38 -61
  14. data/lib/active_support/cache.rb +299 -147
  15. data/lib/active_support/callbacks.rb +184 -85
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +5 -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 +8 -5
  21. data/lib/active_support/configuration_file.rb +1 -1
  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/big_decimal/conversions.rb +1 -1
  29. data/lib/active_support/core_ext/class/subclasses.rb +25 -17
  30. data/lib/active_support/core_ext/date/blank.rb +1 -1
  31. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  32. data/lib/active_support/core_ext/date/conversions.rb +14 -14
  33. data/lib/active_support/core_ext/date/deprecated_conversions.rb +37 -0
  34. data/lib/active_support/core_ext/date.rb +1 -0
  35. data/lib/active_support/core_ext/date_and_time/calculations.rb +4 -4
  36. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  37. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  38. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  39. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  40. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +33 -0
  41. data/lib/active_support/core_ext/date_time.rb +1 -0
  42. data/lib/active_support/core_ext/digest/uuid.rb +39 -14
  43. data/lib/active_support/core_ext/enumerable.rb +112 -38
  44. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  45. data/lib/active_support/core_ext/hash/conversions.rb +0 -1
  46. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  47. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  48. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  49. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  50. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  51. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  52. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
  53. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
  54. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  55. data/lib/active_support/core_ext/name_error.rb +2 -8
  56. data/lib/active_support/core_ext/numeric/conversions.rb +80 -77
  57. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  58. data/lib/active_support/core_ext/numeric.rb +1 -0
  59. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  60. data/lib/active_support/core_ext/object/blank.rb +2 -2
  61. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  62. data/lib/active_support/core_ext/object/duplicable.rb +15 -4
  63. data/lib/active_support/core_ext/object/json.rb +30 -25
  64. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  65. data/lib/active_support/core_ext/object/try.rb +20 -20
  66. data/lib/active_support/core_ext/object/with_options.rb +21 -2
  67. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  68. data/lib/active_support/core_ext/pathname.rb +3 -0
  69. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  70. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  71. data/lib/active_support/core_ext/range/deprecated_conversions.rb +33 -0
  72. data/lib/active_support/core_ext/range/each.rb +1 -1
  73. data/lib/active_support/core_ext/range/include_time_with_zone.rb +3 -26
  74. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  75. data/lib/active_support/core_ext/range.rb +1 -1
  76. data/lib/active_support/core_ext/securerandom.rb +1 -1
  77. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  78. data/lib/active_support/core_ext/string/filters.rb +1 -1
  79. data/lib/active_support/core_ext/string/inflections.rb +1 -5
  80. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  81. data/lib/active_support/core_ext/string/output_safety.rb +66 -38
  82. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  83. data/lib/active_support/core_ext/time/calculations.rb +11 -8
  84. data/lib/active_support/core_ext/time/conversions.rb +13 -12
  85. data/lib/active_support/core_ext/time/deprecated_conversions.rb +33 -0
  86. data/lib/active_support/core_ext/time/zones.rb +10 -26
  87. data/lib/active_support/core_ext/time.rb +1 -0
  88. data/lib/active_support/core_ext/uri.rb +3 -27
  89. data/lib/active_support/core_ext.rb +1 -0
  90. data/lib/active_support/current_attributes.rb +31 -15
  91. data/lib/active_support/dependencies/interlock.rb +10 -18
  92. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  93. data/lib/active_support/dependencies.rb +58 -788
  94. data/lib/active_support/deprecation/behaviors.rb +8 -5
  95. data/lib/active_support/deprecation/disallowed.rb +3 -3
  96. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  97. data/lib/active_support/deprecation/proxy_wrappers.rb +2 -2
  98. data/lib/active_support/deprecation.rb +2 -2
  99. data/lib/active_support/descendants_tracker.rb +174 -68
  100. data/lib/active_support/digest.rb +4 -4
  101. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  102. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  103. data/lib/active_support/duration.rb +77 -48
  104. data/lib/active_support/encrypted_configuration.rb +45 -3
  105. data/lib/active_support/encrypted_file.rb +13 -1
  106. data/lib/active_support/environment_inquirer.rb +1 -1
  107. data/lib/active_support/error_reporter.rb +117 -0
  108. data/lib/active_support/evented_file_update_checker.rb +20 -7
  109. data/lib/active_support/execution_context/test_helper.rb +13 -0
  110. data/lib/active_support/execution_context.rb +53 -0
  111. data/lib/active_support/execution_wrapper.rb +30 -11
  112. data/lib/active_support/executor/test_helper.rb +7 -0
  113. data/lib/active_support/fork_tracker.rb +19 -12
  114. data/lib/active_support/gem_version.rb +4 -4
  115. data/lib/active_support/hash_with_indifferent_access.rb +3 -1
  116. data/lib/active_support/html_safe_translation.rb +43 -0
  117. data/lib/active_support/i18n.rb +1 -0
  118. data/lib/active_support/i18n_railtie.rb +1 -1
  119. data/lib/active_support/inflector/inflections.rb +23 -7
  120. data/lib/active_support/inflector/methods.rb +28 -53
  121. data/lib/active_support/inflector/transliterate.rb +1 -1
  122. data/lib/active_support/isolated_execution_state.rb +72 -0
  123. data/lib/active_support/json/encoding.rb +3 -3
  124. data/lib/active_support/key_generator.rb +22 -5
  125. data/lib/active_support/lazy_load_hooks.rb +28 -4
  126. data/lib/active_support/locale/en.yml +1 -1
  127. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  128. data/lib/active_support/log_subscriber.rb +15 -5
  129. data/lib/active_support/logger_thread_safe_level.rb +4 -13
  130. data/lib/active_support/message_encryptor.rb +12 -6
  131. data/lib/active_support/message_verifier.rb +46 -14
  132. data/lib/active_support/messages/metadata.rb +2 -2
  133. data/lib/active_support/multibyte/chars.rb +10 -11
  134. data/lib/active_support/multibyte/unicode.rb +0 -12
  135. data/lib/active_support/multibyte.rb +1 -1
  136. data/lib/active_support/notifications/fanout.rb +91 -65
  137. data/lib/active_support/notifications/instrumenter.rb +32 -15
  138. data/lib/active_support/notifications.rb +23 -23
  139. data/lib/active_support/number_helper/number_converter.rb +1 -3
  140. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  141. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  142. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  143. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  144. data/lib/active_support/number_helper/rounding_helper.rb +1 -5
  145. data/lib/active_support/number_helper.rb +4 -5
  146. data/lib/active_support/option_merger.rb +10 -18
  147. data/lib/active_support/ordered_hash.rb +1 -1
  148. data/lib/active_support/ordered_options.rb +1 -1
  149. data/lib/active_support/parameter_filter.rb +20 -11
  150. data/lib/active_support/per_thread_registry.rb +5 -1
  151. data/lib/active_support/railtie.rb +69 -19
  152. data/lib/active_support/rescuable.rb +12 -12
  153. data/lib/active_support/ruby_features.rb +7 -0
  154. data/lib/active_support/secure_compare_rotator.rb +2 -2
  155. data/lib/active_support/string_inquirer.rb +0 -2
  156. data/lib/active_support/subscriber.rb +7 -18
  157. data/lib/active_support/tagged_logging.rb +1 -1
  158. data/lib/active_support/test_case.rb +13 -21
  159. data/lib/active_support/testing/assertions.rb +35 -5
  160. data/lib/active_support/testing/deprecation.rb +52 -1
  161. data/lib/active_support/testing/isolation.rb +30 -29
  162. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  163. data/lib/active_support/testing/parallelization/server.rb +4 -0
  164. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  165. data/lib/active_support/testing/parallelization.rb +4 -0
  166. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  167. data/lib/active_support/testing/stream.rb +3 -5
  168. data/lib/active_support/testing/tagged_logging.rb +1 -1
  169. data/lib/active_support/testing/time_helpers.rb +13 -2
  170. data/lib/active_support/time_with_zone.rb +69 -22
  171. data/lib/active_support/values/time_zone.rb +33 -14
  172. data/lib/active_support/version.rb +1 -1
  173. data/lib/active_support/xml_mini/jdom.rb +1 -1
  174. data/lib/active_support/xml_mini/libxml.rb +5 -5
  175. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  176. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  177. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  178. data/lib/active_support/xml_mini/rexml.rb +1 -1
  179. data/lib/active_support/xml_mini.rb +5 -4
  180. data/lib/active_support.rb +16 -0
  181. metadata +25 -23
  182. data/lib/active_support/core_ext/marshal.rb +0 -26
  183. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -120
data/README.rdoc CHANGED
@@ -13,7 +13,7 @@ The latest version of Active Support can be installed with RubyGems:
13
13
 
14
14
  $ gem install activesupport
15
15
 
16
- Source code can be downloaded as part of the Rails project on GitHub:
16
+ Source code can be downloaded as part of the \Rails project on GitHub:
17
17
 
18
18
  * https://github.com/rails/rails/tree/main/activesupport
19
19
 
@@ -31,7 +31,7 @@ API documentation is at:
31
31
 
32
32
  * https://api.rubyonrails.org
33
33
 
34
- Bug reports for the Ruby on Rails project can be filed here:
34
+ Bug reports for the Ruby on \Rails project can be filed here:
35
35
 
36
36
  * https://github.com/rails/rails/issues
37
37
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
- # Actionable errors let's you define actions to resolve an error.
4
+ # Actionable errors lets you define actions to resolve an error.
5
5
  #
6
6
  # To make an error actionable, include the <tt>ActiveSupport::ActionableError</tt>
7
7
  # module and invoke the +action+ class macro to define the action. An action
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/symbol/starts_ends_with"
4
-
5
3
  module ActiveSupport
6
4
  # Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
7
5
  # its string-like contents:
@@ -21,10 +21,10 @@ module ActiveSupport
21
21
  #
22
22
  # To reconfigure an existing BacktraceCleaner (like the default one in Rails)
23
23
  # and show as much data as possible, you can always call
24
- # <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the
24
+ # BacktraceCleaner#remove_silencers!, which will restore the
25
25
  # backtrace to a pristine state. If you need to reconfigure an existing
26
26
  # BacktraceCleaner so that it does not filter or modify the paths of any lines
27
- # of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
27
+ # of the backtrace, you can call BacktraceCleaner#remove_filters!
28
28
  # These two methods will give you a completely untouched backtrace.
29
29
  #
30
30
  # Inspired by the Quiet Backtrace gem by thoughtbot.
@@ -34,13 +34,13 @@ module ActiveSupport
34
34
  # <% benchmark 'Process data files', level: :info, silence: true do %>
35
35
  # <%= expensive_and_chatty_files_operation %>
36
36
  # <% end %>
37
- def benchmark(message = "Benchmarking", options = {})
37
+ def benchmark(message = "Benchmarking", options = {}, &block)
38
38
  if logger
39
39
  options.assert_valid_keys(:level, :silence)
40
40
  options[:level] ||= :info
41
41
 
42
42
  result = nil
43
- ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
43
+ ms = Benchmark.ms { result = options[:silence] ? logger.silence(&block) : yield }
44
44
  logger.public_send(options[:level], "%s (%.1fms)" % [ message, ms ])
45
45
  result
46
46
  else
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/marshal"
4
3
  require "active_support/core_ext/file/atomic"
5
4
  require "active_support/core_ext/string/conversions"
6
5
  require "uri/common"
@@ -12,7 +11,6 @@ module ActiveSupport
12
11
  # FileStore implements the Strategy::LocalCache strategy which implements
13
12
  # an in-memory cache inside of a block.
14
13
  class FileStore < Store
15
- prepend Strategy::LocalCache
16
14
  attr_reader :cache_path
17
15
 
18
16
  DIR_FORMATTER = "%03X"
@@ -73,19 +71,27 @@ module ActiveSupport
73
71
 
74
72
  private
75
73
  def read_entry(key, **options)
76
- if File.exist?(key)
77
- entry = File.open(key) { |f| deserialize_entry(f.read) }
74
+ if payload = read_serialized_entry(key, **options)
75
+ entry = deserialize_entry(payload)
78
76
  entry if entry.is_a?(Cache::Entry)
79
77
  end
80
- rescue => e
81
- logger.error("FileStoreError (#{e}): #{e.message}") if logger
78
+ end
79
+
80
+ def read_serialized_entry(key, **)
81
+ File.binread(key) if File.exist?(key)
82
+ rescue => error
83
+ logger.error("FileStoreError (#{error}): #{error.message}") if logger
82
84
  nil
83
85
  end
84
86
 
85
87
  def write_entry(key, entry, **options)
88
+ write_serialized_entry(key, serialize_entry(entry, **options), **options)
89
+ end
90
+
91
+ def write_serialized_entry(key, payload, **options)
86
92
  return false if options[:unless_exist] && File.exist?(key)
87
93
  ensure_cache_path(File.dirname(key))
88
- File.atomic_write(key, cache_path) { |f| f.write(serialize_entry(entry)) }
94
+ File.atomic_write(key, cache_path) { |f| f.write(payload) }
89
95
  true
90
96
  end
91
97
 
@@ -95,9 +101,9 @@ module ActiveSupport
95
101
  File.delete(key)
96
102
  delete_empty_directories(File.dirname(key))
97
103
  true
98
- rescue => e
104
+ rescue
99
105
  # Just in case the error was caused by another process deleting the file first.
100
- raise e if File.exist?(key)
106
+ raise if File.exist?(key)
101
107
  false
102
108
  end
103
109
  end
@@ -7,8 +7,8 @@ rescue LoadError => e
7
7
  raise e
8
8
  end
9
9
 
10
+ require "delegate"
10
11
  require "active_support/core_ext/enumerable"
11
- require "active_support/core_ext/marshal"
12
12
  require "active_support/core_ext/array/extract_options"
13
13
 
14
14
  module ActiveSupport
@@ -26,29 +26,52 @@ module ActiveSupport
26
26
  # MemCacheStore implements the Strategy::LocalCache strategy which implements
27
27
  # an in-memory cache inside of a block.
28
28
  class MemCacheStore < Store
29
- DEFAULT_CODER = NullCoder # Dalli automatically Marshal values
29
+ # Advertise cache versioning support.
30
+ def self.supports_cache_versioning?
31
+ true
32
+ end
33
+
34
+ prepend Strategy::LocalCache
35
+
36
+ module DupLocalCache
37
+ class DupLocalStore < DelegateClass(Strategy::LocalCache::LocalStore)
38
+ def write_entry(_key, entry)
39
+ if entry.is_a?(Entry)
40
+ entry.dup_value!
41
+ end
42
+ super
43
+ end
44
+
45
+ def fetch_entry(key)
46
+ entry = super do
47
+ new_entry = yield
48
+ if entry.is_a?(Entry)
49
+ new_entry.dup_value!
50
+ end
51
+ new_entry
52
+ end
53
+ entry = entry.dup
54
+
55
+ if entry.is_a?(Entry)
56
+ entry.dup_value!
57
+ end
58
+
59
+ entry
60
+ end
61
+ end
30
62
 
31
- # Provide support for raw values in the local cache strategy.
32
- module LocalCacheWithRaw # :nodoc:
33
63
  private
34
- def write_entry(key, entry, **options)
35
- if options[:raw] && local_cache
36
- raw_entry = Entry.new(entry.value.to_s)
37
- raw_entry.expires_at = entry.expires_at
38
- super(key, raw_entry, **options)
64
+ def local_cache
65
+ if ActiveSupport::Cache.format_version == 6.1
66
+ if local_cache = super
67
+ DupLocalStore.new(local_cache)
68
+ end
39
69
  else
40
70
  super
41
71
  end
42
72
  end
43
73
  end
44
-
45
- # Advertise cache versioning support.
46
- def self.supports_cache_versioning?
47
- true
48
- end
49
-
50
- prepend Strategy::LocalCache
51
- prepend LocalCacheWithRaw
74
+ prepend DupLocalCache
52
75
 
53
76
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
54
77
 
@@ -81,11 +104,14 @@ module ActiveSupport
81
104
  #
82
105
  # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
83
106
  #
84
- # If no addresses are provided, but ENV['MEMCACHE_SERVERS'] is defined, it will be used instead. Otherwise,
107
+ # If no addresses are provided, but <tt>ENV['MEMCACHE_SERVERS']</tt> is defined, it will be used instead. Otherwise,
85
108
  # MemCacheStore will connect to localhost:11211 (the default memcached port).
86
109
  def initialize(*addresses)
87
110
  addresses = addresses.flatten
88
111
  options = addresses.extract_options!
112
+ if options.key?(:cache_nils)
113
+ options[:skip_nil] = !options.delete(:cache_nils)
114
+ end
89
115
  super(options)
90
116
 
91
117
  unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
@@ -95,14 +121,32 @@ module ActiveSupport
95
121
  @data = addresses.first
96
122
  else
97
123
  mem_cache_options = options.dup
98
- UNIVERSAL_OPTIONS.each { |name| mem_cache_options.delete(name) }
124
+ # The value "compress: false" prevents duplicate compression within Dalli.
125
+ mem_cache_options[:compress] = false
126
+ (UNIVERSAL_OPTIONS - %i(compress)).each { |name| mem_cache_options.delete(name) }
99
127
  @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
100
128
  end
101
129
  end
102
130
 
131
+ ##
132
+ # :method: write
133
+ # :call-seq: write(name, value, options = nil)
134
+ #
135
+ # Behaves the same as ActiveSupport::Cache::Store#write, but supports
136
+ # additional options specific to memcached.
137
+ #
138
+ # ==== Additional Options
139
+ #
140
+ # * <tt>raw: true</tt> - Sends the value directly to the server as raw
141
+ # bytes. The value must be a string or number. You can use memcached
142
+ # direct operations like +increment+ and +decrement+ only on raw values.
143
+ #
144
+ # * <tt>unless_exist: true</tt> - Prevents overwriting an existing cache
145
+ # entry.
146
+
103
147
  # Increment a cached value. This method uses the memcached incr atomic
104
- # operator and can only be used on values written with the :raw option.
105
- # Calling it on a value not stored with :raw will initialize that value
148
+ # operator and can only be used on values written with the +:raw+ option.
149
+ # Calling it on a value not stored with +:raw+ will initialize that value
106
150
  # to zero.
107
151
  def increment(name, amount = 1, options = nil)
108
152
  options = merged_options(options)
@@ -114,8 +158,8 @@ module ActiveSupport
114
158
  end
115
159
 
116
160
  # Decrement a cached value. This method uses the memcached decr atomic
117
- # operator and can only be used on values written with the :raw option.
118
- # Calling it on a value not stored with :raw will initialize that value
161
+ # operator and can only be used on values written with the +:raw+ option.
162
+ # Calling it on a value not stored with +:raw+ will initialize that value
119
163
  # to zero.
120
164
  def decrement(name, amount = 1, options = nil)
121
165
  options = merged_options(options)
@@ -138,23 +182,81 @@ module ActiveSupport
138
182
  end
139
183
 
140
184
  private
185
+ module Coders # :nodoc:
186
+ class << self
187
+ def [](version)
188
+ case version
189
+ when 6.1
190
+ Rails61Coder
191
+ when 7.0
192
+ Rails70Coder
193
+ else
194
+ raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}"
195
+ end
196
+ end
197
+ end
198
+
199
+ module Loader
200
+ def load(payload)
201
+ if payload.is_a?(Entry)
202
+ payload
203
+ else
204
+ Cache::Coders::Loader.load(payload)
205
+ end
206
+ end
207
+ end
208
+
209
+ module Rails61Coder
210
+ include Loader
211
+ extend self
212
+
213
+ def dump(entry)
214
+ entry
215
+ end
216
+
217
+ def dump_compressed(entry, threshold)
218
+ entry.compressed(threshold)
219
+ end
220
+ end
221
+
222
+ module Rails70Coder
223
+ include Cache::Coders::Rails70Coder
224
+ include Loader
225
+ extend self
226
+ end
227
+ end
228
+
229
+ def default_coder
230
+ Coders[Cache.format_version]
231
+ end
232
+
141
233
  # Read an entry from the cache.
142
234
  def read_entry(key, **options)
143
- rescue_error_with(nil) { deserialize_entry(@data.with { |c| c.get(key, options) }) }
235
+ deserialize_entry(read_serialized_entry(key, **options), **options)
236
+ end
237
+
238
+ def read_serialized_entry(key, **options)
239
+ rescue_error_with(nil) do
240
+ @data.with { |c| c.get(key, options) }
241
+ end
144
242
  end
145
243
 
146
244
  # Write an entry to the cache.
147
245
  def write_entry(key, entry, **options)
246
+ write_serialized_entry(key, serialize_entry(entry, **options), **options)
247
+ end
248
+
249
+ def write_serialized_entry(key, payload, **options)
148
250
  method = options[:unless_exist] ? :add : :set
149
- value = options[:raw] ? entry.value.to_s : serialize_entry(entry)
150
251
  expires_in = options[:expires_in].to_i
151
252
  if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
152
253
  # Set the memcache expire a few minutes in the future to support race condition ttls on read
153
254
  expires_in += 5.minutes
154
255
  end
155
256
  rescue_error_with false do
156
- # The value "compress: false" prevents duplicate compression within Dalli.
157
- @data.with { |c| c.send(method, key, value, expires_in, **options, compress: false) }
257
+ # Don't pass compress option to Dalli since we are already dealing with compression.
258
+ options.delete(:compress)
259
+ @data.with { |c| c.send(method, key, payload, expires_in, **options) }
158
260
  end
159
261
  end
160
262
 
@@ -166,7 +268,7 @@ module ActiveSupport
166
268
  values = {}
167
269
 
168
270
  raw_values.each do |key, value|
169
- entry = deserialize_entry(value)
271
+ entry = deserialize_entry(value, raw: options[:raw])
170
272
 
171
273
  unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
172
274
  values[keys_to_names[key]] = entry.value
@@ -181,31 +283,40 @@ module ActiveSupport
181
283
  rescue_error_with(false) { @data.with { |c| c.delete(key) } }
182
284
  end
183
285
 
286
+ def serialize_entry(entry, raw: false, **options)
287
+ if raw
288
+ entry.value.to_s
289
+ else
290
+ super(entry, raw: raw, **options)
291
+ end
292
+ end
293
+
184
294
  # Memcache keys are binaries. So we need to force their encoding to binary
185
295
  # before applying the regular expression to ensure we are escaping all
186
296
  # characters properly.
187
297
  def normalize_key(key, options)
188
298
  key = super
189
-
190
299
  if key
191
300
  key = key.dup.force_encoding(Encoding::ASCII_8BIT)
192
301
  key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
193
- key = "#{key[0, 213]}:md5:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
302
+ key = "#{key[0, 212]}:hash:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
194
303
  end
195
-
196
304
  key
197
305
  end
198
306
 
199
- def deserialize_entry(payload)
200
- entry = super
201
- entry = Entry.new(entry, compress: false) unless entry.nil? || entry.is_a?(Entry)
202
- entry
307
+ def deserialize_entry(payload, raw: false, **)
308
+ if payload && raw
309
+ Entry.new(payload)
310
+ else
311
+ super(payload)
312
+ end
203
313
  end
204
314
 
205
315
  def rescue_error_with(fallback)
206
316
  yield
207
- rescue Dalli::DalliError => e
208
- logger.error("DalliError (#{e}): #{e.message}") if logger
317
+ rescue Dalli::DalliError => error
318
+ ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
319
+ logger.error("DalliError (#{error}): #{error.message}") if logger
209
320
  fallback
210
321
  end
211
322
  end
@@ -11,7 +11,7 @@ module ActiveSupport
11
11
  # to share cache data with each other and this may not be the most
12
12
  # appropriate cache in that scenario.
13
13
  #
14
- # This cache has a bounded size specified by the :size options to the
14
+ # This cache has a bounded size specified by the +:size+ options to the
15
15
  # initializer (default is 32Mb). When the cache exceeds the allotted size,
16
16
  # a cleanup will occur which tries to prune the cache down to three quarters
17
17
  # of the maximum size by removing the least recently used entries.
@@ -25,21 +25,25 @@ module ActiveSupport
25
25
  # MemoryStore is thread-safe.
26
26
  class MemoryStore < Store
27
27
  module DupCoder # :nodoc:
28
- class << self
29
- def load(entry)
30
- entry = entry.dup
31
- entry.dup_value!
32
- entry
33
- end
28
+ extend self
34
29
 
35
- def dump(entry)
36
- entry.dup_value!
37
- entry
38
- end
30
+ def dump(entry)
31
+ entry.dup_value! unless entry.compressed?
32
+ entry
33
+ end
34
+
35
+ def dump_compressed(entry, threshold)
36
+ entry = entry.compressed(threshold)
37
+ entry.dup_value! unless entry.compressed?
38
+ entry
39
39
  end
40
- end
41
40
 
42
- DEFAULT_CODER = DupCoder
41
+ def load(entry)
42
+ entry = entry.dup
43
+ entry.dup_value!
44
+ entry
45
+ end
46
+ end
43
47
 
44
48
  def initialize(options = nil)
45
49
  options ||= {}
@@ -85,13 +89,13 @@ module ActiveSupport
85
89
  return if pruning?
86
90
  @pruning = true
87
91
  begin
88
- start_time = Concurrent.monotonic_time
92
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
89
93
  cleanup
90
94
  instrument(:prune, target_size, from: @cache_size) do
91
95
  keys = synchronize { @data.keys }
92
96
  keys.each do |key|
93
97
  delete_entry(key, **options)
94
- return if @cache_size <= target_size || (max_time && Concurrent.monotonic_time - start_time > max_time)
98
+ return if @cache_size <= target_size || (max_time && Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time > max_time)
95
99
  end
96
100
  end
97
101
  ensure
@@ -139,6 +143,10 @@ module ActiveSupport
139
143
  private
140
144
  PER_ENTRY_OVERHEAD = 240
141
145
 
146
+ def default_coder
147
+ DupCoder
148
+ end
149
+
142
150
  def cached_size(key, payload)
143
151
  key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
144
152
  end
@@ -156,7 +164,7 @@ module ActiveSupport
156
164
  end
157
165
 
158
166
  def write_entry(key, entry, **options)
159
- payload = serialize_entry(entry)
167
+ payload = serialize_entry(entry, **options)
160
168
  synchronize do
161
169
  return false if options[:unless_exist] && @data.key?(key)
162
170
 
@@ -33,10 +33,18 @@ module ActiveSupport
33
33
  end
34
34
 
35
35
  private
36
- def read_entry(key, **options)
36
+ def read_entry(key, **s)
37
+ deserialize_entry(read_serialized_entry(key))
37
38
  end
38
39
 
39
- def write_entry(key, entry, **options)
40
+ def read_serialized_entry(_key, **)
41
+ end
42
+
43
+ def write_entry(key, entry, **)
44
+ write_serialized_entry(key, serialize_entry(entry))
45
+ end
46
+
47
+ def write_serialized_entry(_key, _payload, **)
40
48
  true
41
49
  end
42
50