activesupport 4.0.12 → 7.0.2.4

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 (295) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +249 -501
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -5
  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 +48 -0
  8. data/lib/active_support/backtrace_cleaner.rb +41 -13
  9. data/lib/active_support/benchmarkable.rb +7 -15
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache/file_store.rb +96 -74
  12. data/lib/active_support/cache/mem_cache_store.rb +211 -103
  13. data/lib/active_support/cache/memory_store.rb +90 -58
  14. data/lib/active_support/cache/null_store.rb +19 -7
  15. data/lib/active_support/cache/redis_cache_store.rb +468 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +86 -83
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  18. data/lib/active_support/cache.rb +580 -241
  19. data/lib/active_support/callbacks.rb +812 -425
  20. data/lib/active_support/code_generator.rb +65 -0
  21. data/lib/active_support/concern.rb +103 -14
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +33 -0
  23. data/lib/active_support/concurrency/share_lock.rb +226 -0
  24. data/lib/active_support/configurable.rb +21 -19
  25. data/lib/active_support/configuration_file.rb +51 -0
  26. data/lib/active_support/core_ext/array/access.rb +47 -1
  27. data/lib/active_support/core_ext/array/conversions.rb +35 -44
  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 +26 -16
  32. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  33. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  34. data/lib/active_support/core_ext/array.rb +10 -7
  35. data/lib/active_support/core_ext/benchmark.rb +5 -3
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  37. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  38. data/lib/active_support/core_ext/class/attribute.rb +52 -49
  39. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  40. data/lib/active_support/core_ext/class/subclasses.rb +25 -26
  41. data/lib/active_support/core_ext/class.rb +4 -4
  42. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  43. data/lib/active_support/core_ext/date/blank.rb +14 -0
  44. data/lib/active_support/core_ext/date/calculations.rb +31 -18
  45. data/lib/active_support/core_ext/date/conversions.rb +43 -32
  46. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  47. data/lib/active_support/core_ext/date/zones.rb +5 -34
  48. data/lib/active_support/core_ext/date.rb +7 -4
  49. data/lib/active_support/core_ext/date_and_time/calculations.rb +198 -66
  50. data/lib/active_support/core_ext/date_and_time/compatibility.rb +31 -0
  51. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  53. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  54. data/lib/active_support/core_ext/date_time/calculations.rb +79 -38
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  56. data/lib/active_support/core_ext/date_time/conversions.rb +31 -26
  57. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  58. data/lib/active_support/core_ext/date_time.rb +8 -4
  59. data/lib/active_support/core_ext/digest/uuid.rb +79 -0
  60. data/lib/active_support/core_ext/digest.rb +3 -0
  61. data/lib/active_support/core_ext/enumerable.rb +249 -17
  62. data/lib/active_support/core_ext/file/atomic.rb +41 -32
  63. data/lib/active_support/core_ext/file.rb +3 -1
  64. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +14 -5
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  69. data/lib/active_support/core_ext/hash/keys.rb +39 -56
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  71. data/lib/active_support/core_ext/hash/slice.rb +8 -23
  72. data/lib/active_support/core_ext/hash.rb +10 -8
  73. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  74. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  75. data/lib/active_support/core_ext/integer/time.rb +11 -33
  76. data/lib/active_support/core_ext/integer.rb +5 -3
  77. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  78. data/lib/active_support/core_ext/kernel/reporting.rb +9 -78
  79. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  80. data/lib/active_support/core_ext/kernel.rb +5 -4
  81. data/lib/active_support/core_ext/load_error.rb +5 -21
  82. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  83. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  84. data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
  85. data/lib/active_support/core_ext/module/attribute_accessors.rb +186 -44
  86. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +157 -0
  87. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  88. data/lib/active_support/core_ext/module/delegation.rb +172 -45
  89. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  90. data/lib/active_support/core_ext/module/introspection.rb +23 -38
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/module.rb +13 -10
  94. data/lib/active_support/core_ext/name_error.rb +45 -4
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +135 -127
  97. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  99. data/lib/active_support/core_ext/numeric.rb +6 -3
  100. data/lib/active_support/core_ext/object/acts_like.rb +41 -6
  101. data/lib/active_support/core_ext/object/blank.rb +70 -20
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  104. data/lib/active_support/core_ext/object/duplicable.rb +17 -47
  105. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +244 -0
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  110. data/lib/active_support/core_ext/object/try.rb +106 -26
  111. data/lib/active_support/core_ext/object/with_options.rb +64 -5
  112. data/lib/active_support/core_ext/object.rb +14 -12
  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 +37 -15
  117. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  118. data/lib/active_support/core_ext/range/each.rb +18 -17
  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 +2 -0
  121. data/lib/active_support/core_ext/range.rb +7 -4
  122. data/lib/active_support/core_ext/regexp.rb +10 -1
  123. data/lib/active_support/core_ext/securerandom.rb +45 -0
  124. data/lib/active_support/core_ext/string/access.rb +42 -51
  125. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  126. data/lib/active_support/core_ext/string/conversions.rb +18 -13
  127. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  128. data/lib/active_support/core_ext/string/filters.rb +97 -7
  129. data/lib/active_support/core_ext/string/indent.rb +6 -4
  130. data/lib/active_support/core_ext/string/inflections.rb +106 -25
  131. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  132. data/lib/active_support/core_ext/string/multibyte.rb +18 -9
  133. data/lib/active_support/core_ext/string/output_safety.rb +227 -54
  134. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  135. data/lib/active_support/core_ext/string/strip.rb +6 -5
  136. data/lib/active_support/core_ext/string/zones.rb +4 -1
  137. data/lib/active_support/core_ext/string.rb +15 -13
  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 +3 -1
  141. data/lib/active_support/core_ext/time/calculations.rb +178 -116
  142. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  143. data/lib/active_support/core_ext/time/conversions.rb +37 -25
  144. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  145. data/lib/active_support/core_ext/time/zones.rb +44 -42
  146. data/lib/active_support/core_ext/time.rb +8 -5
  147. data/lib/active_support/core_ext/uri.rb +4 -25
  148. data/lib/active_support/core_ext.rb +4 -2
  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 +3 -1
  152. data/lib/active_support/dependencies/interlock.rb +49 -0
  153. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  154. data/lib/active_support/dependencies.rb +71 -696
  155. data/lib/active_support/deprecation/behaviors.rb +65 -16
  156. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  157. data/lib/active_support/deprecation/disallowed.rb +56 -0
  158. data/lib/active_support/deprecation/instance_delegator.rb +16 -2
  159. data/lib/active_support/deprecation/method_wrappers.rb +62 -21
  160. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -31
  161. data/lib/active_support/deprecation/reporting.rb +81 -18
  162. data/lib/active_support/deprecation.rb +19 -11
  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 +123 -0
  166. data/lib/active_support/duration/iso8601_serializer.rb +67 -0
  167. data/lib/active_support/duration.rb +437 -39
  168. data/lib/active_support/encrypted_configuration.rb +56 -0
  169. data/lib/active_support/encrypted_file.rb +117 -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 +170 -0
  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 +151 -0
  176. data/lib/active_support/executor/test_helper.rb +7 -0
  177. data/lib/active_support/executor.rb +8 -0
  178. data/lib/active_support/file_update_checker.rb +62 -37
  179. data/lib/active_support/fork_tracker.rb +71 -0
  180. data/lib/active_support/gem_version.rb +17 -0
  181. data/lib/active_support/gzip.rb +7 -5
  182. data/lib/active_support/hash_with_indifferent_access.rb +207 -54
  183. data/lib/active_support/html_safe_translation.rb +43 -0
  184. data/lib/active_support/i18n.rb +10 -6
  185. data/lib/active_support/i18n_railtie.rb +48 -19
  186. data/lib/active_support/inflections.rb +19 -12
  187. data/lib/active_support/inflector/inflections.rb +97 -37
  188. data/lib/active_support/inflector/methods.rb +192 -157
  189. data/lib/active_support/inflector/transliterate.rb +83 -33
  190. data/lib/active_support/inflector.rb +7 -5
  191. data/lib/active_support/isolated_execution_state.rb +64 -0
  192. data/lib/active_support/json/decoding.rb +37 -42
  193. data/lib/active_support/json/encoding.rb +93 -293
  194. data/lib/active_support/json.rb +4 -2
  195. data/lib/active_support/key_generator.rb +30 -47
  196. data/lib/active_support/lazy_load_hooks.rb +54 -21
  197. data/lib/active_support/locale/en.rb +33 -0
  198. data/lib/active_support/locale/en.yml +10 -4
  199. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  200. data/lib/active_support/log_subscriber.rb +61 -18
  201. data/lib/active_support/logger.rb +40 -4
  202. data/lib/active_support/logger_silence.rb +17 -20
  203. data/lib/active_support/logger_thread_safe_level.rb +69 -0
  204. data/lib/active_support/message_encryptor.rb +178 -55
  205. data/lib/active_support/message_verifier.rb +195 -26
  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 +45 -92
  210. data/lib/active_support/multibyte/unicode.rb +44 -377
  211. data/lib/active_support/multibyte.rb +5 -3
  212. data/lib/active_support/notifications/fanout.rb +177 -44
  213. data/lib/active_support/notifications/instrumenter.rb +117 -17
  214. data/lib/active_support/notifications.rb +106 -39
  215. data/lib/active_support/number_helper/number_converter.rb +181 -0
  216. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  217. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  218. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  219. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  220. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  221. data/lib/active_support/number_helper/number_to_phone_converter.rb +59 -0
  222. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  223. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  224. data/lib/active_support/number_helper.rb +152 -394
  225. data/lib/active_support/option_merger.rb +18 -5
  226. data/lib/active_support/ordered_hash.rb +8 -6
  227. data/lib/active_support/ordered_options.rb +43 -7
  228. data/lib/active_support/parameter_filter.rb +138 -0
  229. data/lib/active_support/per_thread_registry.rb +24 -11
  230. data/lib/active_support/proxy_object.rb +2 -0
  231. data/lib/active_support/rails.rb +10 -11
  232. data/lib/active_support/railtie.rb +118 -12
  233. data/lib/active_support/reloader.rb +130 -0
  234. data/lib/active_support/rescuable.rb +112 -57
  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 +38 -0
  238. data/lib/active_support/string_inquirer.rb +11 -4
  239. data/lib/active_support/subscriber.rb +109 -39
  240. data/lib/active_support/tagged_logging.rb +54 -17
  241. data/lib/active_support/test_case.rb +121 -37
  242. data/lib/active_support/testing/assertions.rb +177 -39
  243. data/lib/active_support/testing/autorun.rb +5 -3
  244. data/lib/active_support/testing/constant_lookup.rb +3 -6
  245. data/lib/active_support/testing/declarative.rb +10 -22
  246. data/lib/active_support/testing/deprecation.rb +65 -11
  247. data/lib/active_support/testing/file_fixtures.rb +38 -0
  248. data/lib/active_support/testing/isolation.rb +56 -87
  249. data/lib/active_support/testing/method_call_assertions.rb +70 -0
  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 +30 -10
  255. data/lib/active_support/testing/stream.rb +41 -0
  256. data/lib/active_support/testing/tagged_logging.rb +6 -4
  257. data/lib/active_support/testing/time_helpers.rb +246 -0
  258. data/lib/active_support/time.rb +13 -13
  259. data/lib/active_support/time_with_zone.rb +315 -90
  260. data/lib/active_support/values/time_zone.rb +306 -135
  261. data/lib/active_support/version.rb +6 -7
  262. data/lib/active_support/xml_mini/jdom.rb +117 -115
  263. data/lib/active_support/xml_mini/libxml.rb +22 -21
  264. data/lib/active_support/xml_mini/libxmlsax.rb +17 -19
  265. data/lib/active_support/xml_mini/nokogiri.rb +19 -19
  266. data/lib/active_support/xml_mini/nokogirisax.rb +16 -17
  267. data/lib/active_support/xml_mini/rexml.rb +25 -17
  268. data/lib/active_support/xml_mini.rb +67 -56
  269. data/lib/active_support.rb +58 -3
  270. metadata +125 -66
  271. data/lib/active_support/basic_object.rb +0 -11
  272. data/lib/active_support/buffered_logger.rb +0 -21
  273. data/lib/active_support/concurrency/latch.rb +0 -27
  274. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  275. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  276. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  277. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  278. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  279. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  280. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  281. data/lib/active_support/core_ext/logger.rb +0 -67
  282. data/lib/active_support/core_ext/marshal.rb +0 -21
  283. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  284. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  285. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  286. data/lib/active_support/core_ext/proc.rb +0 -17
  287. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  288. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  289. data/lib/active_support/core_ext/struct.rb +0 -6
  290. data/lib/active_support/core_ext/thread.rb +0 -79
  291. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  292. data/lib/active_support/file_watcher.rb +0 -36
  293. data/lib/active_support/json/variable.rb +0 -18
  294. data/lib/active_support/testing/pending.rb +0 -14
  295. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,18 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
