activesupport 5.1.7 → 5.2.0.beta1

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 (238) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +303 -617
  3. data/README.rdoc +1 -1
  4. data/lib/active_support.rb +3 -12
  5. data/lib/active_support/all.rb +2 -0
  6. data/lib/active_support/array_inquirer.rb +2 -0
  7. data/lib/active_support/backtrace_cleaner.rb +2 -0
  8. data/lib/active_support/benchmarkable.rb +2 -0
  9. data/lib/active_support/builder.rb +2 -0
  10. data/lib/active_support/cache.rb +127 -58
  11. data/lib/active_support/cache/file_store.rb +4 -3
  12. data/lib/active_support/cache/mem_cache_store.rb +12 -4
  13. data/lib/active_support/cache/memory_store.rb +2 -0
  14. data/lib/active_support/cache/null_store.rb +2 -0
  15. data/lib/active_support/cache/redis_cache_store.rb +404 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +9 -2
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
  18. data/lib/active_support/callbacks.rb +26 -37
  19. data/lib/active_support/concern.rb +3 -1
  20. data/lib/active_support/concurrency/share_lock.rb +2 -0
  21. data/lib/active_support/configurable.rb +2 -0
  22. data/lib/active_support/core_ext.rb +3 -1
  23. data/lib/active_support/core_ext/array.rb +2 -0
  24. data/lib/active_support/core_ext/array/access.rb +4 -2
  25. data/lib/active_support/core_ext/array/conversions.rb +2 -0
  26. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  27. data/lib/active_support/core_ext/array/grouping.rb +2 -0
  28. data/lib/active_support/core_ext/array/inquiry.rb +2 -0
  29. data/lib/active_support/core_ext/array/prepend_and_append.rb +4 -2
  30. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  31. data/lib/active_support/core_ext/benchmark.rb +2 -0
  32. data/lib/active_support/core_ext/big_decimal.rb +2 -0
  33. data/lib/active_support/core_ext/big_decimal/conversions.rb +2 -0
  34. data/lib/active_support/core_ext/class.rb +2 -0
  35. data/lib/active_support/core_ext/class/attribute.rb +34 -16
  36. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
  37. data/lib/active_support/core_ext/class/subclasses.rb +1 -2
  38. data/lib/active_support/core_ext/date.rb +2 -0
  39. data/lib/active_support/core_ext/date/acts_like.rb +2 -0
  40. data/lib/active_support/core_ext/date/blank.rb +2 -0
  41. data/lib/active_support/core_ext/date/calculations.rb +2 -0
  42. data/lib/active_support/core_ext/date/conversions.rb +10 -9
  43. data/lib/active_support/core_ext/date/zones.rb +2 -0
  44. data/lib/active_support/core_ext/date_and_time/calculations.rb +42 -16
  45. data/lib/active_support/core_ext/date_and_time/compatibility.rb +3 -1
  46. data/lib/active_support/core_ext/date_and_time/zones.rb +2 -0
  47. data/lib/active_support/core_ext/date_time.rb +2 -0
  48. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
  49. data/lib/active_support/core_ext/date_time/blank.rb +2 -0
  50. data/lib/active_support/core_ext/date_time/calculations.rb +2 -0
  51. data/lib/active_support/core_ext/date_time/compatibility.rb +6 -4
  52. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  53. data/lib/active_support/core_ext/digest/uuid.rb +3 -1
  54. data/lib/active_support/core_ext/enumerable.rb +3 -1
  55. data/lib/active_support/core_ext/file.rb +2 -0
  56. data/lib/active_support/core_ext/file/atomic.rb +2 -0
  57. data/lib/active_support/core_ext/hash.rb +2 -0
  58. data/lib/active_support/core_ext/hash/compact.rb +2 -0
  59. data/lib/active_support/core_ext/hash/conversions.rb +2 -0
  60. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  61. data/lib/active_support/core_ext/hash/except.rb +2 -0
  62. data/lib/active_support/core_ext/hash/indifferent_access.rb +2 -0
  63. data/lib/active_support/core_ext/hash/keys.rb +2 -0
  64. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  65. data/lib/active_support/core_ext/hash/slice.rb +4 -4
  66. data/lib/active_support/core_ext/hash/transform_values.rb +2 -0
  67. data/lib/active_support/core_ext/integer.rb +2 -0
  68. data/lib/active_support/core_ext/integer/inflections.rb +2 -0
  69. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  70. data/lib/active_support/core_ext/integer/time.rb +7 -14
  71. data/lib/active_support/core_ext/kernel.rb +2 -0
  72. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  74. data/lib/active_support/core_ext/kernel/reporting.rb +2 -0
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/load_error.rb +2 -7
  77. data/lib/active_support/core_ext/marshal.rb +2 -0
  78. data/lib/active_support/core_ext/module.rb +3 -0
  79. data/lib/active_support/core_ext/module/aliasing.rb +2 -0
  80. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  81. data/lib/active_support/core_ext/module/attr_internal.rb +2 -0
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +21 -24
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +2 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +2 -0
  85. data/lib/active_support/core_ext/module/delegation.rb +29 -24
  86. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  87. data/lib/active_support/core_ext/module/introspection.rb +2 -0
  88. data/lib/active_support/core_ext/module/reachable.rb +3 -0
  89. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  90. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  91. data/lib/active_support/core_ext/name_error.rb +2 -0
  92. data/lib/active_support/core_ext/numeric.rb +2 -0
  93. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  94. data/lib/active_support/core_ext/numeric/conversions.rb +9 -7
  95. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -0
  96. data/lib/active_support/core_ext/numeric/time.rb +7 -15
  97. data/lib/active_support/core_ext/object.rb +2 -0
  98. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  99. data/lib/active_support/core_ext/object/blank.rb +2 -0
  100. data/lib/active_support/core_ext/object/conversions.rb +2 -0
  101. data/lib/active_support/core_ext/object/deep_dup.rb +2 -0
  102. data/lib/active_support/core_ext/object/duplicable.rb +10 -8
  103. data/lib/active_support/core_ext/object/inclusion.rb +2 -0
  104. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  105. data/lib/active_support/core_ext/object/json.rb +8 -0
  106. data/lib/active_support/core_ext/object/to_param.rb +2 -0
  107. data/lib/active_support/core_ext/object/to_query.rb +4 -5
  108. data/lib/active_support/core_ext/object/try.rb +2 -0
  109. data/lib/active_support/core_ext/object/with_options.rb +3 -1
  110. data/lib/active_support/core_ext/range.rb +3 -0
  111. data/lib/active_support/core_ext/range/conversions.rb +9 -1
  112. data/lib/active_support/core_ext/range/each.rb +5 -1
  113. data/lib/active_support/core_ext/range/include_range.rb +2 -0
  114. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  115. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  116. data/lib/active_support/core_ext/regexp.rb +2 -0
  117. data/lib/active_support/core_ext/securerandom.rb +2 -0
  118. data/lib/active_support/core_ext/string.rb +2 -0
  119. data/lib/active_support/core_ext/string/access.rb +2 -0
  120. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  121. data/lib/active_support/core_ext/string/conversions.rb +2 -0
  122. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  123. data/lib/active_support/core_ext/string/filters.rb +2 -0
  124. data/lib/active_support/core_ext/string/indent.rb +2 -0
  125. data/lib/active_support/core_ext/string/inflections.rb +26 -12
  126. data/lib/active_support/core_ext/string/inquiry.rb +2 -0
  127. data/lib/active_support/core_ext/string/multibyte.rb +2 -0
  128. data/lib/active_support/core_ext/string/output_safety.rb +6 -7
  129. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  130. data/lib/active_support/core_ext/string/strip.rb +2 -0
  131. data/lib/active_support/core_ext/string/zones.rb +2 -0
  132. data/lib/active_support/core_ext/time.rb +2 -0
  133. data/lib/active_support/core_ext/time/acts_like.rb +2 -0
  134. data/lib/active_support/core_ext/time/calculations.rb +23 -15
  135. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  136. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  137. data/lib/active_support/core_ext/time/zones.rb +6 -4
  138. data/lib/active_support/core_ext/uri.rb +4 -1
  139. data/lib/active_support/current_attributes.rb +195 -0
  140. data/lib/active_support/dependencies.rb +15 -25
  141. data/lib/active_support/dependencies/autoload.rb +2 -0
  142. data/lib/active_support/dependencies/interlock.rb +2 -0
  143. data/lib/active_support/deprecation.rb +4 -2
  144. data/lib/active_support/deprecation/behaviors.rb +23 -8
  145. data/lib/active_support/deprecation/constant_accessor.rb +3 -1
  146. data/lib/active_support/deprecation/instance_delegator.rb +2 -0
  147. data/lib/active_support/deprecation/method_wrappers.rb +2 -7
  148. data/lib/active_support/deprecation/proxy_wrappers.rb +4 -1
  149. data/lib/active_support/deprecation/reporting.rb +4 -2
  150. data/lib/active_support/descendants_tracker.rb +2 -0
  151. data/lib/active_support/duration.rb +8 -14
  152. data/lib/active_support/duration/iso8601_parser.rb +4 -2
  153. data/lib/active_support/duration/iso8601_serializer.rb +4 -2
  154. data/lib/active_support/encrypted_configuration.rb +48 -0
  155. data/lib/active_support/encrypted_file.rb +99 -0
  156. data/lib/active_support/evented_file_update_checker.rb +2 -0
  157. data/lib/active_support/execution_wrapper.rb +2 -0
  158. data/lib/active_support/executor.rb +2 -0
  159. data/lib/active_support/file_update_checker.rb +2 -0
  160. data/lib/active_support/gem_version.rb +5 -3
  161. data/lib/active_support/gzip.rb +2 -0
  162. data/lib/active_support/hash_with_indifferent_access.rb +33 -1
  163. data/lib/active_support/i18n.rb +3 -1
  164. data/lib/active_support/i18n_railtie.rb +5 -11
  165. data/lib/active_support/inflections.rb +2 -0
  166. data/lib/active_support/inflector.rb +2 -0
  167. data/lib/active_support/inflector/inflections.rb +19 -3
  168. data/lib/active_support/inflector/methods.rb +40 -23
  169. data/lib/active_support/inflector/transliterate.rb +17 -8
  170. data/lib/active_support/json.rb +2 -0
  171. data/lib/active_support/json/decoding.rb +2 -0
  172. data/lib/active_support/json/encoding.rb +2 -0
  173. data/lib/active_support/key_generator.rb +3 -1
  174. data/lib/active_support/lazy_load_hooks.rb +2 -0
  175. data/lib/active_support/log_subscriber.rb +3 -2
  176. data/lib/active_support/log_subscriber/test_helper.rb +2 -0
  177. data/lib/active_support/logger.rb +2 -0
  178. data/lib/active_support/logger_silence.rb +3 -2
  179. data/lib/active_support/logger_thread_safe_level.rb +2 -0
  180. data/lib/active_support/message_encryptor.rb +94 -22
  181. data/lib/active_support/message_verifier.rb +78 -7
  182. data/lib/active_support/messages/metadata.rb +71 -0
  183. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  184. data/lib/active_support/messages/rotator.rb +56 -0
  185. data/lib/active_support/multibyte.rb +2 -0
  186. data/lib/active_support/multibyte/chars.rb +2 -0
  187. data/lib/active_support/multibyte/unicode.rb +3 -1
  188. data/lib/active_support/notifications.rb +2 -0
  189. data/lib/active_support/notifications/fanout.rb +2 -0
  190. data/lib/active_support/notifications/instrumenter.rb +2 -0
  191. data/lib/active_support/number_helper.rb +2 -0
  192. data/lib/active_support/number_helper/number_converter.rb +2 -0
  193. data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -0
  194. data/lib/active_support/number_helper/number_to_delimited_converter.rb +2 -0
  195. data/lib/active_support/number_helper/number_to_human_converter.rb +2 -0
  196. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -0
  197. data/lib/active_support/number_helper/number_to_percentage_converter.rb +2 -0
  198. data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -1
  199. data/lib/active_support/number_helper/number_to_rounded_converter.rb +2 -20
  200. data/lib/active_support/number_helper/rounding_helper.rb +5 -3
  201. data/lib/active_support/option_merger.rb +2 -0
  202. data/lib/active_support/ordered_hash.rb +2 -0
  203. data/lib/active_support/ordered_options.rb +4 -2
  204. data/lib/active_support/per_thread_registry.rb +2 -0
  205. data/lib/active_support/proxy_object.rb +2 -0
  206. data/lib/active_support/rails.rb +2 -0
  207. data/lib/active_support/railtie.rb +27 -8
  208. data/lib/active_support/reloader.rb +7 -5
  209. data/lib/active_support/rescuable.rb +3 -2
  210. data/lib/active_support/security_utils.rb +15 -11
  211. data/lib/active_support/string_inquirer.rb +2 -0
  212. data/lib/active_support/subscriber.rb +2 -0
  213. data/lib/active_support/tagged_logging.rb +2 -0
  214. data/lib/active_support/test_case.rb +2 -1
  215. data/lib/active_support/testing/assertions.rb +6 -4
  216. data/lib/active_support/testing/autorun.rb +2 -0
  217. data/lib/active_support/testing/constant_lookup.rb +2 -0
  218. data/lib/active_support/testing/declarative.rb +2 -0
  219. data/lib/active_support/testing/deprecation.rb +2 -0
  220. data/lib/active_support/testing/file_fixtures.rb +2 -0
  221. data/lib/active_support/testing/isolation.rb +5 -5
  222. data/lib/active_support/testing/method_call_assertions.rb +2 -0
  223. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  224. data/lib/active_support/testing/stream.rb +2 -0
  225. data/lib/active_support/testing/tagged_logging.rb +2 -0
  226. data/lib/active_support/testing/time_helpers.rb +31 -2
  227. data/lib/active_support/time.rb +2 -0
  228. data/lib/active_support/time_with_zone.rb +38 -0
  229. data/lib/active_support/values/time_zone.rb +20 -12
  230. data/lib/active_support/version.rb +2 -0
  231. data/lib/active_support/xml_mini.rb +2 -0
  232. data/lib/active_support/xml_mini/jdom.rb +4 -2
  233. data/lib/active_support/xml_mini/libxml.rb +3 -1
  234. data/lib/active_support/xml_mini/libxmlsax.rb +4 -2
  235. data/lib/active_support/xml_mini/nokogiri.rb +3 -1
  236. data/lib/active_support/xml_mini/nokogirisax.rb +3 -1
  237. data/lib/active_support/xml_mini/rexml.rb +3 -1
  238. metadata +19 -15
