activesupport 5.1.7 → 7.0.4.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +259 -585
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -5
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +2 -0
  7. data/lib/active_support/array_inquirer.rb +4 -2
  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 +2 -0
  11. data/lib/active_support/cache/file_store.rb +50 -43
  12. data/lib/active_support/cache/mem_cache_store.rb +194 -67
  13. data/lib/active_support/cache/memory_store.rb +70 -34
  14. data/lib/active_support/cache/null_store.rb +18 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +474 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +73 -50
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
  18. data/lib/active_support/cache.rb +556 -220
  19. data/lib/active_support/callbacks.rb +264 -159
  20. data/lib/active_support/code_generator.rb +65 -0
  21. data/lib/active_support/concern.rb +81 -8
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
  23. data/lib/active_support/concurrency/share_lock.rb +4 -3
  24. data/lib/active_support/configurable.rb +17 -16
  25. data/lib/active_support/configuration_file.rb +51 -0
  26. data/lib/active_support/core_ext/array/access.rb +18 -8
  27. data/lib/active_support/core_ext/array/conversions.rb +20 -17
  28. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +8 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +4 -2
  33. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  34. data/lib/active_support/core_ext/array.rb +4 -1
  35. data/lib/active_support/core_ext/benchmark.rb +4 -2
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +3 -1
  37. data/lib/active_support/core_ext/big_decimal.rb +2 -0
  38. data/lib/active_support/core_ext/class/attribute.rb +50 -47
  39. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
  40. data/lib/active_support/core_ext/class/subclasses.rb +10 -24
  41. data/lib/active_support/core_ext/class.rb +2 -0
  42. data/lib/active_support/core_ext/date/acts_like.rb +2 -0
  43. data/lib/active_support/core_ext/date/blank.rb +3 -1
  44. data/lib/active_support/core_ext/date/calculations.rb +17 -14
  45. data/lib/active_support/core_ext/date/conversions.rb +24 -22
  46. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  47. data/lib/active_support/core_ext/date/zones.rb +2 -0
  48. data/lib/active_support/core_ext/date.rb +3 -0
  49. data/lib/active_support/core_ext/date_and_time/calculations.rb +65 -41
  50. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -1
  51. data/lib/active_support/core_ext/date_and_time/zones.rb +2 -1
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +3 -1
  54. data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
  56. data/lib/active_support/core_ext/date_time/conversions.rb +15 -14
  57. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  58. data/lib/active_support/core_ext/date_time.rb +3 -0
  59. data/lib/active_support/core_ext/digest/uuid.rb +42 -14
  60. data/lib/active_support/core_ext/digest.rb +3 -0
  61. data/lib/active_support/core_ext/enumerable.rb +244 -72
  62. data/lib/active_support/core_ext/file/atomic.rb +6 -2
  63. data/lib/active_support/core_ext/file.rb +2 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +7 -6
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +4 -2
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  69. data/lib/active_support/core_ext/hash/keys.rb +4 -31
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  71. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  72. data/lib/active_support/core_ext/hash.rb +3 -2
  73. data/lib/active_support/core_ext/integer/inflections.rb +2 -0
  74. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  75. data/lib/active_support/core_ext/integer/time.rb +7 -14
  76. data/lib/active_support/core_ext/integer.rb +2 -0
  77. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  78. data/lib/active_support/core_ext/kernel/reporting.rb +6 -4
  79. data/lib/active_support/core_ext/kernel/singleton_class.rb +3 -1
  80. data/lib/active_support/core_ext/kernel.rb +2 -1
  81. data/lib/active_support/core_ext/load_error.rb +3 -8
  82. data/lib/active_support/core_ext/module/aliasing.rb +2 -0
  83. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  84. data/lib/active_support/core_ext/module/attr_internal.rb +4 -2
  85. data/lib/active_support/core_ext/module/attribute_accessors.rb +46 -56
  86. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +36 -27
  87. data/lib/active_support/core_ext/module/concerning.rb +15 -10
  88. data/lib/active_support/core_ext/module/delegation.rb +97 -58
  89. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  90. data/lib/active_support/core_ext/module/introspection.rb +18 -15
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  93. data/lib/active_support/core_ext/module.rb +3 -1
  94. data/lib/active_support/core_ext/name_error.rb +30 -2
  95. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +134 -129
  97. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +7 -15
  99. data/lib/active_support/core_ext/numeric.rb +3 -1
  100. data/lib/active_support/core_ext/object/acts_like.rb +41 -6
  101. data/lib/active_support/core_ext/object/blank.rb +15 -5
  102. data/lib/active_support/core_ext/object/conversions.rb +2 -0
  103. data/lib/active_support/core_ext/object/deep_dup.rb +3 -1
  104. data/lib/active_support/core_ext/object/duplicable.rb +16 -110
  105. data/lib/active_support/core_ext/object/inclusion.rb +2 -0
  106. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  107. data/lib/active_support/core_ext/object/json.rb +51 -26
  108. data/lib/active_support/core_ext/object/to_param.rb +2 -0
  109. data/lib/active_support/core_ext/object/to_query.rb +4 -2
  110. data/lib/active_support/core_ext/object/try.rb +26 -14
  111. data/lib/active_support/core_ext/object/with_options.rb +24 -3
  112. data/lib/active_support/core_ext/object.rb +2 -0
  113. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  114. data/lib/active_support/core_ext/pathname.rb +3 -0
  115. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  116. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  117. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  118. data/lib/active_support/core_ext/range/each.rb +6 -3
  119. data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
  120. data/lib/active_support/core_ext/range/overlaps.rb +3 -1
  121. data/lib/active_support/core_ext/range.rb +4 -1
  122. data/lib/active_support/core_ext/regexp.rb +10 -5
  123. data/lib/active_support/core_ext/securerandom.rb +25 -3
  124. data/lib/active_support/core_ext/string/access.rb +7 -16
  125. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  126. data/lib/active_support/core_ext/string/conversions.rb +5 -2
  127. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  128. data/lib/active_support/core_ext/string/filters.rb +44 -1
  129. data/lib/active_support/core_ext/string/indent.rb +2 -0
  130. data/lib/active_support/core_ext/string/inflections.rb +69 -16
  131. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  132. data/lib/active_support/core_ext/string/multibyte.rb +9 -4
  133. data/lib/active_support/core_ext/string/output_safety.rb +135 -27
  134. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  135. data/lib/active_support/core_ext/string/strip.rb +5 -1
  136. data/lib/active_support/core_ext/string/zones.rb +2 -0
  137. data/lib/active_support/core_ext/string.rb +2 -0
  138. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/symbol.rb +3 -0
  140. data/lib/active_support/core_ext/time/acts_like.rb +2 -0
  141. data/lib/active_support/core_ext/time/calculations.rb +81 -24
  142. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  143. data/lib/active_support/core_ext/time/conversions.rb +17 -12
  144. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  145. data/lib/active_support/core_ext/time/zones.rb +12 -25
  146. data/lib/active_support/core_ext/time.rb +3 -0
  147. data/lib/active_support/core_ext/uri.rb +4 -23
  148. data/lib/active_support/core_ext.rb +4 -1
  149. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  150. data/lib/active_support/current_attributes.rb +226 -0
  151. data/lib/active_support/dependencies/autoload.rb +2 -0
  152. data/lib/active_support/dependencies/interlock.rb +12 -18
  153. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  154. data/lib/active_support/dependencies.rb +59 -715
  155. data/lib/active_support/deprecation/behaviors.rb +48 -13
  156. data/lib/active_support/deprecation/constant_accessor.rb +4 -2
  157. data/lib/active_support/deprecation/disallowed.rb +56 -0
  158. data/lib/active_support/deprecation/instance_delegator.rb +2 -1
  159. data/lib/active_support/deprecation/method_wrappers.rb +29 -21
  160. data/lib/active_support/deprecation/proxy_wrappers.rb +34 -8
  161. data/lib/active_support/deprecation/reporting.rb +54 -9
  162. data/lib/active_support/deprecation.rb +10 -3
  163. data/lib/active_support/descendants_tracker.rb +192 -34
  164. data/lib/active_support/digest.rb +22 -0
  165. data/lib/active_support/duration/iso8601_parser.rb +9 -9
  166. data/lib/active_support/duration/iso8601_serializer.rb +29 -15
  167. data/lib/active_support/duration.rb +158 -72
  168. data/lib/active_support/encrypted_configuration.rb +56 -0
  169. data/lib/active_support/encrypted_file.rb +129 -0
  170. data/lib/active_support/environment_inquirer.rb +20 -0
  171. data/lib/active_support/error_reporter.rb +117 -0
  172. data/lib/active_support/evented_file_update_checker.rb +87 -122
  173. data/lib/active_support/execution_context/test_helper.rb +13 -0
  174. data/lib/active_support/execution_context.rb +53 -0
  175. data/lib/active_support/execution_wrapper.rb +46 -21
  176. data/lib/active_support/executor/test_helper.rb +7 -0
  177. data/lib/active_support/executor.rb +2 -0
  178. data/lib/active_support/file_update_checker.rb +2 -1
  179. data/lib/active_support/fork_tracker.rb +71 -0
  180. data/lib/active_support/gem_version.rb +7 -5
  181. data/lib/active_support/gzip.rb +2 -0
  182. data/lib/active_support/hash_with_indifferent_access.rb +126 -42
  183. data/lib/active_support/html_safe_translation.rb +43 -0
  184. data/lib/active_support/i18n.rb +5 -1
  185. data/lib/active_support/i18n_railtie.rb +19 -14
  186. data/lib/active_support/inflections.rb +2 -0
  187. data/lib/active_support/inflector/inflections.rb +41 -14
  188. data/lib/active_support/inflector/methods.rb +73 -87
  189. data/lib/active_support/inflector/transliterate.rb +56 -18
  190. data/lib/active_support/inflector.rb +2 -0
  191. data/lib/active_support/isolated_execution_state.rb +72 -0
  192. data/lib/active_support/json/decoding.rb +27 -26
  193. data/lib/active_support/json/encoding.rb +16 -6
  194. data/lib/active_support/json.rb +2 -0
  195. data/lib/active_support/key_generator.rb +25 -38
  196. data/lib/active_support/lazy_load_hooks.rb +35 -6
  197. data/lib/active_support/locale/en.rb +33 -0
  198. data/lib/active_support/locale/en.yml +8 -4
  199. data/lib/active_support/log_subscriber/test_helper.rb +4 -2
  200. data/lib/active_support/log_subscriber.rb +54 -13
  201. data/lib/active_support/logger.rb +4 -17
  202. data/lib/active_support/logger_silence.rb +13 -20
  203. data/lib/active_support/logger_thread_safe_level.rb +48 -10
  204. data/lib/active_support/message_encryptor.rb +111 -37
  205. data/lib/active_support/message_verifier.rb +124 -21
  206. data/lib/active_support/messages/metadata.rb +80 -0
  207. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  208. data/lib/active_support/messages/rotator.rb +57 -0
  209. data/lib/active_support/multibyte/chars.rb +19 -76
  210. data/lib/active_support/multibyte/unicode.rb +9 -331
  211. data/lib/active_support/multibyte.rb +3 -1
  212. data/lib/active_support/notifications/fanout.rb +165 -37
  213. data/lib/active_support/notifications/instrumenter.rb +92 -11
  214. data/lib/active_support/notifications.rb +96 -30
  215. data/lib/active_support/number_helper/number_converter.rb +8 -9
  216. data/lib/active_support/number_helper/number_to_currency_converter.rb +14 -12
  217. data/lib/active_support/number_helper/number_to_delimited_converter.rb +6 -3
  218. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -3
  219. data/lib/active_support/number_helper/number_to_human_size_converter.rb +7 -4
  220. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  221. data/lib/active_support/number_helper/number_to_phone_converter.rb +6 -3
  222. data/lib/active_support/number_helper/number_to_rounded_converter.rb +14 -27
  223. data/lib/active_support/number_helper/rounding_helper.rb +16 -34
  224. data/lib/active_support/number_helper.rb +38 -12
  225. data/lib/active_support/option_merger.rb +19 -6
  226. data/lib/active_support/ordered_hash.rb +4 -2
  227. data/lib/active_support/ordered_options.rb +18 -6
  228. data/lib/active_support/parameter_filter.rb +138 -0
  229. data/lib/active_support/per_thread_registry.rb +8 -1
  230. data/lib/active_support/proxy_object.rb +2 -0
  231. data/lib/active_support/rails.rb +3 -10
  232. data/lib/active_support/railtie.rb +112 -11
  233. data/lib/active_support/reloader.rb +12 -11
  234. data/lib/active_support/rescuable.rb +19 -18
  235. data/lib/active_support/ruby_features.rb +7 -0
  236. data/lib/active_support/secure_compare_rotator.rb +51 -0
  237. data/lib/active_support/security_utils.rb +26 -15
  238. data/lib/active_support/string_inquirer.rb +4 -3
  239. data/lib/active_support/subscriber.rb +81 -42
  240. data/lib/active_support/tagged_logging.rb +45 -9
  241. data/lib/active_support/test_case.rb +86 -2
  242. data/lib/active_support/testing/assertions.rb +89 -21
  243. data/lib/active_support/testing/autorun.rb +2 -0
  244. data/lib/active_support/testing/constant_lookup.rb +2 -0
  245. data/lib/active_support/testing/declarative.rb +2 -0
  246. data/lib/active_support/testing/deprecation.rb +54 -2
  247. data/lib/active_support/testing/file_fixtures.rb +4 -0
  248. data/lib/active_support/testing/isolation.rb +6 -4
  249. data/lib/active_support/testing/method_call_assertions.rb +34 -5
  250. data/lib/active_support/testing/parallelization/server.rb +82 -0
  251. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  252. data/lib/active_support/testing/parallelization.rb +55 -0
  253. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  254. data/lib/active_support/testing/setup_and_teardown.rb +12 -7
  255. data/lib/active_support/testing/stream.rb +6 -7
  256. data/lib/active_support/testing/tagged_logging.rb +3 -1
  257. data/lib/active_support/testing/time_helpers.rb +91 -15
  258. data/lib/active_support/time.rb +2 -0
  259. data/lib/active_support/time_with_zone.rb +168 -56
  260. data/lib/active_support/values/time_zone.rb +85 -37
  261. data/lib/active_support/version.rb +3 -1
  262. data/lib/active_support/xml_mini/jdom.rb +6 -5
  263. data/lib/active_support/xml_mini/libxml.rb +9 -7
  264. data/lib/active_support/xml_mini/libxmlsax.rb +7 -5
  265. data/lib/active_support/xml_mini/nokogiri.rb +8 -6
  266. data/lib/active_support/xml_mini/nokogirisax.rb +6 -4
  267. data/lib/active_support/xml_mini/rexml.rb +13 -4
  268. data/lib/active_support/xml_mini.rb +10 -15
  269. data/lib/active_support.rb +30 -9
  270. metadata +76 -35
  271. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  272. data/lib/active_support/core_ext/hash/compact.rb +0 -27
  273. data/lib/active_support/core_ext/hash/transform_values.rb +0 -30
  274. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  275. data/lib/active_support/core_ext/marshal.rb +0 -22
  276. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  277. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  278. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  279. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require "dalli"