- require 'dalli'
4
+ require "dalli"
3
5
  rescue LoadError => e
4
6
  $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
5
7
  raise e
6
8
  end
7
9
 
8
- require 'digest/md5'
9
- require 'active_support/core_ext/marshal'
10
- require 'active_support/core_ext/array/extract_options'
10
+ require "delegate"
11
+ require "active_support/core_ext/enumerable"
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,13 +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
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
62
+
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
69
+ else
70
+ super
71
+ end
72
+ end
73
+ end
74
+ prepend DupLocalCache
75
+
27
76
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
28
77
 
29
- def self.build_mem_cache(*addresses)
78
+ # Creates a new Dalli::Client instance with specified addresses and options.
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)
82
+ #
83
+ # ActiveSupport::Cache::MemCacheStore.build_mem_cache
84
+ # # => #<Dalli::Client:0x007f98a47d2028 @servers=["127.0.0.1:11211"], @options={}, @ring=nil>
85
+ # ActiveSupport::Cache::MemCacheStore.build_mem_cache('localhost:10290')
86
+ # # => #<Dalli::Client:0x007f98a47b3a60 @servers=["localhost:10290"], @options={}, @ring=nil>
87
+ def self.build_mem_cache(*addresses) # :nodoc:
30
88
  addresses = addresses.flatten
