activesupport 5.1.7 → 5.2.4.3

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

Potentially problematic release.


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

Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +401 -541
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_support.rb +5 -13
  6. data/lib/active_support/all.rb +2 -0
  7. data/lib/active_support/array_inquirer.rb +2 -0
  8. data/lib/active_support/backtrace_cleaner.rb +2 -0
  9. data/lib/active_support/benchmarkable.rb +2 -0
  10. data/lib/active_support/builder.rb +2 -0
  11. data/lib/active_support/cache.rb +197 -83
  12. data/lib/active_support/cache/file_store.rb +5 -4
  13. data/lib/active_support/cache/mem_cache_store.rb +39 -39
  14. data/lib/active_support/cache/memory_store.rb +2 -0
  15. data/lib/active_support/cache/null_store.rb +2 -0
  16. data/lib/active_support/cache/redis_cache_store.rb +466 -0
  17. data/lib/active_support/cache/strategy/local_cache.rb +33 -2
  18. data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
  19. data/lib/active_support/callbacks.rb +28 -39
  20. data/lib/active_support/concern.rb +10 -4
  21. data/lib/active_support/concurrency/share_lock.rb +2 -0
  22. data/lib/active_support/configurable.rb +2 -0
  23. data/lib/active_support/core_ext.rb +3 -1
  24. data/lib/active_support/core_ext/array.rb +2 -0
  25. data/lib/active_support/core_ext/array/access.rb +4 -2
  26. data/lib/active_support/core_ext/array/conversions.rb +2 -0
  27. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  28. data/lib/active_support/core_ext/array/grouping.rb +2 -0
  29. data/lib/active_support/core_ext/array/inquiry.rb +2 -0
  30. data/lib/active_support/core_ext/array/prepend_and_append.rb +4 -2
  31. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  32. data/lib/active_support/core_ext/benchmark.rb +2 -0
  33. data/lib/active_support/core_ext/big_decimal.rb +2 -0
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +2 -0
  35. data/lib/active_support/core_ext/class.rb +2 -0
  36. data/lib/active_support/core_ext/class/attribute.rb +34 -16
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
  38. data/lib/active_support/core_ext/class/subclasses.rb +1 -2
  39. data/lib/active_support/core_ext/date.rb +2 -0
  40. data/lib/active_support/core_ext/date/acts_like.rb +2 -0
  41. data/lib/active_support/core_ext/date/blank.rb +2 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +2 -0
  43. data/lib/active_support/core_ext/date/conversions.rb +10 -9
  44. data/lib/active_support/core_ext/date/zones.rb +2 -0
  45. data/lib/active_support/core_ext/date_and_time/calculations.rb +50 -16
  46. data/lib/active_support/core_ext/date_and_time/compatibility.rb +3 -1
  47. data/lib/active_support/core_ext/date_and_time/zones.rb +2 -0
  48. data/lib/active_support/core_ext/date_time.rb +2 -0
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
  50. data/lib/active_support/core_ext/date_time/blank.rb +2 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +2 -0
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
  53. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  54. data/lib/active_support/core_ext/digest.rb +3 -0
  55. data/lib/active_support/core_ext/digest/uuid.rb +3 -1
  56. data/lib/active_support/core_ext/enumerable.rb +8 -1
  57. data/lib/active_support/core_ext/file.rb +2 -0
  58. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  59. data/lib/active_support/core_ext/hash.rb +2 -0
  60. data/lib/active_support/core_ext/hash/compact.rb +2 -0
  61. data/lib/active_support/core_ext/hash/conversions.rb +4 -2
  62. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  63. data/lib/active_support/core_ext/hash/except.rb +2 -0
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +2 -0
  65. data/lib/active_support/core_ext/hash/keys.rb +2 -0
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +4 -4
  68. data/lib/active_support/core_ext/hash/transform_values.rb +2 -0
  69. data/lib/active_support/core_ext/integer.rb +2 -0
  70. data/lib/active_support/core_ext/integer/inflections.rb +2 -0
  71. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  72. data/lib/active_support/core_ext/integer/time.rb +7 -14
  73. data/lib/active_support/core_ext/kernel.rb +2 -0
  74. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  75. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  76. data/lib/active_support/core_ext/kernel/reporting.rb +2 -0
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  78. data/lib/active_support/core_ext/load_error.rb +2 -7
  79. data/lib/active_support/core_ext/marshal.rb +2 -0
  80. data/lib/active_support/core_ext/module.rb +3 -0
  81. data/lib/active_support/core_ext/module/aliasing.rb +2 -0
  82. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  83. data/lib/active_support/core_ext/module/attr_internal.rb +2 -0
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +21 -24
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +2 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +7 -8
  87. data/lib/active_support/core_ext/module/delegation.rb +31 -29
  88. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  89. data/lib/active_support/core_ext/module/introspection.rb +2 -0
  90. data/lib/active_support/core_ext/module/reachable.rb +3 -0
  91. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  93. data/lib/active_support/core_ext/name_error.rb +7 -0
  94. data/lib/active_support/core_ext/numeric.rb +2 -0
  95. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +9 -7
  97. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +7 -15
  99. data/lib/active_support/core_ext/object.rb +2 -0
  100. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  101. data/lib/active_support/core_ext/object/blank.rb +12 -1
  102. data/lib/active_support/core_ext/object/conversions.rb +2 -0
  103. data/lib/active_support/core_ext/object/deep_dup.rb +2 -0
  104. data/lib/active_support/core_ext/object/duplicable.rb +10 -8
  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 +8 -0
  108. data/lib/active_support/core_ext/object/to_param.rb +2 -0
  109. data/lib/active_support/core_ext/object/to_query.rb +2 -0
  110. data/lib/active_support/core_ext/object/try.rb +2 -0
  111. data/lib/active_support/core_ext/object/with_options.rb +3 -1
  112. data/lib/active_support/core_ext/range.rb +4 -1
  113. data/lib/active_support/core_ext/range/compare_range.rb +61 -0
  114. data/lib/active_support/core_ext/range/conversions.rb +9 -1
  115. data/lib/active_support/core_ext/range/each.rb +5 -1
  116. data/lib/active_support/core_ext/range/include_range.rb +2 -22
  117. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  118. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  119. data/lib/active_support/core_ext/regexp.rb +2 -0
  120. data/lib/active_support/core_ext/securerandom.rb +2 -0
  121. data/lib/active_support/core_ext/string.rb +2 -0
  122. data/lib/active_support/core_ext/string/access.rb +2 -0
  123. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  124. data/lib/active_support/core_ext/string/conversions.rb +2 -0
  125. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  126. data/lib/active_support/core_ext/string/filters.rb +2 -0
  127. data/lib/active_support/core_ext/string/indent.rb +2 -0
  128. data/lib/active_support/core_ext/string/inflections.rb +26 -12
  129. data/lib/active_support/core_ext/string/inquiry.rb +2 -0
  130. data/lib/active_support/core_ext/string/multibyte.rb +4 -0
  131. data/lib/active_support/core_ext/string/output_safety.rb +6 -7
  132. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  133. data/lib/active_support/core_ext/string/strip.rb +2 -0
  134. data/lib/active_support/core_ext/string/zones.rb +2 -0
  135. data/lib/active_support/core_ext/time.rb +2 -0
  136. data/lib/active_support/core_ext/time/acts_like.rb +2 -0
  137. data/lib/active_support/core_ext/time/calculations.rb +23 -15
  138. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  139. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  140. data/lib/active_support/core_ext/time/zones.rb +6 -4
  141. data/lib/active_support/core_ext/uri.rb +6 -6
  142. data/lib/active_support/current_attributes.rb +195 -0
  143. data/lib/active_support/dependencies.rb +25 -26
  144. data/lib/active_support/dependencies/autoload.rb +2 -0
  145. data/lib/active_support/dependencies/interlock.rb +2 -0
  146. data/lib/active_support/deprecation.rb +4 -2
  147. data/lib/active_support/deprecation/behaviors.rb +28 -9
  148. data/lib/active_support/deprecation/constant_accessor.rb +4 -2
  149. data/lib/active_support/deprecation/instance_delegator.rb +2 -0
  150. data/lib/active_support/deprecation/method_wrappers.rb +30 -17
  151. data/lib/active_support/deprecation/proxy_wrappers.rb +5 -2
  152. data/lib/active_support/deprecation/reporting.rb +5 -3
  153. data/lib/active_support/descendants_tracker.rb +2 -0
  154. data/lib/active_support/digest.rb +20 -0
  155. data/lib/active_support/duration.rb +11 -7
  156. data/lib/active_support/duration/iso8601_parser.rb +4 -2
  157. data/lib/active_support/duration/iso8601_serializer.rb +4 -2
  158. data/lib/active_support/encrypted_configuration.rb +49 -0
  159. data/lib/active_support/encrypted_file.rb +99 -0
  160. data/lib/active_support/evented_file_update_checker.rb +2 -0
  161. data/lib/active_support/execution_wrapper.rb +2 -0
  162. data/lib/active_support/executor.rb +2 -0
  163. data/lib/active_support/file_update_checker.rb +2 -0
  164. data/lib/active_support/gem_version.rb +5 -3
  165. data/lib/active_support/gzip.rb +2 -0
  166. data/lib/active_support/hash_with_indifferent_access.rb +55 -1
  167. data/lib/active_support/i18n.rb +3 -1
  168. data/lib/active_support/i18n_railtie.rb +4 -6
  169. data/lib/active_support/inflections.rb +2 -0
  170. data/lib/active_support/inflector.rb +2 -0
  171. data/lib/active_support/inflector/inflections.rb +20 -4
  172. data/lib/active_support/inflector/methods.rb +43 -24
  173. data/lib/active_support/inflector/transliterate.rb +17 -8
  174. data/lib/active_support/json.rb +2 -0
  175. data/lib/active_support/json/decoding.rb +2 -0
  176. data/lib/active_support/json/encoding.rb +2 -0
  177. data/lib/active_support/key_generator.rb +3 -1
  178. data/lib/active_support/lazy_load_hooks.rb +2 -0
  179. data/lib/active_support/log_subscriber.rb +3 -2
  180. data/lib/active_support/log_subscriber/test_helper.rb +2 -0
  181. data/lib/active_support/logger.rb +2 -0
  182. data/lib/active_support/logger_silence.rb +3 -2
  183. data/lib/active_support/logger_thread_safe_level.rb +4 -1
  184. data/lib/active_support/message_encryptor.rb +95 -22
  185. data/lib/active_support/message_verifier.rb +78 -7
  186. data/lib/active_support/messages/metadata.rb +71 -0
  187. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  188. data/lib/active_support/messages/rotator.rb +56 -0
  189. data/lib/active_support/multibyte.rb +2 -0
  190. data/lib/active_support/multibyte/chars.rb +2 -0
  191. data/lib/active_support/multibyte/unicode.rb +4 -2
  192. data/lib/active_support/notifications.rb +2 -0
  193. data/lib/active_support/notifications/fanout.rb +4 -2
  194. data/lib/active_support/notifications/instrumenter.rb +2 -0
  195. data/lib/active_support/number_helper.rb +2 -0
  196. data/lib/active_support/number_helper/number_converter.rb +2 -0
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -0
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +2 -0
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +2 -0
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -0
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +2 -0
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -1
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +2 -20
  204. data/lib/active_support/number_helper/rounding_helper.rb +6 -4
  205. data/lib/active_support/option_merger.rb +2 -0
  206. data/lib/active_support/ordered_hash.rb +2 -0
  207. data/lib/active_support/ordered_options.rb +5 -3
  208. data/lib/active_support/per_thread_registry.rb +2 -0
  209. data/lib/active_support/proxy_object.rb +2 -0
  210. data/lib/active_support/rails.rb +2 -0
  211. data/lib/active_support/railtie.rb +37 -8
  212. data/lib/active_support/reloader.rb +7 -5
  213. data/lib/active_support/rescuable.rb +3 -2
  214. data/lib/active_support/security_utils.rb +15 -11
  215. data/lib/active_support/string_inquirer.rb +2 -0
  216. data/lib/active_support/subscriber.rb +8 -2
  217. data/lib/active_support/tagged_logging.rb +2 -0
  218. data/lib/active_support/test_case.rb +3 -2
  219. data/lib/active_support/testing/assertions.rb +31 -14
  220. data/lib/active_support/testing/autorun.rb +2 -0
  221. data/lib/active_support/testing/constant_lookup.rb +2 -0
  222. data/lib/active_support/testing/declarative.rb +2 -0
  223. data/lib/active_support/testing/deprecation.rb +2 -0
  224. data/lib/active_support/testing/file_fixtures.rb +2 -0
  225. data/lib/active_support/testing/isolation.rb +3 -1
  226. data/lib/active_support/testing/method_call_assertions.rb +2 -0
  227. data/lib/active_support/testing/setup_and_teardown.rb +12 -7
  228. data/lib/active_support/testing/stream.rb +2 -0
  229. data/lib/active_support/testing/tagged_logging.rb +2 -0
  230. data/lib/active_support/testing/time_helpers.rb +33 -3
  231. data/lib/active_support/time.rb +2 -0
  232. data/lib/active_support/time_with_zone.rb +38 -0
  233. data/lib/active_support/values/time_zone.rb +20 -8
  234. data/lib/active_support/version.rb +2 -0
  235. data/lib/active_support/xml_mini.rb +4 -2
  236. data/lib/active_support/xml_mini/jdom.rb +4 -2
  237. data/lib/active_support/xml_mini/libxml.rb +3 -1
  238. data/lib/active_support/xml_mini/libxmlsax.rb +4 -2
  239. data/lib/active_support/xml_mini/nokogiri.rb +3 -1
  240. data/lib/active_support/xml_mini/nokogirisax.rb +3 -1
  241. data/lib/active_support/xml_mini/rexml.rb +3 -1
  242. metadata +17 -5
