activesupport 5.0.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 (268) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +343 -590
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -4
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +11 -5
  8. data/lib/active_support/backtrace_cleaner.rb +33 -5
  9. data/lib/active_support/benchmarkable.rb +5 -3
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +45 -53
  12. data/lib/active_support/cache/mem_cache_store.rb +81 -79
  13. data/lib/active_support/cache/memory_store.rb +69 -41
  14. data/lib/active_support/cache/null_store.rb +11 -4
  15. data/lib/active_support/cache/redis_cache_store.rb +493 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +74 -37
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  18. data/lib/active_support/cache.rb +332 -161
  19. data/lib/active_support/callbacks.rb +657 -586
  20. data/lib/active_support/concern.rb +79 -6
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  22. data/lib/active_support/concurrency/share_lock.rb +59 -19
  23. data/lib/active_support/configurable.rb +15 -17
  24. data/lib/active_support/configuration_file.rb +46 -0
  25. data/lib/active_support/core_ext/array/access.rb +21 -7
  26. data/lib/active_support/core_ext/array/conversions.rb +20 -18
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +3 -1
  30. data/lib/active_support/core_ext/array/inquiry.rb +3 -1
  31. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  32. data/lib/active_support/core_ext/array.rb +9 -7
  33. data/lib/active_support/core_ext/benchmark.rb +5 -3
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +6 -6
  35. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  36. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  38. data/lib/active_support/core_ext/class/subclasses.rb +18 -26
  39. data/lib/active_support/core_ext/class.rb +4 -2
  40. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  41. data/lib/active_support/core_ext/date/blank.rb +3 -1
  42. data/lib/active_support/core_ext/date/calculations.rb +16 -13
  43. data/lib/active_support/core_ext/date/conversions.rb +23 -21
  44. data/lib/active_support/core_ext/date/zones.rb +4 -2
  45. data/lib/active_support/core_ext/date.rb +7 -5
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +82 -53
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -5
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +9 -9
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  50. data/lib/active_support/core_ext/date_time/blank.rb +3 -1
  51. data/lib/active_support/core_ext/date_time/calculations.rb +23 -11
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +15 -2
  53. data/lib/active_support/core_ext/date_time/conversions.rb +14 -13
  54. data/lib/active_support/core_ext/date_time.rb +7 -5
  55. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  56. data/lib/active_support/core_ext/digest.rb +3 -0
  57. data/lib/active_support/core_ext/enumerable.rb +165 -29
  58. data/lib/active_support/core_ext/file/atomic.rb +7 -5
  59. data/lib/active_support/core_ext/file.rb +3 -1
  60. data/lib/active_support/core_ext/hash/conversions.rb +40 -39
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  62. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  63. data/lib/active_support/core_ext/hash/except.rb +4 -2
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -2
  65. data/lib/active_support/core_ext/hash/keys.rb +9 -36
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  68. data/lib/active_support/core_ext/hash.rb +10 -9
  69. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  70. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  71. data/lib/active_support/core_ext/integer/time.rb +11 -18
  72. data/lib/active_support/core_ext/integer.rb +5 -3
  73. data/lib/active_support/core_ext/kernel/concern.rb +3 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +3 -1
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +5 -4
  77. data/lib/active_support/core_ext/load_error.rb +2 -23
  78. data/lib/active_support/core_ext/marshal.rb +6 -2
  79. data/lib/active_support/core_ext/module/aliasing.rb +5 -48
  80. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  81. data/lib/active_support/core_ext/module/attr_internal.rb +7 -5
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +53 -59
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +31 -24
  84. data/lib/active_support/core_ext/module/concerning.rb +16 -11
  85. data/lib/active_support/core_ext/module/delegation.rb +159 -44
  86. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  87. data/lib/active_support/core_ext/module/introspection.rb +23 -26
  88. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  89. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  90. data/lib/active_support/core_ext/module.rb +13 -12
  91. data/lib/active_support/core_ext/name_error.rb +36 -2
  92. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  93. data/lib/active_support/core_ext/numeric/conversions.rb +129 -134
  94. data/lib/active_support/core_ext/numeric/time.rb +18 -26
  95. data/lib/active_support/core_ext/numeric.rb +5 -4
  96. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  97. data/lib/active_support/core_ext/object/blank.rb +14 -2
  98. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  99. data/lib/active_support/core_ext/object/deep_dup.rb +4 -2
  100. data/lib/active_support/core_ext/object/duplicable.rb +13 -62
  101. data/lib/active_support/core_ext/object/inclusion.rb +3 -1
  102. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  103. data/lib/active_support/core_ext/object/json.rb +42 -15
  104. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  105. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  106. data/lib/active_support/core_ext/object/try.rb +20 -8
  107. data/lib/active_support/core_ext/object/with_options.rb +15 -2
  108. data/lib/active_support/core_ext/object.rb +14 -12
  109. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  110. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  111. data/lib/active_support/core_ext/range/each.rb +5 -2
  112. data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
  113. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  114. data/lib/active_support/core_ext/range.rb +7 -4
  115. data/lib/active_support/core_ext/regexp.rb +10 -1
  116. data/lib/active_support/core_ext/securerandom.rb +28 -6
  117. data/lib/active_support/core_ext/string/access.rb +9 -18
  118. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  119. data/lib/active_support/core_ext/string/conversions.rb +5 -2
  120. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  121. data/lib/active_support/core_ext/string/filters.rb +47 -4
  122. data/lib/active_support/core_ext/string/indent.rb +6 -4
  123. data/lib/active_support/core_ext/string/inflections.rb +78 -29
  124. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  125. data/lib/active_support/core_ext/string/multibyte.rb +10 -5
  126. data/lib/active_support/core_ext/string/output_safety.rb +86 -31
  127. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  128. data/lib/active_support/core_ext/string/strip.rb +5 -1
  129. data/lib/active_support/core_ext/string/zones.rb +4 -2
  130. data/lib/active_support/core_ext/string.rb +15 -13
  131. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  132. data/lib/active_support/core_ext/symbol.rb +3 -0
  133. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  134. data/lib/active_support/core_ext/time/calculations.rb +117 -45
  135. data/lib/active_support/core_ext/time/compatibility.rb +13 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +18 -12
  137. data/lib/active_support/core_ext/time/zones.rb +9 -7
  138. data/lib/active_support/core_ext/time.rb +7 -5
  139. data/lib/active_support/core_ext/uri.rb +12 -7
  140. data/lib/active_support/core_ext.rb +3 -2
  141. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  142. data/lib/active_support/current_attributes.rb +208 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +7 -1
  145. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  146. data/lib/active_support/dependencies.rb +172 -98
  147. data/lib/active_support/deprecation/behaviors.rb +45 -13
  148. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  149. data/lib/active_support/deprecation/disallowed.rb +56 -0
  150. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  151. data/lib/active_support/deprecation/method_wrappers.rb +32 -17
  152. data/lib/active_support/deprecation/proxy_wrappers.rb +35 -7
  153. data/lib/active_support/deprecation/reporting.rb +61 -16
  154. data/lib/active_support/deprecation.rb +17 -9
  155. data/lib/active_support/descendants_tracker.rb +61 -9
  156. data/lib/active_support/digest.rb +20 -0
  157. data/lib/active_support/duration/iso8601_parser.rb +67 -66
  158. data/lib/active_support/duration/iso8601_serializer.rb +25 -17
  159. data/lib/active_support/duration.rb +349 -46
  160. data/lib/active_support/encrypted_configuration.rb +45 -0
  161. data/lib/active_support/encrypted_file.rb +117 -0
  162. data/lib/active_support/environment_inquirer.rb +20 -0
  163. data/lib/active_support/evented_file_update_checker.rb +88 -112
  164. data/lib/active_support/execution_wrapper.rb +25 -13
  165. data/lib/active_support/executor.rb +3 -1
  166. data/lib/active_support/file_update_checker.rb +56 -51
  167. data/lib/active_support/fork_tracker.rb +62 -0
  168. data/lib/active_support/gem_version.rb +4 -2
  169. data/lib/active_support/gzip.rb +7 -5
  170. data/lib/active_support/hash_with_indifferent_access.rb +153 -49
  171. data/lib/active_support/i18n.rb +9 -6
  172. data/lib/active_support/i18n_railtie.rb +30 -20
  173. data/lib/active_support/inflections.rb +13 -11
  174. data/lib/active_support/inflector/inflections.rb +28 -15
  175. data/lib/active_support/inflector/methods.rb +120 -109
  176. data/lib/active_support/inflector/transliterate.rb +60 -25
  177. data/lib/active_support/inflector.rb +7 -5
  178. data/lib/active_support/json/decoding.rb +30 -29
  179. data/lib/active_support/json/encoding.rb +22 -11
  180. data/lib/active_support/json.rb +4 -2
  181. data/lib/active_support/key_generator.rb +6 -36
  182. data/lib/active_support/lazy_load_hooks.rb +53 -20
  183. data/lib/active_support/locale/en.rb +33 -0
  184. data/lib/active_support/locale/en.yml +7 -3
  185. data/lib/active_support/log_subscriber/test_helper.rb +11 -9
  186. data/lib/active_support/log_subscriber.rb +51 -18
  187. data/lib/active_support/logger.rb +9 -22
  188. data/lib/active_support/logger_silence.rb +14 -21
  189. data/lib/active_support/logger_thread_safe_level.rb +55 -8
  190. data/lib/active_support/message_encryptor.rb +170 -53
  191. data/lib/active_support/message_verifier.rb +91 -20
  192. data/lib/active_support/messages/metadata.rb +80 -0
  193. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  194. data/lib/active_support/messages/rotator.rb +57 -0
  195. data/lib/active_support/multibyte/chars.rb +24 -78
  196. data/lib/active_support/multibyte/unicode.rb +21 -352
  197. data/lib/active_support/multibyte.rb +4 -2
  198. data/lib/active_support/notifications/fanout.rb +121 -19
  199. data/lib/active_support/notifications/instrumenter.rb +78 -14
  200. data/lib/active_support/notifications.rb +80 -12
  201. data/lib/active_support/number_helper/number_converter.rb +17 -16
  202. data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
  203. data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -3
  204. data/lib/active_support/number_helper/number_to_human_converter.rb +13 -12
  205. data/lib/active_support/number_helper/number_to_human_size_converter.rb +11 -13
  206. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  207. data/lib/active_support/number_helper/number_to_phone_converter.rb +5 -4
  208. data/lib/active_support/number_helper/number_to_rounded_converter.rb +18 -55
  209. data/lib/active_support/number_helper/rounding_helper.rb +50 -0
  210. data/lib/active_support/number_helper.rb +45 -16
  211. data/lib/active_support/option_merger.rb +25 -4
  212. data/lib/active_support/ordered_hash.rb +6 -4
  213. data/lib/active_support/ordered_options.rb +23 -9
  214. data/lib/active_support/parameter_filter.rb +133 -0
  215. data/lib/active_support/per_thread_registry.rb +7 -5
  216. data/lib/active_support/proxy_object.rb +2 -0
  217. data/lib/active_support/rails.rb +8 -9
  218. data/lib/active_support/railtie.rb +62 -11
  219. data/lib/active_support/reloader.rb +12 -11
  220. data/lib/active_support/rescuable.rb +20 -11
  221. data/lib/active_support/secure_compare_rotator.rb +51 -0
  222. data/lib/active_support/security_utils.rb +26 -15
  223. data/lib/active_support/string_inquirer.rb +12 -3
  224. data/lib/active_support/subscriber.rb +77 -23
  225. data/lib/active_support/tagged_logging.rb +52 -17
  226. data/lib/active_support/test_case.rb +106 -29
  227. data/lib/active_support/testing/assertions.rb +144 -8
  228. data/lib/active_support/testing/autorun.rb +5 -10
  229. data/lib/active_support/testing/constant_lookup.rb +2 -1
  230. data/lib/active_support/testing/declarative.rb +3 -1
  231. data/lib/active_support/testing/deprecation.rb +4 -2
  232. data/lib/active_support/testing/file_fixtures.rb +4 -0
  233. data/lib/active_support/testing/isolation.rb +19 -24
  234. data/lib/active_support/testing/method_call_assertions.rb +31 -2
  235. data/lib/active_support/testing/parallelization/server.rb +78 -0
  236. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  237. data/lib/active_support/testing/parallelization.rb +51 -0
  238. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  239. data/lib/active_support/testing/stream.rb +30 -29
  240. data/lib/active_support/testing/tagged_logging.rb +3 -1
  241. data/lib/active_support/testing/time_helpers.rb +125 -24
  242. data/lib/active_support/time.rb +14 -12
  243. data/lib/active_support/time_with_zone.rb +142 -55
  244. data/lib/active_support/values/time_zone.rb +160 -53
  245. data/lib/active_support/version.rb +3 -1
  246. data/lib/active_support/xml_mini/jdom.rb +115 -114
  247. data/lib/active_support/xml_mini/libxml.rb +15 -14
  248. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  249. data/lib/active_support/xml_mini/nokogiri.rb +13 -13
  250. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  251. data/lib/active_support/xml_mini/rexml.rb +18 -9
  252. data/lib/active_support/xml_mini.rb +44 -42
  253. data/lib/active_support.rb +19 -10
  254. metadata +79 -37
  255. data/lib/active_support/concurrency/latch.rb +0 -19
  256. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  257. data/lib/active_support/core_ext/hash/compact.rb +0 -20
  258. data/lib/active_support/core_ext/hash/transform_values.rb +0 -29
  259. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  260. data/lib/active_support/core_ext/kernel/debugger.rb +0 -3
  261. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -3
  262. data/lib/active_support/core_ext/module/qualified_const.rb +0 -70
  263. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  264. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  265. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  266. data/lib/active_support/core_ext/struct.rb +0 -3
  267. data/lib/active_support/core_ext/time/marshal.rb +0 -3
  268. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,29 +1,31 @@