31
89
  options = addresses.extract_options!
32
- addresses = ["localhost:11211"] if addresses.empty?
33
- 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
34
99
  end
35
100
 
36
101
  # Creates a new MemCacheStore object, with the given memcached server
@@ -39,162 +104,205 @@ module ActiveSupport
39
104
  #
40
105
  # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
41
106
  #
42
- # If no addresses are specified, then MemCacheStore will connect to
43
- # localhost port 11211 (the default memcached port).
44
- #
45
- # Instead of addresses one can pass in a MemCache-like object. For example:
46
- #
47
- # require 'memcached' # gem install memcached; uses C bindings to libmemcached
48
- # ActiveSupport::Cache::MemCacheStore.new(Memcached::Rails.new("localhost:11211"))
107
+ # If no addresses are provided, but ENV['MEMCACHE_SERVERS'] is defined, it will be used instead. Otherwise,
108
+ # MemCacheStore will connect to localhost:11211 (the default memcached port).
49
109
  def initialize(*addresses)
50
110
  addresses = addresses.flatten
51
111
  options = addresses.extract_options!
112
+ if options.key?(:cache_nils)
113
+ options[:skip_nil] = !options.delete(:cache_nils)
114
+ end
52
115
  super(options)
53
116
 
54
- if addresses.first.respond_to?(:get)
117
+ unless [String, Dalli::Client, NilClass].include?(addresses.first.class)
118
+ raise ArgumentError, "First argument must be an empty array, an array of hosts or a Dalli::Client instance."
119
+ end
120
+ if addresses.first.is_a?(Dalli::Client)
55
121
  @data = addresses.first