@@ -1,4 +1,4 @@
1
- Copyright (c) 2005-2017 David Heinemeier Hansson
1
+ Copyright (c) 2005-2018 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -14,14 +14,14 @@ The latest version of Active Support can be installed with RubyGems:
14
14
 
15
15
  Source code can be downloaded as part of the Rails project on GitHub:
16
16
 
17
- * https://github.com/rails/rails/tree/master/activesupport
17
+ * https://github.com/rails/rails/tree/5-2-stable/activesupport
18
18
 
19
19
 
20
20
  == License
21
21
 
22
22
  Active Support is released under the MIT license:
23
23
 
24
- * http://www.opensource.org/licenses/MIT
24
+ * https://opensource.org/licenses/MIT
25
25
 
26
26
 
27
27
  == Support
@@ -30,7 +30,7 @@ API documentation is at:
30
30
 
31
31
  * http://api.rubyonrails.org
32
32
 
33
- Bug reports can be filed for the Ruby on Rails project here:
33
+ Bug reports for the Ruby on Rails project can be filed here:
34
34
 
35
35
  * https://github.com/rails/rails/issues
36
36
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #--
2
- # Copyright (c) 2005-2017 David Heinemeier Hansson
4
+ # Copyright (c) 2005-2018 David Heinemeier Hansson
3
5
  #