1
- require 'benchmark'
2
- require 'zlib'
3
- require 'active_support/core_ext/array/extract_options'
4
- require 'active_support/core_ext/array/wrap'
5
- require 'active_support/core_ext/benchmark'
6
- require 'active_support/core_ext/module/attribute_accessors'
7
- require 'active_support/core_ext/numeric/bytes'
8
- require 'active_support/core_ext/numeric/time'
9
- require 'active_support/core_ext/object/to_param'
10
- require 'active_support/core_ext/string/inflections'
11
- require 'active_support/core_ext/string/strip'
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+ require "active_support/core_ext/array/extract_options"
5
+ require "active_support/core_ext/array/wrap"
6
+ require "active_support/core_ext/enumerable"
7
+ require "active_support/core_ext/module/attribute_accessors"
8
+ require "active_support/core_ext/numeric/bytes"
9
+ require "active_support/core_ext/numeric/time"
10
+ require "active_support/core_ext/object/to_param"
11
+ require "active_support/core_ext/object/try"
12
+ require "active_support/core_ext/string/inflections"
12
13
 
13
14
  module ActiveSupport
14
15
  # See ActiveSupport::Cache::Store for documentation.
15
16
  module Cache
16
- autoload :FileStore, 'active_support/cache/file_store'
17
- autoload :MemoryStore, 'active_support/cache/memory_store'
18
- autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
19
- autoload :NullStore, 'active_support/cache/null_store'
17
+ autoload :FileStore, "active_support/cache/file_store"
18
+ autoload :MemoryStore, "active_support/cache/memory_store"
19
+ autoload :MemCacheStore, "active_support/cache/mem_cache_store"
20
+ autoload :NullStore, "active_support/cache/null_store"
21
+ autoload :RedisCacheStore, "active_support/cache/redis_cache_store"
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
- autoload :LocalCache, 'active_support/cache/strategy/local_cache'
28
+ autoload :LocalCache, "active_support/cache/strategy/local_cache"
27
29
  end