56
122
  else
57
123
  mem_cache_options = options.dup
58
- 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) }
59
127
  @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
60
128
  end
61
-
62
- extend Strategy::LocalCache
63
- extend LocalCacheWithRaw
64
- end
65
-
66
- # Reads multiple values from the cache using a single call to the
67
- # servers for all keys. Options can be passed in the last argument.
68
- def read_multi(*names)
69
- options = names.extract_options!
70
- options = merged_options(options)
71
- keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
72
- raw_values = @data.get_multi(keys_to_names.keys, :raw => true)
73
- values = {}
74
- raw_values.each do |key, value|
75
- entry = deserialize_entry(value)
76
- values[keys_to_names[key]] = entry.value unless entry.expired?
77
- end
78
- values
79
129
  end
80
130
 
81
131
  # Increment a cached value. This method uses the memcached incr atomic
82
132
  # operator and can only be used on values written with the :raw option.
83
133
  # Calling it on a value not stored with :raw will initialize that value
84
134
  # to zero.
85
- def increment(name, amount = 1, options = nil) # :nodoc:
135
+ def increment(name, amount = 1, options = nil)
86
136
  options = merged_options(options)
87
- instrument(:increment, name, :amount => amount) do
88
- @data.incr(escape_key(namespaced_key(name, options)), amount)
137
+ instrument(:increment, name, amount: amount) do
138
+ rescue_error_with nil do
139
+ @data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in]) }
140
+ end
89
141
  end