4
6
  # Permission is hereby granted, free of charge, to any person obtaining
5
7
  # a copy of this software and associated documentation files (the
@@ -32,6 +34,7 @@ module ActiveSupport
32
34
  extend ActiveSupport::Autoload
33
35
 
34
36
  autoload :Concern
37
+ autoload :CurrentAttributes
35
38
  autoload :Dependencies
36
39
  autoload :DescendantsTracker
37
40
  autoload :ExecutionWrapper
@@ -50,6 +53,7 @@ module ActiveSupport
50
53
  autoload :Callbacks
51
54
  autoload :Configurable
52
55
  autoload :Deprecation
56
+ autoload :Digest
53
57
  autoload :Gzip
54
58
  autoload :Inflector
55
59
  autoload :JSON
@@ -79,18 +83,6 @@ module ActiveSupport
79
83
 
80
84
  cattr_accessor :test_order # :nodoc:
81
85
 
82
- def self.halt_callback_chains_on_return_false
83
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
84
- ActiveSupport.halt_callback_chains_on_return_false is deprecated and will be removed in Rails 5.2.
85
- MSG
86
- end
87
-
88
- def self.halt_callback_chains_on_return_false=(value)
89
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
90
- ActiveSupport.halt_callback_chains_on_return_false= is deprecated and will be removed in Rails 5.2.
91
- MSG
92
- end
93
-
94
86
  def self.to_time_preserves_timezone