@@ -21,7 +21,7 @@ Source code can be downloaded as part of the Rails project on GitHub:
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #--
2
4
  # Copyright (c) 2005-2017 David Heinemeier Hansson
3
5
  #
@@ -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
@@ -79,18 +82,6 @@ module ActiveSupport
79
82
 
80
83
  cattr_accessor :test_order # :nodoc:
81
84
 
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
85
  def self.to_time_preserves_timezone
95
86
  DateAndTime::Compatibility.preserve_timezone
96
87
  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,24 @@ 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)
95
+ key.cache_key_with_version
96
+ when key.respond_to?(:cache_key)
97
+ key.cache_key
98
+ when key.is_a?(Hash)
99
+ key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }.to_param
100
+ when key.respond_to?(:to_a)
101
+ key.to_a.collect { |element| retrieve_cache_key(element) }.to_param
102
+ else
103
+ key.to_param
95
104
  end.to_s
96
105
  end
97
106
 
98
107
  # Obtains the specified cache store class, given the name of the +store+.
99
108
  # Raises an error when the store class cannot be found.
100
109
  def retrieve_store_class(store)
110
+ # require_relative cannot be used here because the class might be
111
+ # provided by another gem, like redis-activesupport for example.
101
112
  require "active_support/cache/#{store}"