90
- rescue Dalli::DalliError => e
91
- logger.error("DalliError (#{e}): #{e.message}") if logger
92
- nil
93
142
  end
94
143
 
95
144
  # Decrement a cached value. This method uses the memcached decr atomic
96
145
  # operator and can only be used on values written with the :raw option.
97
146
  # Calling it on a value not stored with :raw will initialize that value
98
147
  # to zero.
99
- def decrement(name, amount = 1, options = nil) # :nodoc:
148
+ def decrement(name, amount = 1, options = nil)
100
149
  options = merged_options(options)
101
- instrument(:decrement, name, :amount => amount) do
102
- @data.decr(escape_key(namespaced_key(name, options)), amount)
150
+ instrument(:decrement, name, amount: amount) do
151
+ rescue_error_with nil do
152
+ @data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in]) }
153
+ end
103
154
  end
104
- rescue Dalli::DalliError => e
105
- logger.error("DalliError (#{e}): #{e.message}") if logger
106
- nil
107
155
  end
108
156
 
109
157
  # Clear the entire cache on all memcached servers. This method should
110
158
  # be used with care when shared cache is being used.
111
159
  def clear(options = nil)
112
- @data.flush_all
113
- rescue Dalli::DalliError => e
114
- logger.error("DalliError (#{e}): #{e.message}") if logger
115
- nil
160
+ rescue_error_with(nil) { @data.with { |c| c.flush_all } }
116
161
  end
117
162
 
118
163
  # Get the statistics from the memcached servers.
119
164
  def stats
120
- @data.stats
165
+ @data.with { |c| c.stats }
121
166
  end
122
167
 
123
- protected
168
+ private
169
+ module Coders # :nodoc:
170
+ class << self
171
+ def [](version)
172
+ case version
173
+ when 6.1
174
+ Rails61Coder
175
+ when 7.0
176
+ Rails70Coder
177
+ else
178
+ raise ArgumentError, "Unknown ActiveSupport::Cache.format_version #{Cache.format_version.inspect}"
179
+ end
180
+ end
181
+ end
182
+
183
+ module Loader
184
+ def load(payload)
185
+ if payload.is_a?(Entry)
186
+ payload
187
+ else
188
+ Cache::Coders::Loader.load(payload)
189
+ end
190
+ end
191
+ end
192
+
193
+ module Rails61Coder
194
+ include Loader
195
+ extend self
196
+
197
+ def dump(entry)
198
+ entry
199
+ end
200
+
201
+ def dump_compressed(entry, threshold)
202
+ entry.compressed(threshold)
203
+ end
204
+ end
205
+
206
+ module Rails70Coder
207
+ include Cache::Coders::Rails70Coder
208
+ include Loader
209
+ extend self
210
+ end
211
+ end
212
+
213
+ def default_coder
214
+ Coders[Cache.format_version]
215
+ end
216
+
124
217
  # Read an entry from the cache.