95
87
  DateAndTime::Compatibility.preserve_timezone
96
88
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support"
2
4
  require "active_support/time"
3
5
  require "active_support/core_ext"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
4
  # Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
3
5
  # its string-like contents:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
4
  # Backtraces often include many lines that are not relevant for the context
3
5
  # under review. This makes it hard to find the signal amongst the backtrace
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/benchmark"
2
4
  require "active_support/core_ext/hash/keys"
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require "builder"
3
5
  rescue LoadError => e
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "zlib"
2
4
  require "active_support/core_ext/array/extract_options"
3
5
  require "active_support/core_ext/array/wrap"
@@ -10,10 +12,11 @@ require "active_support/core_ext/string/inflections"
10
12
  module ActiveSupport
11
13
  # See ActiveSupport::Cache::Store for documentation.
12
14
  module Cache
13
- autoload :FileStore, "active_support/cache/file_store"
14
- autoload :MemoryStore, "active_support/cache/memory_store"
15
- autoload :MemCacheStore, "active_support/cache/mem_cache_store"
16
- autoload :NullStore, "active_support/cache/null_store"
15
+ autoload :FileStore, "active_support/cache/file_store"
16
+ autoload :MemoryStore, "active_support/cache/memory_store"
17
+ autoload :MemCacheStore, "active_support/cache/mem_cache_store"
18
+ autoload :NullStore, "active_support/cache/null_store"
19
+ autoload :RedisCacheStore, "active_support/cache/redis_cache_store"
17
20
 