102
113
  rescue LoadError => e
103
114
  raise "Could not find cache store adapter for #{store} (#{e})"
@@ -143,12 +154,11 @@ module ActiveSupport
143
154
  # cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
144
155
  # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
145
156
  #
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.
157
+ # Cached data larger than 1kB are compressed by default. To turn off
158
+ # compression, pass <tt>compress: false</tt> to the initializer or to
159
+ # individual +fetch+ or +write+ method calls. The 1kB compression
160
+ # threshold is configurable with the <tt>:compress_threshold</tt> option,
161
+ # specified in bytes.
152
162
  class Store
153
163
  cattr_accessor :logger, instance_writer: true
154
164
 
@@ -207,8 +217,7 @@ module ActiveSupport
207
217
  # ask whether you should force a cache write. Otherwise, it's clearer to
208
218
  # just call <tt>Cache#write</tt>.
209
219
  #
210
- # Setting <tt>:compress</tt> will store a large cache entry set by the call
211
- # in a compressed format.
220
+ # Setting <tt>compress: false</tt> disables compression of the cache entry.
212
221
  #
213
222
  # Setting <tt>:expires_in</tt> will set an expiration time on the cache.
214
223
  # All caches support auto-expiring content after a specified number of
@@ -219,6 +228,10 @@ module ActiveSupport
219
228
  # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