125
- def read_entry(key, options) # :nodoc:
126
- deserialize_entry(@data.get(escape_key(key), options))
127
- rescue Dalli::DalliError => e
128
- logger.error("DalliError (#{e}): #{e.message}") if logger
129
- nil
218
+ def read_entry(key, **options)
219
+ deserialize_entry(read_serialized_entry(key, **options), **options)
220
+ end
221
+
222
+ def read_serialized_entry(key, **options)
223
+ rescue_error_with(nil) do
224
+ @data.with { |c| c.get(key, options) }
225
+ end
130
226
  end
131
227
 
132
228
  # Write an entry to the cache.
133
- def write_entry(key, entry, options) # :nodoc:
134
- method = options && options[:unless_exist] ? :add : :set
135
- value = options[:raw] ? entry.value.to_s : entry
229
+ def write_entry(key, entry, **options)
230
+ write_serialized_entry(key, serialize_entry(entry, **options), **options)
231
+ end
232
+
233
+ def write_serialized_entry(key, payload, **options)
234
+ method = options[:unless_exist] ? :add : :set
136
235
  expires_in = options[:expires_in].to_i
137
- if expires_in > 0 && !options[:raw]
236
+ if options[:race_condition_ttl] && expires_in > 0 && !options[:raw]
138
237
  # Set the memcache expire a few minutes in the future to support race condition ttls on read
139
238
  expires_in += 5.minutes
140
239
  end
141
- @data.send(method, escape_key(key), value, expires_in, options)
142
- rescue Dalli::DalliError => e
143
- logger.error("DalliError (#{e}): #{e.message}") if logger
144
- false
240
+ rescue_error_with false do
241
+ # Don't pass compress option to Dalli since we are already dealing with compression.
242
+ options.delete(:compress)
243
+ @data.with { |c| c.send(method, key, payload, expires_in, **options) }
244
+ end
245
+ end
246
+
247
+ # Reads multiple entries from the cache implementation.
248
+ def read_multi_entries(names, **options)
249
+ keys_to_names = names.index_by { |name| normalize_key(name, options) }
250
+
251
+ raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
252
+ values = {}
253
+
254
+ raw_values.each do |key, value|
255
+ entry = deserialize_entry(value, raw: options[:raw])
256
+
257
+ unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
258
+ values[keys_to_names[key]] = entry.value
259
+ end
260
+ end
261
+
262
+ values
145
263
  end
146
264
 
147
265
  # Delete an entry from the cache.
148
- def delete_entry(key, options) # :nodoc:
149
- @data.delete(escape_key(key))
150
- rescue Dalli::DalliError => e
151
- logger.error("DalliError (#{e}): #{e.message}") if logger
152
- false
266
+ def delete_entry(key, **options)
267
+ rescue_error_with(false) { @data.with { |c| c.delete(key) } }
153
268
  end
154
269
 
155
- private
270
+ def serialize_entry(entry, raw: false, **options)
271
+ if raw
272
+ entry.value.to_s
273
+ else
274
+ super(entry, raw: raw, **options)
275
+ end
276
+ end
156
277
 
157
278
  # Memcache keys are binaries. So we need to force their encoding to binary
158
279
  # before applying the regular expression to ensure we are escaping all
159
280
  # characters properly.
160
- def escape_key(key)
161
- key = key.to_s.dup
162
- key = key.force_encoding(Encoding::ASCII_8BIT)
163
- key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
164
- key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
281
+ def normalize_key(key, options)
282
+ key = super
283
+ if key
284
+ key = key.dup.force_encoding(Encoding::ASCII_8BIT)
285
+ key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
286
+ key = "#{key[0, 212]}:hash:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
287
+ end
165
288
  key