28
30
 
29
31
  class << self
@@ -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
@@ -73,12 +76,12 @@ module ActiveSupport
73
76
  # each of elements in the array will be turned into parameters/keys and
74
77
  # concatenated into a single key. For example:
75
78
  #
76
- # expand_cache_key([:foo, :bar]) # => "foo/bar"
77
- # expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
79
+ # ActiveSupport::Cache.expand_cache_key([:foo, :bar]) # => "foo/bar"
80
+ # ActiveSupport::Cache.expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
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}/" : ""
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}/"
@@ -91,16 +94,19 @@ module ActiveSupport
91
94
  private
92
95
  def retrieve_cache_key(key)
93
96
  case
94
- when key.respond_to?(:cache_key) then key.cache_key
95
- when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
96
- when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
97
- else key.to_param
97
+ when key.respond_to?(:cache_key_with_version) then key.cache_key_with_version
98
+ when key.respond_to?(:cache_key) then key.cache_key
99
+ when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
100
+ when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
101
+ else key.to_param
98
102
  end.to_s
99
103
  end
100
104
 
101
105
  # Obtains the specified cache store class, given the name of the +store+.
102
106
  # Raises an error when the store class cannot be found.
103
107
  def retrieve_store_class(store)
108
+ # require_relative cannot be used here because the class might be
109
+ # provided by another gem, like redis-activesupport for example.
104
110
  require "active_support/cache/#{store}"