3
5
  rescue LoadError => e
@@ -5,14 +7,14 @@ rescue LoadError => e
5
7
  raise e
6
8
  end
7
9
 
8
- require "digest/md5"
9
- require "active_support/core_ext/marshal"
10
+ require "delegate"
11
+ require "active_support/core_ext/enumerable"
10
12
  require "active_support/core_ext/array/extract_options"
11
13
 
12
14
  module ActiveSupport
13
15
  module Cache
14
16
  # A cache store implementation which stores data in Memcached:
15
- # http://memcached.org/
17
+ # https://memcached.org
16
18
  #
17
19
  # This is currently the most popular cache store for production websites.
18
20
  #
@@ -24,45 +26,76 @@ module ActiveSupport
24
26
  # MemCacheStore implements the Strategy::LocalCache strategy which implements
25
27
  # an in-memory cache inside of a block.
26
28
  class MemCacheStore < Store
27
- # Provide support for raw values in the local cache strategy.
28
- module LocalCacheWithRaw # :nodoc:
29
- private
30
- def read_entry(key, options)
31
- entry = super
32
- if options[:raw] && local_cache && entry
33
- entry = deserialize_entry(entry.value)
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!
34
57
  end
58
+
35
59
  entry
36
60
  end
61
+ end
37
62
 