166
289
  end
167
290
 
168
- def deserialize_entry(raw_value)
169
- if raw_value
170
- entry = Marshal.load(raw_value) rescue raw_value
171
- entry.is_a?(Entry) ? entry : Entry.new(entry)
291
+ def deserialize_entry(payload, raw: false, **)
292
+ if payload && raw
293
+ Entry.new(payload)
172
294
  else
173
- nil
295
+ super(payload)
174
296
  end
175
297
  end
176
298
 
177
- # Provide support for raw values in the local cache strategy.
178
- module LocalCacheWithRaw # :nodoc:
179
- protected
180
- def read_entry(key, options)
181
- entry = super
182
- if options[:raw] && local_cache && entry
183
- entry = deserialize_entry(entry.value)
184
- end
185
- entry
186
- end
187
-
188
- def write_entry(key, entry, options) # :nodoc:
189
- retval = super
190
- if options[:raw] && local_cache && retval
191
- raw_entry = Entry.new(entry.value.to_s)
192
- raw_entry.expires_at = entry.expires_at
193
- local_cache.write_entry(key, raw_entry, options)
194
- end
195
- retval
196
- end
197
- end
299
+ def rescue_error_with(fallback)
300
+ yield
301
+ rescue Dalli::DalliError => error
302
+ ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning)
303
+ logger.error("DalliError (#{error}): #{error.message}") if logger
304
+ fallback
305
+ end
198
306
  end
199
307
  end
200
308
  end
@@ -1,10 +1,12 @@
1
- require 'monitor'
1
+ # frozen_string_literal: true
2
+
3
+ require "monitor"
2
4
 
3
5
  module ActiveSupport
4
6
  module Cache
5
7
  # A cache store implementation which stores everything into memory in the
6
8
  # same process. If you're running multiple Ruby on Rails server processes
7
- # (which is the case if you're using mongrel_cluster or Phusion Passenger),
9
+ # (which is the case if you're using Phusion Passenger or puma clustered mode),
8
10
  # then this means that Rails server process instances won't be able
9
11
  # to share cache data with each other and this may not be the most
10
12
  # appropriate cache in that scenario.
@@ -14,13 +16,41 @@ module ActiveSupport
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,21 +58,27 @@ 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
+
66
+ # Delete all data stored in a given cache store.
31
67
  def clear(options = nil)
32
68
  synchronize do
33
69
  @data.clear
34
- @key_access.clear
35
70
  @cache_size = 0
36
71
  end
37
72
  end
38
73
 
74
+ # Preemptively iterates through all stored keys and removes the ones which have expired.
39
75
  def cleanup(options = nil)
40
76
  options = merged_options(options)
41
- instrument(:cleanup, :size => @data.size) do
42
- keys = synchronize{ @data.keys }
77
+ instrument(:cleanup, size: @data.size) do
78
+ keys = synchronize { @data.keys }
43
79
  keys.each do |key|
44
80
  entry = @data[key]
45
- delete_entry(key, options) if entry && entry.expired?
81
+ delete_entry(key, **options) if entry && entry.expired?
46
82
  end
47
83
  end
48
84
  end
@@ -53,13 +89,13 @@ module ActiveSupport
53
89
  return if pruning?
54
90
  @pruning = true
55
91
  begin
56
- start_time = Time.now
92
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
57
93
  cleanup
58
- instrument(:prune, target_size, :from => @cache_size) do
59
- keys = synchronize{ @key_access.keys.sort{|a,b| @key_access[a].to_f <=> @key_access[b].to_f} }
94
+ instrument(:prune, target_size, from: @cache_size) do
95
+ keys = synchronize { @data.keys }
60
96
  keys.each do |key|
61
- delete_entry(key, options)
62
- 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)
63
99
  end
64
100
  end
65
101
  ensure
@@ -74,45 +110,28 @@ module ActiveSupport
74
110
 
75
111
  # Increment an integer value in the cache.
76
112
  def increment(name, amount = 1, options = nil)