105
111
  rescue LoadError => e
106
112
  raise "Could not find cache store adapter for #{store} (#{e})"
@@ -146,23 +152,42 @@ module ActiveSupport
146
152
  # cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
147
153
  # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
148
154
  #
149
- # Caches can also store values in a compressed format to save space and
150
- # reduce time spent sending data. Since there is overhead, values must be
151
- # large enough to warrant compression. To turn on compression either pass
152
- # <tt>compress: true</tt> in the initializer or as an option to +fetch+
153
- # or +write+. To specify the threshold at which to compress values, set the
154
- # <tt>:compress_threshold</tt> option. The default threshold is 16K.
155
+ # Cached data larger than 1kB are compressed by default. To turn off
156
+ # compression, pass <tt>compress: false</tt> to the initializer or to
157
+ # individual +fetch+ or +write+ method calls. The 1kB compression
158
+ # threshold is configurable with the <tt>:compress_threshold</tt> option,
159
+ # specified in bytes.
155
160
  class Store
156
- cattr_accessor :logger, :instance_writer => true
161
+ DEFAULT_CODER = Marshal
162
+
163
+ cattr_accessor :logger, instance_writer: true
157
164
 
158
165
  attr_reader :silence, :options
159
166
  alias :silence? :silence
160
167
 
168
+ class << self
169
+ private
170
+ def retrieve_pool_options(options)
171
+ {}.tap do |pool_options|
172
+ pool_options[:size] = options.delete(:pool_size) if options[:pool_size]
173
+ pool_options[:timeout] = options.delete(:pool_timeout) if options[:pool_timeout]
174
+ end
175
+ end
176
+
177
+ def ensure_connection_pool_added!
178
+ require "connection_pool"
179
+ rescue LoadError => e
180
+ $stderr.puts "You don't have connection_pool installed in your application. Please add it to your Gemfile and run bundle install"
181
+ raise e
182
+ end
183
+ end
184
+
161
185
  # Creates a new cache. The options will be passed to any write method calls
162
186
  # except for <tt>:namespace</tt> which can be used to set the global
163
187
  # namespace for the cache.
164
188
  def initialize(options = nil)
165
189
  @options = options ? options.dup : {}
190
+ @coder = @options.delete(:coder) { self.class::DEFAULT_CODER } || NullCoder
166
191
  end
167
192
 
168
193
  # Silences the logger.
@@ -200,18 +225,25 @@ module ActiveSupport
200
225
  # You may also specify additional options via the +options+ argument.
201
226
  # Setting <tt>force: true</tt> forces a cache "miss," meaning we treat
202
227
  # the cache value as missing even if it's present. Passing a block is
203
- # required when `force` is true so this always results in a cache write.
228
+ # required when +force+ is true so this always results in a cache write.
204
229
  #
205
230
  # cache.write('today', 'Monday')
206
231
  # cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday'