38
- def write_entry(key, entry, options)
39
- if options[:raw] && local_cache
40
- raw_entry = Entry.new(entry.value.to_s)
41
- raw_entry.expires_at = entry.expires_at
42
- super(key, raw_entry, options)
63
+ private
64
+ def local_cache
65
+ if ActiveSupport::Cache.format_version == 6.1
66
+ if local_cache = super
67
+ DupLocalStore.new(local_cache)
68
+ end
43
69
  else
44
70
  super
45
71
  end
46
72
  end
47
73
  end
48
-
49
- prepend Strategy::LocalCache
50
- prepend LocalCacheWithRaw
74
+ prepend DupLocalCache
51
75
 
52
76
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
53
77
 
54
78
  # Creates a new Dalli::Client instance with specified addresses and options.
55
- # By default address is equal localhost:11211.
79
+ # If no addresses are provided, we give nil to Dalli::Client, so it uses its fallbacks:
80
+ # - ENV["MEMCACHE_SERVERS"] (if defined)
81
+ # - "127.0.0.1:11211" (otherwise)
56
82
  #
57
83
  # ActiveSupport::Cache::MemCacheStore.build_mem_cache
58
- # # => #<Dalli::Client:0x007f98a47d2028 @servers=["localhost:11211"], @options={}, @ring=nil>
84
+ # # => #<Dalli::Client:0x007f98a47d2028 @servers=["127.0.0.1:11211"], @options={}, @ring=nil>
59
85
  # ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