220
229
  # cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
221
230
  #
231
+ # Setting <tt>:version</tt> verifies the cache stored under <tt>name</tt>
232
+ # is of the same version. nil is returned on mismatches despite contents.
233
+ # This feature is used to support recyclable cache keys.
234
+ #
222
235
  # Setting <tt>:race_condition_ttl</tt> is very useful in situations where
223
236
  # a cache entry is used very frequently and is under heavy load. If a
224
237
  # cache expires and due to heavy load several different processes will try
@@ -287,6 +300,7 @@ module ActiveSupport
287
300
  instrument(:read, name, options) do |payload|
288
301
  cached_entry = read_entry(key, options) unless options[:force]
289
302
  entry = handle_expired_entry(cached_entry, key, options)
303
+ entry = nil if entry && entry.mismatched?(normalize_version(name, options))
290
304
  payload[:super_operation] = :fetch if payload
291
305
  payload[:hit] = !!entry if payload
292
306
  end
@@ -303,21 +317,30 @@ module ActiveSupport
303
317
  end
304
318
  end
305
319
 
306
- # Fetches data from the cache, using the given key. If there is data in
320
+ # Reads data from the cache, using the given key. If there is data in
307
321
  # the cache with the given key, then that data is returned. Otherwise,
308
322
  # +nil+ is returned.