207
232
  # cache.fetch('today', force: true) # => ArgumentError
208
233
  #
209
- # The `:force` option is useful when you're calling some other method to
234
+ # The +:force+ option is useful when you're calling some other method to
210
235
  # ask whether you should force a cache write. Otherwise, it's clearer to
211
- # just call `Cache#write`.
236
+ # just call <tt>Cache#write</tt>.
212
237
  #
213
- # Setting <tt>:compress</tt> will store a large cache entry set by the call
214
- # in a compressed format.
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
+ #
246
+ # Setting <tt>compress: false</tt> disables compression of the cache entry.
215
247
  #
216
248
  # Setting <tt>:expires_in</tt> will set an expiration time on the cache.
217
249
  # All caches support auto-expiring content after a specified number of
@@ -222,6 +254,10 @@ module ActiveSupport
222
254
  # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
223
255
  # cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
224
256
  #
257
+ # Setting <tt>:version</tt> verifies the cache stored under <tt>name</tt>
258
+ # is of the same version. nil is returned on mismatches despite contents.
259
+ # This feature is used to support recyclable cache keys.
260
+ #
225
261
  # Setting <tt>:race_condition_ttl</tt> is very useful in situations where
226
262
  # a cache entry is used very frequently and is under heavy load. If a
227
263
  # cache expires and due to heavy load several different processes will try
@@ -250,14 +286,14 @@ module ActiveSupport
250
286
  # sleep 60
251
287
  #
252
288
  # Thread.new do
253
- # val_1 = cache.fetch('foo', race_condition_ttl: 10) do
289
+ # val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
254
290
  # sleep 1
255
291
  # 'new value 1'
256
292
  # end
257
293
  # end
258
294
  #
259
295
  # Thread.new do
260
- # val_2 = cache.fetch('foo', race_condition_ttl: 10) do
296
+ # val_2 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
261
297
  # 'new value 2'
262
298
  # end
263
299
  # end
@@ -281,15 +317,16 @@ module ActiveSupport
281
317
  # :bar
282
318
  # end
283
319
  # cache.fetch('foo') # => "bar"
284
- def fetch(name, options = nil)
320
+ def fetch(name, options = nil, &block)
285
321
  if block_given?
286
322
  options = merged_options(options)
287
323
  key = normalize_key(name, options)
288
324
 
289
325
  entry = nil
290
326
  instrument(:read, name, options) do |payload|
291
- cached_entry = read_entry(key, options) unless options[:force]
327
+ cached_entry = read_entry(key, **options, event: payload) unless options[:force]
292
328
  entry = handle_expired_entry(cached_entry, key, options)
329
+ entry = nil if entry && entry.mismatched?(normalize_version(name, options))
293
330
  payload[:super_operation] = :fetch if payload
294
331
  payload[:hit] = !!entry if payload
295
332
  end
@@ -297,28 +334,38 @@ module ActiveSupport
297
334
  if entry
298
335
  get_entry_value(entry, name, options)
299
336
  else
300
- save_block_result_to_cache(name, options) { |_name| yield _name }
337
+ save_block_result_to_cache(name, options, &block)
301
338
  end
302
339
  elsif options && options[:force]
303
- raise ArgumentError, 'Missing block: Calling `Cache#fetch` with `force: true` requires a block.'
340
+ raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
304
341
  else
305
342
  read(name, options)
306
343
  end
307
344
  end
308
345
 
309
- # Fetches data from the cache, using the given key. If there is data in
346
+ # Reads data from the cache, using the given key. If there is data in
310
347
  # the cache with the given key, then that data is returned. Otherwise,
311
348
  # +nil+ is returned.
312
349
  #
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.
353
+ #
313
354
  # Options are passed to the underlying cache implementation.
314
355
  def read(name, options = nil)
315
356
  options = merged_options(options)
316
- key = normalize_key(name, options)
357
+ key = normalize_key(name, options)
358
+ version = normalize_version(name, options)
359
+
317
360
  instrument(:read, name, options) do |payload|
318
- entry = read_entry(key, options)
361
+ entry = read_entry(key, **options, event: payload)
362
+
319
363
  if entry
320
364
  if entry.expired?
321
- delete_entry(key, options)
365
+ delete_entry(key, **options)
366
+ payload[:hit] = false if payload
367
+ nil
368
+ elsif entry.mismatched?(version)
322
369
  payload[:hit] = false if payload
323
370
  nil
324
371
  else
@@ -342,27 +389,33 @@ module ActiveSupport
342
389
  options = names.extract_options!
343
390
  options = merged_options(options)
344
391
 
345
- results = {}
346
- names.each do |name|
347
- key = normalize_key(name, options)
348
- entry = read_entry(key, options)
349
- if entry
350
- if entry.expired?
351
- delete_entry(key, options)
352
- else
353
- results[name] = entry.value
354
- end
392
+ instrument :read_multi, names, options do |payload|
393
+ read_multi_entries(names, **options, event: payload).tap do |results|
394
+ payload[:hits] = results.keys
395
+ end
396
+ end
397
+ end
398
+
399
+ # Cache Storage API to write multiple values at once.
400
+ def write_multi(hash, options = nil)
401
+ options = merged_options(options)
402
+
403
+ instrument :write_multi, hash, options do |payload|
404
+ entries = hash.each_with_object({}) do |(name, value), memo|
405
+ memo[normalize_key(name, options)] = Entry.new(value, **options.merge(version: normalize_version(name, options)))
355
406
  end