60
86
  # # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
61
87
  def self.build_mem_cache(*addresses) # :nodoc:
62
88
  addresses = addresses.flatten
63
89
  options = addresses.extract_options!
64
- addresses = ["localhost:11211"] if addresses.empty?
65
- Dalli::Client.new(addresses, options)
90
+ addresses = nil if addresses.compact.empty?
91
+ pool_options = retrieve_pool_options(options)
92
+
93
+ if pool_options.empty?
94
+ Dalli::Client.new(addresses, options)
95
+ else
96
+ ensure_connection_pool_added!
97
+ ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
98
+ end
66
99
  end
67
100
 
68
101
  # Creates a new MemCacheStore object, with the given memcached server
@@ -71,11 +104,14 @@ module ActiveSupport
71
104
  #
72
105
  # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
73
106
  #
74
- # If no addresses are specified, then MemCacheStore will connect to
75
- # localhost port 11211 (the default memcached port).
107
+ # If no addresses are provided, but <tt>ENV['MEMCACHE_SERVERS']</tt> is defined, it will be used instead. Otherwise,
108
+ # MemCacheStore will connect to localhost:11211 (the default memcached port).
76
109
  def initialize(*addresses)
77
110
  addresses = addresses.flatten
78
111
  options = addresses.extract_options!