18
21
  # These options mean something to all cache implementations. Individual cache
19
22
  # implementations may support additional options.
@@ -75,7 +78,7 @@ module ActiveSupport
75
78
  #
76
79
  # The +key+ argument can also respond to +cache_key+ or +to_param+.
77
80
  def expand_cache_key(key, namespace = nil)
78
- expanded_cache_key = namespace ? "#{namespace}/" : ""
81
+ expanded_cache_key = (namespace ? "#{namespace}/" : "").dup
79
82
 
80
83
  if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
81
84
  expanded_cache_key << "#{prefix}/"
@@ -88,16 +91,19 @@ module ActiveSupport
88
91
  private
89
92
  def retrieve_cache_key(key)
90
93
  case
91
- when key.respond_to?(:cache_key) then key.cache_key
92
- when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
93
- when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
94
- else key.to_param
94
+ when key.respond_to?(:cache_key_with_version) then key.cache_key_with_version
95
+ when key.respond_to?(:cache_key) then key.cache_key
96
+ when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
97
+ when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
98
+ else key.to_param
95
99
  end.to_s
96
100
  end
97
101
 
98
102
  # Obtains the specified cache store class, given the name of the +store+.
99
103
  # Raises an error when the store class cannot be found.
100
104
  def retrieve_store_class(store)
105
+ # require_relative cannot be used here because the class might be
106
+ # provided by another gem, like redis-activesupport for example.
101
107
  require "active_support/cache/#{store}"
102
108
  rescue LoadError => e
103
109
  raise "Could not find cache store adapter for #{store} (#{e})"
@@ -143,18 +149,34 @@ module ActiveSupport
143
149
  # cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
144
150
  # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
145
151
  #
146
- # Caches can also store values in a compressed format to save space and
147
- # reduce time spent sending data. Since there is overhead, values must be
148
- # large enough to warrant compression. To turn on compression either pass
149
- # <tt>compress: true</tt> in the initializer or as an option to +fetch+
150
- # or +write+. To specify the threshold at which to compress values, set the
151
- # <tt>:compress_threshold</tt> option. The default threshold is 16K.
152
+ # Cached data larger than 1kB are compressed by default. To turn off
153
+ # compression, pass <tt>compress: false</tt> to the initializer or to
154
+ # individual +fetch+ or +write+ method calls. The 1kB compression
155
+ # threshold is configurable with the <tt>:compress_threshold</tt> option,
156
+ # specified in bytes.
152
157
  class Store
153
158
  cattr_accessor :logger, instance_writer: true
154
159
 
155
160
  attr_reader :silence, :options
156
161
  alias :silence? :silence
157
162
 
163
+ class << self
164
+ private
165
+ def retrieve_pool_options(options)
166
+ {}.tap do |pool_options|
167
+ pool_options[:size] = options.delete(:pool_size) if options[:pool_size]
168
+ pool_options[:timeout] = options.delete(:pool_timeout) if options[:pool_timeout]
169
+ end
170
+ end
171
+
172
+ def ensure_connection_pool_added!
173
+ require "connection_pool"
174
+ rescue LoadError => e
175
+ $stderr.puts "You don't have connection_pool installed in your application. Please add it to your Gemfile and run bundle install"
176
+ raise e
177
+ end
178
+ end
179
+
158
180
  # Creates a new cache. The options will be passed to any write method calls
159
181
  # except for <tt>:namespace</tt> which can be used to set the global
160
182
  # namespace for the cache.
@@ -207,8 +229,7 @@ module ActiveSupport
207
229
  # ask whether you should force a cache write. Otherwise, it's clearer to