407
+
408
+ write_multi_entries entries, **options
356
409
  end
357
- results
358
410
  end
359
411
 
360
412
  # Fetches data from the cache, using the given keys. If there is data in
361
413
  # the cache with the given keys, then that data is returned. Otherwise,
362
414
  # the supplied block is called for each key for which there was no data,
363
415
  # and the result will be written to the cache and returned.
364
- #
365
- # Options are passed to the underlying cache implementation.
416
+ # Therefore, you need to pass a block that returns the data to be written
417
+ # to the cache. If you do not want to write the cache when the cache is
418
+ # not found, use #read_multi.
366
419
  #
367
420
  # Returns a hash with the data for each of the names. For example:
368
421
  #
@@ -373,17 +426,36 @@ module ActiveSupport
373
426
  # # => { "bim" => "bam",
374
427
  # # "unknown_key" => "Fallback value for key: unknown_key" }
375
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
376
440
  def fetch_multi(*names)
441
+ raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
442
+
377
443
  options = names.extract_options!
378
444
  options = merged_options(options)
379
- results = read_multi(*names, options)
380
445
 
381
- names.each_with_object({}) do |name, memo|
382
- memo[name] = results.fetch(name) do
383
- value = yield name
384
- write(name, value, options)
385
- value
446
+ instrument :read_multi, names, options do |payload|
447
+ reads = read_multi_entries(names, **options)
448
+ writes = {}
449
+ ordered = names.index_with do |name|
450
+ reads.fetch(name) { writes[name] = yield(name) }
386
451
  end
452
+
453
+ payload[:hits] = reads.keys
454
+ payload[:super_operation] = :fetch_multi
455
+
456
+ write_multi(writes, options)
457
+
458
+ ordered
387
459
  end
388
460
  end
389
461
 
@@ -394,8 +466,8 @@ module ActiveSupport
394
466
  options = merged_options(options)
395
467
 
396
468
  instrument(:write, name, options) do
397
- entry = Entry.new(value, options)
398
- 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)
399
471
  end
400
472
  end
401
473
 
@@ -406,7 +478,19 @@ module ActiveSupport
406
478
  options = merged_options(options)
407
479
 
408
480
  instrument(:delete, name) do
409
- 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)
410
494
  end
411
495
  end
412
496
 
@@ -416,9 +500,9 @@ module ActiveSupport
416
500
  def exist?(name, options = nil)
417
501
  options = merged_options(options)
418
502
 