112
+ if options.key?(:cache_nils)
113
+ options[:skip_nil] = !options.delete(:cache_nils)
114
+ end
79
115
  super(options)
80
116
 
81
117
  unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
@@ -85,49 +121,51 @@ module ActiveSupport
85
121
  @data = addresses.first
86
122
  else
87
123
  mem_cache_options = options.dup
88
- 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) }
89
127
  @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
90
128
  end
91
129
  end
92
130
 
93
- # Reads multiple values from the cache using a single call to the
94
- # servers for all keys. Options can be passed in the last argument.
95
- def read_multi(*names)
96
- options = names.extract_options!
97
- options = merged_options(options)
98
-
99
- keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
100
- raw_values = @data.get_multi(keys_to_names.keys)
101
- values = {}
102
- raw_values.each do |key, value|
103
- entry = deserialize_entry(value)
104
- values[keys_to_names[key]] = entry.value unless entry.expired?
105
- end
106
- values
107
- end
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.
108
146
 
109
147
  # Increment a cached value. This method uses the memcached incr atomic
110
- # operator and can only be used on values written with the :raw option.
111
- # 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
112
150
  # to zero.
113
151
  def increment(name, amount = 1, options = nil)
114
152
  options = merged_options(options)
115
153
  instrument(:increment, name, amount: amount) do