77
- synchronize do
78
- options = merged_options(options)
79
- if num = read(name, options)
80
- num = num.to_i + amount
81
- write(name, num, options)
82
- num
83
- else
84
- nil
85
- end
86
- end
113
+ modify_value(name, amount, options)
87
114
  end
88
115
 
89
116
  # Decrement an integer value in the cache.
90
117
  def decrement(name, amount = 1, options = nil)
91
- synchronize do
92
- options = merged_options(options)
93
- if num = read(name, options)
94
- num = num.to_i - amount
95
- write(name, num, options)
96
- num
97
- else
98
- nil
99
- end
100
- end
118
+ modify_value(name, -amount, options)
101
119
  end
102
120
 
121
+ # Deletes cache entries if the cache key matches a given pattern.
103
122
  def delete_matched(matcher, options = nil)
104
123
  options = merged_options(options)
105
124
  instrument(:delete_matched, matcher.inspect) do
106
125
  matcher = key_matcher(matcher, options)
107
126
  keys = synchronize { @data.keys }
108
127
  keys.each do |key|
109
- delete_entry(key, options) if key.match(matcher)
128
+ delete_entry(key, **options) if key.match(matcher)
110
129
  end
111
130
  end
112
131
  end
113
132
 
114
133
  def inspect # :nodoc:
115
- "<##{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}>"
116
135
  end
117
136
 
118
137
  # Synchronize calls to the cache. This should be called wherever the underlying cache implementation
@@ -121,49 +140,62 @@ module ActiveSupport
121
140
  @monitor.synchronize(&block)
122
141
  end
123
142
 
124
- protected
125
-
143
+ private
126
144
  PER_ENTRY_OVERHEAD = 240
127
145
 
128
- def cached_size(key, entry)
129
- key.to_s.bytesize + entry.size + PER_ENTRY_OVERHEAD
146
+ def default_coder
147
+ DupCoder
148
+ end
149
+
150
+ def cached_size(key, payload)
151
+ key.to_s.bytesize + payload.bytesize + PER_ENTRY_OVERHEAD
130
152
  end
131
153
 
132
- def read_entry(key, options) # :nodoc:
133
- entry = @data[key]
154
+ def read_entry(key, **options)
155
+ entry = nil
134
156
  synchronize do
135
- if entry
136
- @key_access[key] = Time.now.to_f
137
- else
138
- @key_access.delete(key)
157
+ payload = @data.delete(key)
158
+ if payload
159
+ @data[key] = payload
160
+ entry = deserialize_entry(payload)
139
161
  end
140
162
  end
141
163
  entry
142
164
  end
143
165
 
144
- def write_entry(key, entry, options) # :nodoc:
145
- entry.dup_value!
166
+ def write_entry(key, entry, **options)
167
+ payload = serialize_entry(entry, **options)
146
168
  synchronize do
147
- old_entry = @data[key]
148
- return false if @data.key?(key) && options[:unless_exist]
149
- if old_entry
150
- @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)
151
174
  else
152
- @cache_size += cached_size(key, entry)
175
+ @cache_size += cached_size(key, payload)
153
176
  end
154
- @key_access[key] = Time.now.to_f
155
- @data[key] = entry
177
+ @data[key] = payload
156
178
  prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
157
179
  true
158
180
  end
159
181
  end
160
182
 
161
- def delete_entry(key, options) # :nodoc:
183
+ def delete_entry(key, **options)
162
184
  synchronize do
163
- @key_access.delete(key)
164
- entry = @data.delete(key)
165
- @cache_size -= cached_size(key, entry) if entry
166
- !!entry
185
+ payload = @data.delete(key)
186
+ @cache_size -= cached_size(key, payload) if payload
187
+ !!payload
188
+ end
189
+ end
190
+
191
+ def modify_value(name, amount, options)
192
+ options = merged_options(options)
193
+ synchronize do
194
+ if num = read(name, options)
195
+ num = num.to_i + amount
196
+ write(name, num, options)
197
+ num
198
+ end
167
199
  end
168
200
  end
169
201
  end