208
230
  # just call <tt>Cache#write</tt>.
209
231
  #
210
- # Setting <tt>:compress</tt> will store a large cache entry set by the call
211
- # in a compressed format.
232
+ # Setting <tt>compress: false</tt> disables compression of the cache entry.
212
233
  #
213
234
  # Setting <tt>:expires_in</tt> will set an expiration time on the cache.
214
235
  # All caches support auto-expiring content after a specified number of
@@ -219,6 +240,10 @@ module ActiveSupport
219
240
  # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
220
241
  # cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
221
242
  #
243
+ # Setting <tt>:version</tt> verifies the cache stored under <tt>name</tt>
244
+ # is of the same version. nil is returned on mismatches despite contents.
245
+ # This feature is used to support recyclable cache keys.
246
+ #
222
247
  # Setting <tt>:race_condition_ttl</tt> is very useful in situations where
223
248
  # a cache entry is used very frequently and is under heavy load. If a
224
249
  # cache expires and due to heavy load several different processes will try
@@ -287,6 +312,7 @@ module ActiveSupport
287
312
  instrument(:read, name, options) do |payload|
288
313
  cached_entry = read_entry(key, options) unless options[:force]
289
314
  entry = handle_expired_entry(cached_entry, key, options)
315
+ entry = nil if entry && entry.mismatched?(normalize_version(name, options))
290
316
  payload[:super_operation] = :fetch if payload
291
317
  payload[:hit] = !!entry if payload
292
318
  end
@@ -303,21 +329,30 @@ module ActiveSupport
303
329
  end
304
330
  end
305
331
 
306
- # Fetches data from the cache, using the given key. If there is data in
332
+ # Reads data from the cache, using the given key. If there is data in
307
333
  # the cache with the given key, then that data is returned. Otherwise,
308
334
  # +nil+ is returned.
309
335
  #
336
+ # Note, if data was written with the <tt>:expires_in<tt> or <tt>:version</tt> options,
337
+ # both of these conditions are applied before the data is returned.
338
+ #
310
339
  # Options are passed to the underlying cache implementation.
311
340
  def read(name, options = nil)
312
341
  options = merged_options(options)
313
- key = normalize_key(name, options)
342
+ key = normalize_key(name, options)
343
+ version = normalize_version(name, options)
344
+
314
345
  instrument(:read, name, options) do |payload|
315
346
  entry = read_entry(key, options)
347
+
316
348
  if entry
317
349
  if entry.expired?
318
350
  delete_entry(key, options)
319
351
  payload[:hit] = false if payload
320
352
  nil
353
+ elsif entry.mismatched?(version)
354
+ payload[:hit] = false if payload
355
+ nil
321
356
  else
322
357
  payload[:hit] = true if payload
323
358
  entry.value
@@ -339,19 +374,24 @@ module ActiveSupport
339
374
  options = names.extract_options!
340
375
  options = merged_options(options)
341
376
 
342
- results = {}
343
- names.each do |name|
344
- key = normalize_key(name, options)
345
- entry = read_entry(key, options)
346
- if entry
347
- if entry.expired?
348
- delete_entry(key, options)
349
- else
350
- results[name] = entry.value
351
- end
377
+ instrument :read_multi, names, options do |payload|
378
+ read_multi_entries(names, options).tap do |results|
379
+ payload[:hits] = results.keys
352
380
  end
353
381
  end
354
- results
382
+ end
383
+
384
+ # Cache Storage API to write multiple values at once.
385
+ def write_multi(hash, options = nil)
386
+ options = merged_options(options)
387
+
388
+ instrument :write_multi, hash, options do |payload|
389
+ entries = hash.each_with_object({}) do |(name, value), memo|
390
+ memo[normalize_key(name, options)] = Entry.new(value, options.merge(version: normalize_version(name, options)))
391
+ end
392
+
393
+ write_multi_entries entries, options
394
+ end
355
395
  end
356
396
 
357
397
  # Fetches data from the cache, using the given keys. If there is data in
@@ -378,13 +418,19 @@ module ActiveSupport
378
418
 
379
419
  options = names.extract_options!
380
420
  options = merged_options(options)
381
- results = read_multi(*names, options)
382
421
 