116
154
  rescue_error_with nil do
117
- @data.incr(normalize_key(name, options), amount)
155
+ @data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in]) }
118
156
  end
119
157
  end
120
158
  end
121
159
 
122
160
  # Decrement a cached value. This method uses the memcached decr atomic
123
- # operator and can only be used on values written with the :raw option.
124
- # 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
125
163
  # to zero.
126
164
  def decrement(name, amount = 1, options = nil)
127
165
  options = merged_options(options)
128
166
  instrument(:decrement, name, amount: amount) do
129
167
  rescue_error_with nil do
130
- @data.decr(normalize_key(name, options), amount)
168
+ @data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in]) }
131
169
  end
132
170
  end
133
171
  end
@@ -135,61 +173,150 @@ module ActiveSupport
135
173
  # Clear the entire cache on all memcached servers. This method should
136
174
  # be used with care when shared cache is being used.
137
175
  def clear(options = nil)
138
- rescue_error_with(nil) { @data.flush_all }
176
+ rescue_error_with(nil) { @data.with { |c| c.flush_all } }
139
177
  end
140
178
 
141
179
  # Get the statistics from the memcached servers.
142
180
  def stats
143
- @data.stats
181
+ @data.with { |c| c.stats }
144
182
  end
145
183
 
146
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
+
147
233
  # Read an entry from the cache.
148
- def read_entry(key, options)
149
- rescue_error_with(nil) { deserialize_entry(@data.get(key, options)) }
234
+ def read_entry(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
150
242
  end
151
243
 
152
244
  # Write an entry to the cache.
153
- def write_entry(key, entry, options)
154
- method = options && options[:unless_exist] ? :add : :set
155
- value = options[:raw] ? entry.value.to_s : entry
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)
250
+ method = options[:unless_exist] ? :add : :set
156
251
  expires_in = options[:expires_in].to_i
157
- if expires_in > 0 && !options[:raw]
252
+ if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
158
253
  # Set the memcache expire a few minutes in the future to support race condition ttls on read
159
254
  expires_in += 5.minutes
160
255
  end
161
256
  rescue_error_with false do
162
- @data.send(method, key, value, expires_in, options)
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) }
163
260
  end
164
261
  end
165
262
 
263
+ # Reads multiple entries from the cache implementation.
264
+ def read_multi_entries(names, **options)
265
+ keys_to_names = names.index_by { |name| normalize_key(name, options) }
266
+
267
+ raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
268
+ values = {}
269
+
270
+ raw_values.each do |key, value|
271
+ entry = deserialize_entry(value, raw: options[:raw])
272
+
273
+ unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
274
+ values[keys_to_names[key]] = entry.value
275
+ end
276
+ end
277
+
278
+ values
279
+ end
280
+
166
281
  # Delete an entry from the cache.
167
- def delete_entry(key, options)
168
- rescue_error_with(false) { @data.delete(key) }
282
+ def delete_entry(key, **options)
283
+ rescue_error_with(false) { @data.with { |c| c.delete(key) } }
284
+ end
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
169
292
  end
170
293
 
171
294
  # Memcache keys are binaries. So we need to force their encoding to binary
172
295
  # before applying the regular expression to ensure we are escaping all
173
296
  # characters properly.
174
297
  def normalize_key(key, options)
175
- key = super.dup
176
- key = key.force_encoding(Encoding::ASCII_8BIT)
177
- key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
178
- key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
298
+ key = super
299
+ if key
300
+ key = key.dup.force_encoding(Encoding::ASCII_8BIT)
301
+ key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
302
+ key = "#{key[0, 212]}:hash:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
303
+ end
179
304
  key
180
305
  end
181
306
 