419
- instrument(:exist?, name) do
420
- entry = read_entry(normalize_key(name, options), options)
421
- (entry && !entry.expired?) || false
503
+ instrument(:exist?, name) do |payload|
504
+ entry = read_entry(normalize_key(name, options), **options, event: payload)
505
+ (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
422
506
  end
423
507
  end
424
508
 
@@ -426,7 +510,7 @@ module ActiveSupport
426
510
  #
427
511
  # Options are passed to the underlying cache implementation.
428
512
  #
429
- # All implementations may not support this method.
513
+ # Some implementations may not support this method.
430
514
  def delete_matched(matcher, options = nil)
431
515
  raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
432
516
  end
@@ -435,7 +519,7 @@ module ActiveSupport
435
519
  #
436
520
  # Options are passed to the underlying cache implementation.
437
521
  #
438
- # All implementations may not support this method.
522
+ # Some implementations may not support this method.
439
523
  def increment(name, amount = 1, options = nil)
440
524
  raise NotImplementedError.new("#{self.class.name} does not support increment")
441
525
  end
@@ -444,7 +528,7 @@ module ActiveSupport
444
528
  #
445
529
  # Options are passed to the underlying cache implementation.
446
530
  #
447
- # All implementations may not support this method.
531
+ # Some implementations may not support this method.
448
532
  def decrement(name, amount = 1, options = nil)
449
533
  raise NotImplementedError.new("#{self.class.name} does not support decrement")
450
534
  end
@@ -453,7 +537,7 @@ module ActiveSupport
453
537
  #
454
538
  # Options are passed to the underlying cache implementation.
455
539
  #
456
- # All implementations may not support this method.
540
+ # Some implementations may not support this method.
457
541
  def cleanup(options = nil)
458
542
  raise NotImplementedError.new("#{self.class.name} does not support cleanup")
459
543
  end
@@ -463,21 +547,21 @@ module ActiveSupport
463
547
  #
464
548
  # The options hash is passed to the underlying cache implementation.
465
549
  #
466
- # All implementations may not support this method.
550
+ # Some implementations may not support this method.
467
551
  def clear(options = nil)
468
552
  raise NotImplementedError.new("#{self.class.name} does not support clear")
469
553
  end
470
554
 
471
- protected
555
+ private
472
556
  # Adds the namespace defined in the options to a pattern designed to
473
557
  # match keys. Implementations that support delete_matched should call
474
558
  # this method to translate a pattern that matches names into one that
475
559
  # matches namespaced keys.
476
- def key_matcher(pattern, options)
560
+ def key_matcher(pattern, options) # :doc:
477
561
  prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
478
562
  if prefix
479
563
  source = pattern.source
480
- if source.start_with?('^')
564
+ if source.start_with?("^")
481
565
  source = source[1, source.length]
482
566
  else
483
567
  source = ".*#{source[0, source.length]}"
@@ -490,81 +574,150 @@ module ActiveSupport
490
574
 
491
575
  # Reads an entry from the cache implementation. Subclasses must implement
492
576
  # this method.
493
- def read_entry(key, options) # :nodoc:
577
+ def read_entry(key, **options)
494
578
  raise NotImplementedError.new
495
579
  end
496
580
 
497
581
  # Writes an entry to the cache implementation. Subclasses must implement
498
582
  # this method.
499
- def write_entry(key, entry, options) # :nodoc:
583
+ def write_entry(key, entry, **options)
500
584
  raise NotImplementedError.new
501
585
  end
502
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
+
595
+ # Reads multiple entries from the cache implementation. Subclasses MAY
596
+ # implement this method.
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
+
604
+ version = normalize_version(name, options)
605
+
606
+ if entry.expired?
607
+ delete_entry(key, **options)
608
+ elsif !entry.mismatched?(version)
609
+ results[name] = entry.value
610
+ end
611
+ end
612
+ end
613
+
614
+ # Writes multiple entries to the cache implementation. Subclasses MAY
615
+ # implement this method.
616
+ def write_multi_entries(hash, **options)
617
+ hash.each do |key, entry|
618
+ write_entry key, entry, **options
619
+ end
620
+ end
621
+
503
622
  # Deletes an entry from the cache implementation. Subclasses must
504
623
  # implement this method.
505
- def delete_entry(key, options) # :nodoc:
624
+ def delete_entry(key, **options)
506
625
  raise NotImplementedError.new
507
626
  end
508
627
 
509
- private
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
+
510
634
  # Merges the default options with ones specific to a method call.
511
- def merged_options(call_options) # :nodoc:
635
+ def merged_options(call_options)
512
636
  if call_options
513
- options.merge(call_options)
637
+ if options.empty?
638
+ call_options
639
+ else
640
+ options.merge(call_options)
641
+ end
514
642
  else
515
- options.dup
643
+ options
644
+ end
645
+ end
646
+
647
+ # Expands and namespaces the cache key. May be overridden by
648
+ # cache stores to do additional normalization.
649
+ def normalize_key(key, options = nil)
650
+ namespace_key expanded_key(key), options
651
+ end
652
+
653
+ # Prefix the key with a namespace string:
654
+ #
655
+ # namespace_key 'foo', namespace: 'cache'
656
+ # # => 'cache:foo'
657
+ #
658
+ # With a namespace block:
659
+ #
660
+ # namespace_key 'foo', namespace: -> { 'cache' }
661
+ # # => 'cache:foo'
662
+ def namespace_key(key, options = nil)
663
+ options = merged_options(options)
664
+ namespace = options[:namespace]
665
+
666
+ if namespace.respond_to?(:call)
667
+ namespace = namespace.call
668
+ end
669
+
670
+ if key && key.encoding != Encoding::UTF_8
671
+ key = key.dup.force_encoding(Encoding::UTF_8)
672
+ end
673
+
674
+ if namespace
675
+ "#{namespace}:#{key}"
676
+ else
677
+ key
516
678
  end
517
679
  end
518
680
 
519
681
  # Expands key to be a consistent string value. Invokes +cache_key+ if
520
682
  # object responds to +cache_key+. Otherwise, +to_param+ method will be
521
683
  # called. If the key is a Hash, then keys will be sorted alphabetically.
522
- def expanded_key(key) # :nodoc:
684
+ def expanded_key(key)
523
685
  return key.cache_key.to_s if key.respond_to?(:cache_key)
524
686
 
525
687
  case key
526
688
  when Array
527
689
  if key.size > 1
528
- key = key.collect{|element| expanded_key(element)}
690
+ key.collect { |element| expanded_key(element) }
529
691
  else
530
- key = key.first
692
+ expanded_key(key.first)
531
693
  end
532
694
  when Hash
533
- key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
534
- end
535
-
536
- key.to_param
695
+ key.collect { |k, v| "#{k}=#{v}" }.sort!
696
+ else
697
+ key
698
+ end.to_param
537
699
  end
538
700
 
539
- # Prefixes a key with the namespace. Namespace and key will be delimited
540
- # with a colon.
541
- def normalize_key(key, options)
542
- key = expanded_key(key)
543
- namespace = options[:namespace] if options
544
- prefix = namespace.is_a?(Proc) ? namespace.call : namespace
545
- key = "#{prefix}:#{key}" if prefix
546
- key
701
+ def normalize_version(key, options = nil)
702
+ (options && options[:version].try(:to_param)) || expanded_version(key)
547
703
  end
548
704
 
549
- def namespaced_key(*args)
550
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
551
- `namespaced_key` is deprecated and will be removed from Rails 5.1.
552
- Please use `normalize_key` which will return a fully resolved key.
553
- MESSAGE
554
- normalize_key(*args)
705
+ def expanded_version(key)
706
+ case
707
+ when key.respond_to?(:cache_version) then key.cache_version.to_param
708
+ when key.is_a?(Array) then key.map { |element| expanded_version(element) }.tap(&:compact!).to_param
709
+ when key.respond_to?(:to_a) then expanded_version(key.to_a)
710
+ end
555
711
  end
556
712
 
557
713
  def instrument(operation, key, options = nil)
558
- 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
559
717
 
560
- payload = { :key => key }
718
+ payload = { key: key, store: self.class.name }
561
719
  payload.merge!(options) if options.is_a?(Hash)
562
- ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
563
- end
564
-
565
- def log
566
- return unless logger && logger.debug? && !silence?
567
- logger.debug(yield)
720
+ ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
568
721
  end
569
722
 
570
723
  def handle_expired_entry(entry, key, options)
@@ -574,9 +727,9 @@ module ActiveSupport
574
727
  # When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
575
728
  # for a brief period while the entry is being recalculated.
576
729
  entry.expires_at = Time.now + race_ttl
577
- write_entry(key, entry, :expires_in => race_ttl * 2)
730
+ write_entry(key, entry, expires_in: race_ttl * 2)
578
731
  else
579
- delete_entry(key, options)
732
+ delete_entry(key, **options)
580
733
  end
581
734
  entry = nil
582
735
  end
@@ -593,39 +746,54 @@ module ActiveSupport
593
746
  yield(name)
594
747
  end
595
748
 
596
- write(name, result, options)
749
+ write(name, result, options) unless result.nil? && options[:skip_nil]
597
750
  result
598
751
  end
599
752
  end
600
753
 
601
- # This class is used to represent cache entries. Cache entries have a value and an optional
602
- # expiration time. The expiration time is used to support the :race_condition_ttl option
603
- # on the cache.
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
+
766
+ # This class is used to represent cache entries. Cache entries have a value, an optional
767
+ # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
768
+ # on the cache. The version is used to support the :version option on the cache for rejecting
769
+ # mismatches.
604
770
  #
605
771
  # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
606
772
  # using short instance variable names that are lazily defined.
607
773
  class Entry # :nodoc:
608
- DEFAULT_COMPRESS_LIMIT = 16.kilobytes
774
+ attr_reader :version
609
775
 
610
- # Creates a new cache entry for the specified value. Options supported are
611
- # +:compress+, +:compress_threshold+, and +:expires_in+.
612
- def initialize(value, options = {})
613
- if should_compress?(value, options)
614
- @value = compress(value)
615
- @compressed = true
616
- else
617
- @value = value
618
- end
776
+ DEFAULT_COMPRESS_LIMIT = 1.kilobyte
619
777
 
778
+ # Creates a new cache entry for the specified value. Options supported are
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
620
783
  @created_at = Time.now.to_f
621
- @expires_in = options[:expires_in]
622
- @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
623
787
  end
624
788
 
625
789
  def value
626
790
  compressed? ? uncompress(@value) : @value
627
791
  end
628
792
 
793
+ def mismatched?(version)
794
+ @version && version && @version != version
795
+ end
796
+
629
797
  # Checks if the entry is expired. The +expires_in+ parameter can override
630
798
  # the value set when the entry was created.
631
799
  def expired?
@@ -645,19 +813,15 @@ module ActiveSupport
645
813
  end
646
814
 
647
815
  # Returns the size of the cached value. This could be less than
648
- # <tt>value.size</tt> if the data is compressed.
649
- def size
650
- if defined?(@s)
651
- @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
652
823
  else
653
- case value
654
- when NilClass
655
- 0
656
- when String
657
- @value.bytesize
658
- else
659
- @s = Marshal.dump(@value).bytesize
660
- end
824
+ @s ||= Marshal.dump(@value).bytesize
661
825
  end
662
826
  end
663
827
 
@@ -674,23 +838,30 @@ module ActiveSupport
674
838
  end
675
839
 
676
840
  private
677
- def should_compress?(value, options)
678
- if value && options[:compress]
679
- compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
680
- serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
681
-
682
- return true if serialized_value_size >= compress_threshold
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
683
850
  end
684
851
 
685
- false
686
- end
852
+ if uncompressed_size >= compress_threshold
853
+ serialized ||= Marshal.dump(@value)
854
+ compressed = Zlib::Deflate.deflate(serialized)
687
855
 
688
- def compressed?
689
- defined?(@compressed) ? @compressed : false
856
+ if compressed.bytesize < uncompressed_size
857
+ @value = compressed
858
+ @compressed = true
859
+ end
860
+ end
690
861
  end
691
862
 
692
- def compress(value)
693
- Zlib::Deflate.deflate(Marshal.dump(value))
863
+ def compressed?
864
+ defined?(@compressed)
694
865
  end
695
866
 
696
867
  def uncompress(value)