383
- names.each_with_object({}) do |name, memo|
384
- memo[name] = results.fetch(name) do
385
- value = yield name
386
- write(name, value, options)
387
- value
422
+ instrument :read_multi, names, options do |payload|
423
+ read_multi_entries(names, options).tap do |results|
424
+ payload[:hits] = results.keys
425
+ payload[:super_operation] = :fetch_multi
426
+
427
+ writes = {}
428
+
429
+ (names - results.keys).each do |name|
430
+ results[name] = writes[name] = yield(name)
431
+ end
432
+
433
+ write_multi writes, options
388
434
  end
389
435
  end
390
436
  end
@@ -396,7 +442,7 @@ module ActiveSupport
396
442
  options = merged_options(options)
397
443
 
398
444
  instrument(:write, name, options) do
399
- entry = Entry.new(value, options)
445
+ entry = Entry.new(value, options.merge(version: normalize_version(name, options)))
400
446
  write_entry(normalize_key(name, options), entry, options)
401
447
  end
402
448
  end
@@ -420,7 +466,7 @@ module ActiveSupport
420
466
 
421
467
  instrument(:exist?, name) do
422
468
  entry = read_entry(normalize_key(name, options), options)
423
- (entry && !entry.expired?) || false
469
+ (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
424
470
  end
425
471
  end
426
472
 
@@ -502,6 +548,36 @@ module ActiveSupport
502
548
  raise NotImplementedError.new
503
549
  end
504
550
 
551
+ # Reads multiple entries from the cache implementation. Subclasses MAY
552
+ # implement this method.
553
+ def read_multi_entries(names, options)
554
+ results = {}
555
+ names.each do |name|
556
+ key = normalize_key(name, options)
557
+ version = normalize_version(name, options)
558
+ entry = read_entry(key, options)
559
+
560
+ if entry
561
+ if entry.expired?
562
+ delete_entry(key, options)
563
+ elsif entry.mismatched?(version)
564
+ # Skip mismatched versions
565
+ else
566
+ results[name] = entry.value
567
+ end
568
+ end
569
+ end
570
+ results
571
+ end
572
+
573
+ # Writes multiple entries to the cache implementation. Subclasses MAY
574
+ # implement this method.
575
+ def write_multi_entries(hash, options)
576
+ hash.each do |key, entry|
577
+ write_entry key, entry, options
578
+ end
579
+ end
580
+
505
581
  # Deletes an entry from the cache implementation. Subclasses must
506
582
  # implement this method.
507
583
  def delete_entry(key, options)
@@ -517,6 +593,36 @@ module ActiveSupport
517
593
  end
518
594
  end
519
595
 
596
+ # Expands and namespaces the cache key. May be overridden by
597
+ # cache stores to do additional normalization.
598
+ def normalize_key(key, options = nil)
599
+ namespace_key expanded_key(key), options
600
+ end
601
+
602
+ # Prefix the key with a namespace string:
603
+ #
604
+ # namespace_key 'foo', namespace: 'cache'
605
+ # # => 'cache:foo'
606
+ #
607
+ # With a namespace block:
608
+ #
609
+ # namespace_key 'foo', namespace: -> { 'cache' }
610
+ # # => 'cache:foo'
611
+ def namespace_key(key, options = nil)
612
+ options = merged_options(options)
613
+ namespace = options[:namespace]
614
+
615
+ if namespace.respond_to?(:call)
616
+ namespace = namespace.call
617
+ end
618
+
619
+ if namespace
620
+ "#{namespace}:#{key}"
621
+ else
622
+ key
623
+ end
624
+ end
625
+
520
626
  # Expands key to be a consistent string value. Invokes +cache_key+ if
521
627
  # object responds to +cache_key+. Otherwise, +to_param+ method will be
522
628
  # called. If the key is a Hash, then keys will be sorted alphabetically.
@@ -537,14 +643,16 @@ module ActiveSupport
537
643
  key.to_param
538
644
  end
539
645
 
540
- # Prefixes a key with the namespace. Namespace and key will be delimited
541
- # with a colon.
542
- def normalize_key(key, options)
543
- key = expanded_key(key)
544
- namespace = options[:namespace] if options
545
- prefix = namespace.is_a?(Proc) ? namespace.call : namespace
546
- key = "#{prefix}:#{key}" if prefix
547
- key
646
+ def normalize_version(key, options = nil)
647
+ (options && options[:version].try(:to_param)) || expanded_version(key)
648
+ end
649
+
650
+ def expanded_version(key)
651
+ case
652
+ when key.respond_to?(:cache_version) then key.cache_version.to_param
653
+ when key.is_a?(Array) then key.map { |element| expanded_version(element) }.compact.to_param
654
+ when key.respond_to?(:to_a) then expanded_version(key.to_a)
655
+ end
548
656
  end
549
657
 
550
658
  def instrument(operation, key, options = nil)
@@ -591,34 +699,37 @@ module ActiveSupport
591
699
  end
592
700
  end
593
701
 
594
- # This class is used to represent cache entries. Cache entries have a value and an optional
595
- # expiration time. The expiration time is used to support the :race_condition_ttl option
596
- # on the cache.
702
+ # This class is used to represent cache entries. Cache entries have a value, an optional
703
+ # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
704
+ # on the cache. The version is used to support the :version option on the cache for rejecting
705
+ # mismatches.
597
706
  #
598
707
  # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
599
708
  # using short instance variable names that are lazily defined.
600
709
  class Entry # :nodoc:
601
- DEFAULT_COMPRESS_LIMIT = 16.kilobytes
710
+ attr_reader :version
602
711
 
603
- # Creates a new cache entry for the specified value. Options supported are
604
- # +:compress+, +:compress_threshold+, and +:expires_in+.
605
- def initialize(value, options = {})
606
- if should_compress?(value, options)
607
- @value = compress(value)
608
- @compressed = true
609
- else
610
- @value = value
611
- end
712
+ DEFAULT_COMPRESS_LIMIT = 1.kilobyte
612
713
 
714
+ # Creates a new cache entry for the specified value. Options supported are
715
+ # +:compress+, +:compress_threshold+, +:version+ and +:expires_in+.
716
+ def initialize(value, compress: true, compress_threshold: DEFAULT_COMPRESS_LIMIT, version: nil, expires_in: nil, **)
717
+ @value = value
718
+ @version = version
613
719
  @created_at = Time.now.to_f
614
- @expires_in = options[:expires_in]
615
- @expires_in = @expires_in.to_f if @expires_in
720
+ @expires_in = expires_in && expires_in.to_f
721
+
722
+ compress!(compress_threshold) if compress
616
723
  end
617
724
 
618
725
  def value
619
726
  compressed? ? uncompress(@value) : @value
620
727
  end
621
728
 
729
+ def mismatched?(version)
730
+ @version && version && @version != version
731
+ end
732
+
622
733
  # Checks if the entry is expired. The +expires_in+ parameter can override
623
734
  # the value set when the entry was created.
624
735
  def expired?
@@ -640,17 +751,13 @@ module ActiveSupport
640
751
  # Returns the size of the cached value. This could be less than
641
752
  # <tt>value.size</tt> if the data is compressed.
642
753
  def size
643
- if defined?(@s)
644
- @s
754
+ case value
755
+ when NilClass
756
+ 0
757
+ when String
758
+ @value.bytesize
645
759
  else
646
- case value
647
- when NilClass
648
- 0
649
- when String
650
- @value.bytesize
651
- else
652
- @s = Marshal.dump(@value).bytesize
653
- end
760
+ @s ||= Marshal.dump(@value).bytesize
654
761
  end
655
762
  end
656
763
 
@@ -667,23 +774,30 @@ module ActiveSupport
667
774
  end
668
775
 
669
776
  private
670
- def should_compress?(value, options)
671
- if value && options[:compress]
672
- compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
673
- serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
674
-
675
- return true if serialized_value_size >= compress_threshold
777
+ def compress!(compress_threshold)
778
+ case @value
779
+ when nil, true, false, Numeric
780
+ uncompressed_size = 0
781
+ when String
782
+ uncompressed_size = @value.bytesize
783
+ else
784
+ serialized = Marshal.dump(@value)
785
+ uncompressed_size = serialized.bytesize
676
786
  end
677
787
 
678
- false
679
- end
788
+ if uncompressed_size >= compress_threshold
789
+ serialized ||= Marshal.dump(@value)
790
+ compressed = Zlib::Deflate.deflate(serialized)
680
791
 
681
- def compressed?
682
- defined?(@compressed) ? @compressed : false
792
+ if compressed.bytesize < uncompressed_size
793
+ @value = compressed
794
+ @compressed = true
795
+ end
796
+ end
683
797
  end
684
798
 
685
- def compress(value)
686
- Zlib::Deflate.deflate(Marshal.dump(value))
799
+ def compressed?
800
+ defined?(@compressed)
687
801
  end
688
802
 
689
803
  def uncompress(value)