182
- def deserialize_entry(raw_value)
183
- if raw_value
184
- entry = Marshal.load(raw_value) rescue raw_value
185
- entry.is_a?(Entry) ? entry : Entry.new(entry)
307
+ def deserialize_entry(payload, raw: false, **)
308
+ if payload && raw
309
+ Entry.new(payload)
310
+ else
311
+ super(payload)
186
312
  end
187
313
  end
188
314
 
189
315
  def rescue_error_with(fallback)
190
316
  yield
191
- rescue Dalli::DalliError => e
192
- 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
193
320
  fallback
194
321
  end
195
322
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "monitor"
2
4
 
3
5
  module ActiveSupport
@@ -9,18 +11,46 @@ module ActiveSupport
9
11
  # to share cache data with each other and this may not be the most
10
12
  # appropriate cache in that scenario.
11
13
  #
12
- # 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
13
15
  # initializer (default is 32Mb). When the cache exceeds the allotted size,
14
16
  # a cleanup will occur which tries to prune the cache down to three quarters
15
17
  # of the maximum size by removing the least recently used entries.
16
18
  #
19
+ # Unlike other Cache store implementations, MemoryStore does not compress
20
+ # values by default. MemoryStore does not benefit from compression as much
21
+ # as other Store implementations, as it does not send data over a network.
22
+ # However, when compression is enabled, it still pays the full cost of
23
+ # compression in terms of cpu use.
24
+ #
17
25
  # MemoryStore is thread-safe.
18
26
  class MemoryStore < Store
27
+ module DupCoder # :nodoc:
28
+ extend self
29
+
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
+ end
40
+
41
+ def load(entry)
42
+ entry = entry.dup
43
+ entry.dup_value!
44
+ entry
45
+ end
46
+ end
47
+
19
48
  def initialize(options = nil)
20
49
  options ||= {}
50
+ # Disable compression by default.
51
+ options[:compress] ||= false
21
52
  super(options)
22
53
  @data = {}
23
- @key_access = {}
24
54
  @max_size = options[:size] || 32.megabytes
25
55
  @max_prune_time = options[:max_prune_time] || 2
26
56
  @cache_size = 0
@@ -28,11 +58,15 @@ module ActiveSupport
28
58
  @pruning = false
29
59
  end
30
60
 
61
+ # Advertise cache versioning support.
62
+ def self.supports_cache_versioning?
63
+ true
64
+ end
65
+
31
66
  # Delete all data stored in a given cache store.
32
67
  def clear(options = nil)
33
68
  synchronize do
34
69
  @data.clear
35
- @key_access.clear
36
70
  @cache_size = 0
37
71
  end
38
72
  end
@@ -44,7 +78,7 @@ module ActiveSupport
44
78
  keys = synchronize { @data.keys }
45
79
  keys.each do |key|
46
80
  entry = @data[key]
47
- delete_entry(key, options) if entry && entry.expired?
81
+ delete_entry(key, **options) if entry && entry.expired?
48
82
  end
49
83
  end
50
84
  end
@@ -55,13 +89,13 @@ module ActiveSupport
55
89
  return if pruning?
56
90
  @pruning = true
57
91
  begin
58
- start_time = Time.now
92
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
59
93
  cleanup
60
94
  instrument(:prune, target_size, from: @cache_size) do
61
- keys = synchronize { @key_access.keys.sort { |a, b| @key_access[a].to_f <=> @key_access[b].to_f } }
95
+ keys = synchronize { @data.keys }
62
96
  keys.each do |key|
63
- delete_entry(key, options)
64
- return if @cache_size <= target_size || (max_time && Time.now - start_time > max_time)
97
+ delete_entry(key, **options)
98
+ return if @cache_size <= target_size || (max_time && Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time > max_time)
65
99
  end
66
100
  end
67
101
  ensure
@@ -91,13 +125,13 @@ module ActiveSupport
91
125
  matcher = key_matcher(matcher, options)
92
126
  keys = synchronize { @data.keys }
93
127
  keys.each do |key|
94
- delete_entry(key, options) if key.match(matcher)
128
+ delete_entry(key, **options) if key.match(matcher)
95
129
  end