309
323
  #
324
+ # Note, if data was written with the <tt>:expires_in<tt> or <tt>:version</tt> options,
325
+ # both of these conditions are applied before the data is returned.
326
+ #
310
327
  # Options are passed to the underlying cache implementation.
311
328
  def read(name, options = nil)
312
329
  options = merged_options(options)
313
- key = normalize_key(name, options)
330
+ key = normalize_key(name, options)
331
+ version = normalize_version(name, options)
332
+
314
333
  instrument(:read, name, options) do |payload|
315
334
  entry = read_entry(key, options)
335
+
316
336
  if entry
317
337
  if entry.expired?
318
338
  delete_entry(key, options)
319
339
  payload[:hit] = false if payload
320
340
  nil
341
+ elsif entry.mismatched?(version)
342
+ payload[:hit] = false if payload
343
+ nil
321
344
  else
322
345
  payload[:hit] = true if payload
323
346
  entry.value
@@ -341,11 +364,15 @@ module ActiveSupport
341
364
 
342
365
  results = {}
343
366
  names.each do |name|
344
- key = normalize_key(name, options)
345
- entry = read_entry(key, options)
367
+ key = normalize_key(name, options)
368
+ version = normalize_version(name, options)
369
+ entry = read_entry(key, options)
370
+
346
371
  if entry
347
372
  if entry.expired?
348
373
  delete_entry(key, options)
374
+ elsif entry.mismatched?(version)
375
+ # Skip mismatched versions
349
376
  else
350
377
  results[name] = entry.value
351
378
  end
@@ -354,6 +381,19 @@ module ActiveSupport
354
381
  results
355
382
  end