96
130
  end
97
131
  end
98
132
 
99
133
  def inspect # :nodoc:
100
- "<##{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
134
+ "#<#{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
101
135
  end
102
136
 
103
137
  # Synchronize calls to the cache. This should be called wherever the underlying cache implementation
@@ -107,54 +141,56 @@ module ActiveSupport
107
141
  end
108
142
 
109
143
  private
110
-
111
144
  PER_ENTRY_OVERHEAD = 240
112
145
 
113
- def cached_size(key, entry)
114
- key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
146
+ def default_coder
147
+ DupCoder
115
148
  end
116
149
 
117
- def read_entry(key, options)
118
- entry = @data[key]
150
+ def cached_size(key, payload)
151
+ key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
152
+ end
153
+
154
+ def read_entry(key, **options)
155
+ entry = nil
119
156
  synchronize do
120
- if entry
121
- @key_access[key] = Time.now.to_f
122
- else
123
- @key_access.delete(key)
157
+ payload = @data.delete(key)
158
+ if payload
159
+ @data[key] = payload
160
+ entry = deserialize_entry(payload)
124
161
  end
125
162
  end
126
163
  entry
127
164
  end
128
165
 
129
- def write_entry(key, entry, options)
130
- entry.dup_value!
166
+ def write_entry(key, entry, **options)
167
+ payload = serialize_entry(entry, **options)
131
168
  synchronize do
132
- old_entry = @data[key]
133
- return false if @data.key?(key) && options[:unless_exist]
134
- if old_entry
135
- @cache_size -= (old_entry.size - entry.size)
169
+ return false if options[:unless_exist] && @data.key?(key)
170
+
171
+ old_payload = @data[key]
172
+ if old_payload
173
+ @cache_size -= (old_payload.bytesize - payload.bytesize)
136
174
  else
137
- @cache_size += cached_size(key, entry)
175
+ @cache_size += cached_size(key, payload)
138
176
  end
139
- @key_access[key] = Time.now.to_f
140
- @data[key] = entry
177
+ @data[key] = payload
141
178
  prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
142
179
  true
143
180
  end
144
181
  end
145
182
 
146
- def delete_entry(key, options)
183
+ def delete_entry(key, **options)
147
184
  synchronize do
148
- @key_access.delete(key)
149
- entry = @data.delete(key)
150
- @cache_size -= cached_size(key, entry) if entry
151
- !!entry
185
+ payload = @data.delete(key)
186
+ @cache_size -= cached_size(key, payload) if payload
187
+ !!payload
152
188
  end
153
189
  end
154
190
 
155
191
  def modify_value(name, amount, options)
192
+ options = merged_options(options)
156
193
  synchronize do
157
- options = merged_options(options)
158
194
  if num = read(name, options)
159
195
  num = num.to_i + amount
160
196
  write(name, num, options)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
4
  module Cache
3
5
  # A cache store implementation which doesn't actually store anything. Useful in
@@ -10,6 +12,11 @@ module ActiveSupport
10
12
  class NullStore < Store
11
13
  prepend Strategy::LocalCache
12
14
 
15
+ # Advertise cache versioning support.
16
+ def self.supports_cache_versioning?
17
+ true
18
+ end
19
+
13
20
  def clear(options = nil)
14
21
  end
15
22
 
@@ -26,14 +33,22 @@ module ActiveSupport
26
33
  end
27
34
 
28
35
  private
29
- def read_entry(key, options)
36
+ def read_entry(key, **s)
37
+ deserialize_entry(read_serialized_entry(key))
38
+ end
39
+
40
+ def read_serialized_entry(_key, **)
41
+ end
42
+
43
+ def write_entry(key, entry, **)
44
+ write_serialized_entry(key, serialize_entry(entry))
30
45
  end
31
46
 
32
- def write_entry(key, entry, options)
47
+ def write_serialized_entry(_key, _payload, **)
33
48
  true
34
49
  end
35
50
 
36
- def delete_entry(key, options)
51
+ def delete_entry(key, **options)
37
52
  false
38
53
  end
39
54
  end