356
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
395
+ end
396
+
357
397
  # Fetches data from the cache, using the given keys. If there is data in
358
398
  # the cache with the given keys, then that data is returned. Otherwise,
359
399
  # the supplied block is called for each key for which there was no data,
@@ -378,14 +418,15 @@ 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
+ read_multi(*names, options).tap do |results|
423
+ writes = {}
424
+
425
+ (names - results.keys).each do |name|
426
+ results[name] = writes[name] = yield(name)
388
427
  end
428
+
429
+ write_multi writes, options
389
430
  end
390
431
  end
391
432
 
@@ -396,7 +437,7 @@ module ActiveSupport
396
437
  options = merged_options(options)
397
438
 
398
439
  instrument(:write, name, options) do
399
- entry = Entry.new(value, options)
440
+ entry = Entry.new(value, options.merge(version: normalize_version(name, options)))
400
441
  write_entry(normalize_key(name, options), entry, options)
401
442
  end
402
443
  end
@@ -420,7 +461,7 @@ module ActiveSupport
420
461
 
421
462
  instrument(:exist?, name) do
422
463
  entry = read_entry(normalize_key(name, options), options)
423
- (entry && !entry.expired?) || false
464
+ (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
424
465
  end
425
466
  end
426
467
 
@@ -502,6 +543,14 @@ module ActiveSupport
502
543
  raise NotImplementedError.new
503
544
  end
504
545
 
546
+ # Writes multiple entries to the cache implementation. Subclasses MAY
547
+ # implement this method.
548
+ def write_multi_entries(hash, options)
549
+ hash.each do |key, entry|
550
+ write_entry key, entry, options
551
+ end
552
+ end
553
+
505
554
  # Deletes an entry from the cache implementation. Subclasses must
506
555
  # implement this method.
507
556
  def delete_entry(key, options)
@@ -517,34 +566,46 @@ module ActiveSupport
517
566
  end
518
567
  end
519
568
 
520
- # Expands key to be a consistent string value. Invokes +cache_key+ if
521
- # object responds to +cache_key+. Otherwise, +to_param+ method will be
522
- # called. If the key is a Hash, then keys will be sorted alphabetically.
523
- def expanded_key(key)
524
- return key.cache_key.to_s if key.respond_to?(:cache_key)
569
+ # Expands and namespaces the cache key. May be overridden by
570
+ # cache stores to do additional normalization.
571
+ def normalize_key(key, options = nil)
572
+ namespace_key Cache.expand_cache_key(key), options
573
+ end
525
574
 
526
- case key
527
- when Array
528
- if key.size > 1
529
- key = key.collect { |element| expanded_key(element) }
530
- else
531
- key = key.first
532
- end
533
- when Hash
534
- key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
575
+ # Prefix the key with a namespace string:
576
+ #
577
+ # namespace_key 'foo', namespace: 'cache'
578
+ # # => 'cache:foo'
579
+ #
580
+ # With a namespace block:
581
+ #
582
+ # namespace_key 'foo', namespace: -> { 'cache' }
583
+ # # => 'cache:foo'
584
+ def namespace_key(key, options = nil)
585
+ options = merged_options(options)
586
+ namespace = options[:namespace]
587
+
588
+ if namespace.respond_to?(:call)
589
+ namespace = namespace.call
535
590
  end
536
591
 
537
- key.to_param
592
+ if namespace
593
+ "#{namespace}:#{key}"
594
+ else
595
+ key
596
+ end
538
597
  end
539
598
 
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
599
+ def normalize_version(key, options = nil)
600
+ (options && options[:version].try(:to_param)) || expanded_version(key)
601
+ end
602
+
603
+ def expanded_version(key)
604
+ case
605
+ when key.respond_to?(:cache_version) then key.cache_version.to_param
606
+ when key.is_a?(Array) then key.map { |element| expanded_version(element) }.compact.to_param
607
+ when key.respond_to?(:to_a) then expanded_version(key.to_a)
608
+ end
548
609
  end
549
610
 
550
611
  def instrument(operation, key, options = nil)
@@ -591,14 +652,17 @@ module ActiveSupport
591
652
  end
592
653
  end
593
654
 
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.
655
+ # This class is used to represent cache entries. Cache entries have a value, an optional
656
+ # expiration time, and an optional version. The expiration time is used to support the :race_condition_ttl option
657
+ # on the cache. The version is used to support the :version option on the cache for rejecting
658
+ # mismatches.
597
659
  #
598
660
  # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
599
661
  # using short instance variable names that are lazily defined.
600
662
  class Entry # :nodoc:
601
- DEFAULT_COMPRESS_LIMIT = 16.kilobytes
663
+ attr_reader :version
664
+
665
+ DEFAULT_COMPRESS_LIMIT = 1.kilobyte
602
666
 
603
667
  # Creates a new cache entry for the specified value. Options supported are
604
668
  # +:compress+, +:compress_threshold+, and +:expires_in+.
@@ -610,6 +674,7 @@ module ActiveSupport
610
674
  @value = value
611
675
  end
612
676
 
677
+ @version = options[:version]
613
678
  @created_at = Time.now.to_f
614
679
  @expires_in = options[:expires_in]
615
680
  @expires_in = @expires_in.to_f if @expires_in
@@ -619,6 +684,10 @@ module ActiveSupport
619
684
  compressed? ? uncompress(@value) : @value
620
685
  end
621
686
 
687
+ def mismatched?(version)
688
+ @version && version && @version != version
689
+ end
690
+
622
691
  # Checks if the entry is expired. The +expires_in+ parameter can override
623
692
  # the value set when the entry was created.
624
693
  def expired?
@@ -668,8 +737,8 @@ module ActiveSupport
668
737
 
669
738
  private
670
739
  def should_compress?(value, options)
671
- if value && options[:compress]
672
- compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
740
+ if value && options.fetch(:compress, true)
741
+ compress_threshold = options.fetch(:compress_threshold, DEFAULT_COMPRESS_LIMIT)
673
742
  serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
674
743
 
675
744
  return true if serialized_value_size >= compress_threshold
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/marshal"
2
4
  require "active_support/core_ext/file/atomic"
3
5
  require "active_support/core_ext/string/conversions"
@@ -37,9 +39,8 @@ module ActiveSupport
37
39
  def cleanup(options = nil)
38
40
  options = merged_options(options)
39
41
  search_dir(cache_path) do |fname|
40
- key = file_path_key(fname)
41
- entry = read_entry(key, options)
42
- delete_entry(key, options) if entry && entry.expired?
42
+ entry = read_entry(fname, options)
43
+ delete_entry(fname, options) if entry && entry.expired?
43
44
  end
44
45
  end
45
46
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require "dalli"
3
5
  rescue LoadError => e
@@ -12,7 +14,7 @@ require "active_support/core_ext/array/extract_options"
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
  #
@@ -97,12 +99,18 @@ module ActiveSupport
97
99
  options = merged_options(options)
98
100
 
99
101
  keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
102
+
100
103
  raw_values = @data.get_multi(keys_to_names.keys)
101
104
  values = {}
105
+
102
106
  raw_values.each do |key, value|
103
107
  entry = deserialize_entry(value)
104
- values[keys_to_names[key]] = entry.value unless entry.expired?
108
+
109
+ unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
110
+ values[keys_to_names[key]] = entry.value
111
+ end
105
112
  end
113
+
106
114
  values
107
115
  end
108
116
 
@@ -114,7 +122,7 @@ module ActiveSupport
114
122
  options = merged_options(options)
115
123
  instrument(:increment, name, amount: amount) do
116
124
  rescue_error_with nil do
117
- @data.incr(normalize_key(name, options), amount)
125
+ @data.incr(normalize_key(name, options), amount, options[:expires_in])
118
126
  end
119
127
  end
120
128
  end
@@ -127,7 +135,7 @@ module ActiveSupport
127
135
  options = merged_options(options)
128
136
  instrument(:decrement, name, amount: amount) do
129
137
  rescue_error_with nil do
130
- @data.decr(normalize_key(name, options), amount)
138
+ @data.decr(normalize_key(name, options), amount, options[:expires_in])
131
139
  end
132
140
  end
133
141
  end