activesupport 3.1.0 → 5.0.0

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

Potentially problematic release.


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

Files changed (276) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +798 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +13 -7
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +38 -34
  7. data/lib/active_support/benchmarkable.rb +17 -28
  8. data/lib/active_support/cache/file_store.rb +85 -70
  9. data/lib/active_support/cache/mem_cache_store.rb +75 -66
  10. data/lib/active_support/cache/memory_store.rb +31 -23
  11. data/lib/active_support/cache/null_store.rb +41 -0
  12. data/lib/active_support/cache/strategy/local_cache.rb +73 -70
  13. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  14. data/lib/active_support/cache.rb +360 -294
  15. data/lib/active_support/callbacks.rb +563 -393
  16. data/lib/active_support/concern.rb +42 -34
  17. data/lib/active_support/concurrency/latch.rb +19 -0
  18. data/lib/active_support/concurrency/share_lock.rb +186 -0
  19. data/lib/active_support/configurable.rb +70 -12
  20. data/lib/active_support/core_ext/array/access.rb +53 -9
  21. data/lib/active_support/core_ext/array/conversions.rb +109 -62
  22. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  23. data/lib/active_support/core_ext/array/grouping.rb +39 -32
  24. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  25. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  26. data/lib/active_support/core_ext/array/wrap.rb +16 -18
  27. data/lib/active_support/core_ext/array.rb +2 -2
  28. data/lib/active_support/core_ext/benchmark.rb +7 -0
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -36
  30. data/lib/active_support/core_ext/class/attribute.rb +47 -34
  31. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -79
  32. data/lib/active_support/core_ext/class/subclasses.rb +12 -7
  33. data/lib/active_support/core_ext/class.rb +0 -3
  34. data/lib/active_support/core_ext/date/blank.rb +12 -0
  35. data/lib/active_support/core_ext/date/calculations.rb +57 -167
  36. data/lib/active_support/core_ext/date/conversions.rb +31 -42
  37. data/lib/active_support/core_ext/date/zones.rb +2 -10
  38. data/lib/active_support/core_ext/date.rb +5 -0
  39. data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
  40. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -0
  41. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  42. data/lib/active_support/core_ext/date_time/acts_like.rb +1 -0
  43. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  44. data/lib/active_support/core_ext/date_time/calculations.rb +132 -65
  45. data/lib/active_support/core_ext/date_time/compatibility.rb +5 -0
  46. data/lib/active_support/core_ext/date_time/conversions.rb +36 -34
  47. data/lib/active_support/core_ext/date_time.rb +5 -0
  48. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  49. data/lib/active_support/core_ext/enumerable.rb +81 -74
  50. data/lib/active_support/core_ext/file/atomic.rb +53 -26
  51. data/lib/active_support/core_ext/file.rb +0 -1
  52. data/lib/active_support/core_ext/hash/compact.rb +20 -0
  53. data/lib/active_support/core_ext/hash/conversions.rb +175 -70
  54. data/lib/active_support/core_ext/hash/deep_merge.rb +30 -8
  55. data/lib/active_support/core_ext/hash/except.rb +11 -12
  56. data/lib/active_support/core_ext/hash/indifferent_access.rb +7 -8
  57. data/lib/active_support/core_ext/hash/keys.rb +147 -24
  58. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  59. data/lib/active_support/core_ext/hash/slice.rb +22 -14
  60. data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
  61. data/lib/active_support/core_ext/hash.rb +2 -2
  62. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  63. data/lib/active_support/core_ext/integer/multiple.rb +4 -0
  64. data/lib/active_support/core_ext/integer/time.rb +12 -22
  65. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
  66. data/lib/active_support/core_ext/kernel/concern.rb +12 -0
  67. data/lib/active_support/core_ext/kernel/debugger.rb +2 -15
  68. data/lib/active_support/core_ext/kernel/reporting.rb +12 -62
  69. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  70. data/lib/active_support/core_ext/kernel.rb +2 -3
  71. data/lib/active_support/core_ext/load_error.rb +14 -7
  72. data/lib/active_support/core_ext/marshal.rb +22 -0
  73. data/lib/active_support/core_ext/module/aliasing.rb +16 -12
  74. data/lib/active_support/core_ext/module/anonymous.rb +12 -8
  75. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  76. data/lib/active_support/core_ext/module/attribute_accessors.rb +165 -13
  77. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  78. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  79. data/lib/active_support/core_ext/module/delegation.rb +141 -68
  80. data/lib/active_support/core_ext/module/deprecation.rb +17 -3
  81. data/lib/active_support/core_ext/module/introspection.rb +9 -31
  82. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
  83. data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
  84. data/lib/active_support/core_ext/module/reachable.rb +1 -3
  85. data/lib/active_support/core_ext/module/remove_method.rb +24 -5
  86. data/lib/active_support/core_ext/module.rb +3 -3
  87. data/lib/active_support/core_ext/name_error.rb +15 -2
  88. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  89. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  90. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  91. data/lib/active_support/core_ext/numeric/time.rb +31 -36
  92. data/lib/active_support/core_ext/numeric.rb +2 -0
  93. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  94. data/lib/active_support/core_ext/object/blank.rb +52 -18
  95. data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
  96. data/lib/active_support/core_ext/object/duplicable.rb +12 -20
  97. data/lib/active_support/core_ext/object/inclusion.rb +13 -1
  98. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  99. data/lib/active_support/core_ext/object/json.rb +205 -0
  100. data/lib/active_support/core_ext/object/to_param.rb +1 -55
  101. data/lib/active_support/core_ext/object/to_query.rb +66 -9
  102. data/lib/active_support/core_ext/object/try.rb +124 -33
  103. data/lib/active_support/core_ext/object/with_options.rb +37 -11
  104. data/lib/active_support/core_ext/object.rb +2 -1
  105. data/lib/active_support/core_ext/range/conversions.rb +17 -7
  106. data/lib/active_support/core_ext/range/each.rb +21 -0
  107. data/lib/active_support/core_ext/range/include_range.rb +20 -18
  108. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  109. data/lib/active_support/core_ext/range.rb +1 -2
  110. data/lib/active_support/core_ext/securerandom.rb +23 -0
  111. data/lib/active_support/core_ext/string/access.rb +95 -90
  112. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  113. data/lib/active_support/core_ext/string/conversions.rb +41 -38
  114. data/lib/active_support/core_ext/string/exclude.rb +6 -1
  115. data/lib/active_support/core_ext/string/filters.rb +70 -17
  116. data/lib/active_support/core_ext/string/indent.rb +43 -0
  117. data/lib/active_support/core_ext/string/inflections.rb +139 -59
  118. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  119. data/lib/active_support/core_ext/string/multibyte.rb +46 -65
  120. data/lib/active_support/core_ext/string/output_safety.rb +153 -56
  121. data/lib/active_support/core_ext/string/strip.rb +3 -6
  122. data/lib/active_support/core_ext/string/zones.rb +14 -0
  123. data/lib/active_support/core_ext/string.rb +2 -3
  124. data/lib/active_support/core_ext/struct.rb +3 -0
  125. data/lib/active_support/core_ext/time/calculations.rb +173 -173
  126. data/lib/active_support/core_ext/time/compatibility.rb +5 -0
  127. data/lib/active_support/core_ext/time/conversions.rb +33 -29
  128. data/lib/active_support/core_ext/time/marshal.rb +2 -56
  129. data/lib/active_support/core_ext/time/zones.rb +57 -32
  130. data/lib/active_support/core_ext/time.rb +5 -0
  131. data/lib/active_support/core_ext/uri.rb +13 -19
  132. data/lib/active_support/core_ext.rb +3 -2
  133. data/lib/active_support/dependencies/autoload.rb +47 -20
  134. data/lib/active_support/dependencies/interlock.rb +51 -0
  135. data/lib/active_support/dependencies.rb +315 -265
  136. data/lib/active_support/deprecation/behaviors.rb +71 -30
  137. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  138. data/lib/active_support/deprecation/method_wrappers.rb +59 -18
  139. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -14
  140. data/lib/active_support/deprecation/reporting.rb +61 -14
  141. data/lib/active_support/deprecation.rb +38 -13
  142. data/lib/active_support/descendants_tracker.rb +34 -19
  143. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  144. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  145. data/lib/active_support/duration.rb +85 -14
  146. data/lib/active_support/evented_file_update_checker.rb +194 -0
  147. data/lib/active_support/execution_wrapper.rb +117 -0
  148. data/lib/active_support/executor.rb +6 -0
  149. data/lib/active_support/file_update_checker.rb +138 -17
  150. data/lib/active_support/gem_version.rb +15 -0
  151. data/lib/active_support/gzip.rb +11 -5
  152. data/lib/active_support/hash_with_indifferent_access.rb +199 -49
  153. data/lib/active_support/i18n.rb +6 -2
  154. data/lib/active_support/i18n_railtie.rb +40 -21
  155. data/lib/active_support/inflections.rb +22 -13
  156. data/lib/active_support/inflector/inflections.rb +175 -144
  157. data/lib/active_support/inflector/methods.rb +328 -91
  158. data/lib/active_support/inflector/transliterate.rb +51 -37
  159. data/lib/active_support/json/decoding.rb +31 -22
  160. data/lib/active_support/json/encoding.rb +88 -248
  161. data/lib/active_support/key_generator.rb +71 -0
  162. data/lib/active_support/lazy_load_hooks.rb +27 -25
  163. data/lib/active_support/locale/en.yml +102 -3
  164. data/lib/active_support/log_subscriber/test_helper.rb +24 -21
  165. data/lib/active_support/log_subscriber.rb +36 -49
  166. data/lib/active_support/logger.rb +106 -0
  167. data/lib/active_support/logger_silence.rb +28 -0
  168. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  169. data/lib/active_support/message_encryptor.rb +72 -36
  170. data/lib/active_support/message_verifier.rb +96 -24
  171. data/lib/active_support/multibyte/chars.rb +88 -333
  172. data/lib/active_support/multibyte/unicode.rb +156 -136
  173. data/lib/active_support/multibyte.rb +5 -28
  174. data/lib/active_support/notifications/fanout.rb +115 -19
  175. data/lib/active_support/notifications/instrumenter.rb +52 -15
  176. data/lib/active_support/notifications.rb +168 -33
  177. data/lib/active_support/number_helper/number_converter.rb +182 -0
  178. data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
  179. data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
  180. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  181. data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
  182. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  183. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  184. data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
  185. data/lib/active_support/number_helper.rb +368 -0
  186. data/lib/active_support/option_merger.rb +1 -1
  187. data/lib/active_support/ordered_hash.rb +18 -183
  188. data/lib/active_support/ordered_options.rb +44 -24
  189. data/lib/active_support/per_thread_registry.rb +58 -0
  190. data/lib/active_support/proxy_object.rb +13 -0
  191. data/lib/active_support/rails.rb +27 -0
  192. data/lib/active_support/railtie.rb +25 -34
  193. data/lib/active_support/reloader.rb +129 -0
  194. data/lib/active_support/rescuable.rb +98 -48
  195. data/lib/active_support/security_utils.rb +27 -0
  196. data/lib/active_support/string_inquirer.rb +14 -9
  197. data/lib/active_support/subscriber.rb +120 -0
  198. data/lib/active_support/tagged_logging.rb +78 -0
  199. data/lib/active_support/test_case.rb +69 -17
  200. data/lib/active_support/testing/assertions.rb +43 -41
  201. data/lib/active_support/testing/autorun.rb +12 -0
  202. data/lib/active_support/testing/constant_lookup.rb +50 -0
  203. data/lib/active_support/testing/declarative.rb +7 -21
  204. data/lib/active_support/testing/deprecation.rb +14 -33
  205. data/lib/active_support/testing/file_fixtures.rb +34 -0
  206. data/lib/active_support/testing/isolation.rb +53 -95
  207. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  208. data/lib/active_support/testing/setup_and_teardown.rb +21 -82
  209. data/lib/active_support/testing/stream.rb +42 -0
  210. data/lib/active_support/testing/tagged_logging.rb +25 -0
  211. data/lib/active_support/testing/time_helpers.rb +134 -0
  212. data/lib/active_support/time.rb +6 -23
  213. data/lib/active_support/time_with_zone.rb +239 -92
  214. data/lib/active_support/values/time_zone.rb +236 -160
  215. data/lib/active_support/values/unicode_tables.dat +0 -0
  216. data/lib/active_support/version.rb +5 -7
  217. data/lib/active_support/xml_mini/jdom.rb +19 -13
  218. data/lib/active_support/xml_mini/libxml.rb +3 -4
  219. data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
  220. data/lib/active_support/xml_mini/nokogiri.rb +3 -4
  221. data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
  222. data/lib/active_support/xml_mini/rexml.rb +8 -10
  223. data/lib/active_support/xml_mini.rb +66 -34
  224. data/lib/active_support.rb +40 -23
  225. metadata +185 -134
  226. data/CHANGELOG +0 -1534
  227. data/lib/active_support/base64.rb +0 -42
  228. data/lib/active_support/basic_object.rb +0 -21
  229. data/lib/active_support/buffered_logger.rb +0 -137
  230. data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
  231. data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
  232. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  233. data/lib/active_support/core_ext/array/uniq_by.rb +0 -16
  234. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -44
  235. data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
  236. data/lib/active_support/core_ext/date/freeze.rb +0 -31
  237. data/lib/active_support/core_ext/date_time/zones.rb +0 -21
  238. data/lib/active_support/core_ext/exception.rb +0 -3
  239. data/lib/active_support/core_ext/file/path.rb +0 -5
  240. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  241. data/lib/active_support/core_ext/float.rb +0 -1
  242. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -11
  243. data/lib/active_support/core_ext/hash/diff.rb +0 -13
  244. data/lib/active_support/core_ext/kernel/requires.rb +0 -28
  245. data/lib/active_support/core_ext/logger.rb +0 -81
  246. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
  247. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  248. data/lib/active_support/core_ext/module/synchronization.rb +0 -43
  249. data/lib/active_support/core_ext/object/to_json.rb +0 -19
  250. data/lib/active_support/core_ext/proc.rb +0 -14
  251. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  252. data/lib/active_support/core_ext/process.rb +0 -1
  253. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  254. data/lib/active_support/core_ext/range/cover.rb +0 -3
  255. data/lib/active_support/core_ext/rexml.rb +0 -46
  256. data/lib/active_support/core_ext/string/encoding.rb +0 -11
  257. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  258. data/lib/active_support/core_ext/string/xchar.rb +0 -18
  259. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
  260. data/lib/active_support/file_watcher.rb +0 -36
  261. data/lib/active_support/json/variable.rb +0 -9
  262. data/lib/active_support/memoizable.rb +0 -105
  263. data/lib/active_support/multibyte/exceptions.rb +0 -8
  264. data/lib/active_support/multibyte/utils.rb +0 -60
  265. data/lib/active_support/ruby/shim.rb +0 -22
  266. data/lib/active_support/secure_random.rb +0 -6
  267. data/lib/active_support/testing/mochaing.rb +0 -7
  268. data/lib/active_support/testing/pending.rb +0 -52
  269. data/lib/active_support/testing/performance/jruby.rb +0 -115
  270. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  271. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  272. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  273. data/lib/active_support/testing/performance/ruby.rb +0 -152
  274. data/lib/active_support/testing/performance.rb +0 -317
  275. data/lib/active_support/time/autoload.rb +0 -5
  276. data/lib/active_support/whiny_nil.rb +0 -60
@@ -3,21 +3,20 @@ require 'zlib'
3
3
  require 'active_support/core_ext/array/extract_options'
4
4
  require 'active_support/core_ext/array/wrap'
5
5
  require 'active_support/core_ext/benchmark'
6
- require 'active_support/core_ext/exception'
7
- require 'active_support/core_ext/class/attribute_accessors'
6
+ require 'active_support/core_ext/module/attribute_accessors'
8
7
  require 'active_support/core_ext/numeric/bytes'
9
8
  require 'active_support/core_ext/numeric/time'
10
9
  require 'active_support/core_ext/object/to_param'
11
10
  require 'active_support/core_ext/string/inflections'
11
+ require 'active_support/core_ext/string/strip'
12
12
 
13
13
  module ActiveSupport
14
14
  # See ActiveSupport::Cache::Store for documentation.
15
15
  module Cache
16
- autoload :FileStore, 'active_support/cache/file_store'
17
- autoload :MemoryStore, 'active_support/cache/memory_store'
16
+ autoload :FileStore, 'active_support/cache/file_store'
17
+ autoload :MemoryStore, 'active_support/cache/memory_store'
18
18
  autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
19
- autoload :SynchronizedMemoryStore, 'active_support/cache/synchronized_memory_store'
20
- autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store'
19
+ autoload :NullStore, 'active_support/cache/null_store'
21
20
 
22
21
  # These options mean something to all cache implementations. Individual cache
23
22
  # implementations may support additional options.
@@ -27,75 +26,87 @@ module ActiveSupport
27
26
  autoload :LocalCache, 'active_support/cache/strategy/local_cache'
28
27
  end
29
28
 
30
- # Creates a new CacheStore object according to the given options.
31
- #
32
- # If no arguments are passed to this method, then a new
33
- # ActiveSupport::Cache::MemoryStore object will be returned.
34
- #
35
- # If you pass a Symbol as the first argument, then a corresponding cache
36
- # store class under the ActiveSupport::Cache namespace will be created.
37
- # For example:
38
- #
39
- # ActiveSupport::Cache.lookup_store(:memory_store)
40
- # # => returns a new ActiveSupport::Cache::MemoryStore object
41
- #
42
- # ActiveSupport::Cache.lookup_store(:mem_cache_store)
43
- # # => returns a new ActiveSupport::Cache::MemCacheStore object
44
- #
45
- # Any additional arguments will be passed to the corresponding cache store
46
- # class's constructor:
47
- #
48
- # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache")
49
- # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache")
50
- #
51
- # If the first argument is not a Symbol, then it will simply be returned:
52
- #
53
- # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
54
- # # => returns MyOwnCacheStore.new
55
- def self.lookup_store(*store_option)
56
- store, *parameters = *Array.wrap(store_option).flatten
57
-
58
- case store
59
- when Symbol
60
- store_class_name = store.to_s.camelize
61
- store_class =
62
- begin
63
- require "active_support/cache/#{store}"
64
- rescue LoadError => e
65
- raise "Could not find cache store adapter for #{store} (#{e})"
66
- else
67
- ActiveSupport::Cache.const_get(store_class_name)
68
- end
69
- store_class.new(*parameters)
70
- when nil
71
- ActiveSupport::Cache::MemoryStore.new
72
- else
73
- store
29
+ class << self
30
+ # Creates a new Store object according to the given options.
31
+ #
32
+ # If no arguments are passed to this method, then a new
33
+ # ActiveSupport::Cache::MemoryStore object will be returned.
34
+ #
35
+ # If you pass a Symbol as the first argument, then a corresponding cache
36
+ # store class under the ActiveSupport::Cache namespace will be created.
37
+ # For example:
38
+ #
39
+ # ActiveSupport::Cache.lookup_store(:memory_store)
40
+ # # => returns a new ActiveSupport::Cache::MemoryStore object
41
+ #
42
+ # ActiveSupport::Cache.lookup_store(:mem_cache_store)
43
+ # # => returns a new ActiveSupport::Cache::MemCacheStore object
44
+ #
45
+ # Any additional arguments will be passed to the corresponding cache store
46
+ # class's constructor:
47
+ #
48
+ # ActiveSupport::Cache.lookup_store(:file_store, '/tmp/cache')
49
+ # # => same as: ActiveSupport::Cache::FileStore.new('/tmp/cache')
50
+ #
51
+ # If the first argument is not a Symbol, then it will simply be returned:
52
+ #
53
+ # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
54
+ # # => returns MyOwnCacheStore.new
55
+ def lookup_store(*store_option)
56
+ store, *parameters = *Array.wrap(store_option).flatten
57
+
58
+ case store
59
+ when Symbol
60
+ retrieve_store_class(store).new(*parameters)
61
+ when nil
62
+ ActiveSupport::Cache::MemoryStore.new
63
+ else
64
+ store
65
+ end
74
66
  end
75
- end
76
67
 
77
- def self.expand_cache_key(key, namespace = nil)
78
- expanded_cache_key = namespace ? "#{namespace}/" : ""
68
+ # Expands out the +key+ argument into a key that can be used for the
69
+ # cache store. Optionally accepts a namespace, and all keys will be
70
+ # scoped within that namespace.
71
+ #
72
+ # If the +key+ argument provided is an array, or responds to +to_a+, then
73
+ # each of elements in the array will be turned into parameters/keys and
74
+ # concatenated into a single key. For example:
75
+ #
76
+ # expand_cache_key([:foo, :bar]) # => "foo/bar"
77
+ # expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
78
+ #
79
+ # The +key+ argument can also respond to +cache_key+ or +to_param+.
80
+ def expand_cache_key(key, namespace = nil)
81
+ expanded_cache_key = namespace ? "#{namespace}/" : ""
82
+
83
+ if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
84
+ expanded_cache_key << "#{prefix}/"
85
+ end
79
86
 
80
- prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
81
- if prefix
82
- expanded_cache_key << "#{prefix}/"
87
+ expanded_cache_key << retrieve_cache_key(key)
88
+ expanded_cache_key
83
89
  end
84
90
 
85
- expanded_cache_key <<
86
- if key.respond_to?(:cache_key)
87
- key.cache_key
88
- elsif key.is_a?(Array)
89
- if key.size > 1
90
- key.collect { |element| expand_cache_key(element) }.to_param
91
- else
92
- key.first.to_param
93
- end
94
- elsif key
95
- key.to_param
96
- end.to_s
91
+ private
92
+ def retrieve_cache_key(key)
93
+ case
94
+ when key.respond_to?(:cache_key) then key.cache_key
95
+ when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
96
+ when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
97
+ else key.to_param
98
+ end.to_s
99
+ end
97
100
 
98
- expanded_cache_key
101
+ # Obtains the specified cache store class, given the name of the +store+.
102
+ # Raises an error when the store class cannot be found.
103
+ def retrieve_store_class(store)
104
+ require "active_support/cache/#{store}"
105
+ rescue LoadError => e
106
+ raise "Could not find cache store adapter for #{store} (#{e})"
107
+ else
108
+ ActiveSupport::Cache.const_get(store.to_s.camelize)
109
+ end
99
110
  end
100
111
 
101
112
  # An abstract cache store class. There are multiple cache store
@@ -111,56 +122,56 @@ module ActiveSupport
111
122
  #
112
123
  # cache = ActiveSupport::Cache::MemoryStore.new
113
124
  #
114
- # cache.read("city") # => nil
115
- # cache.write("city", "Duckburgh")
116
- # cache.read("city") # => "Duckburgh"
125
+ # cache.read('city') # => nil
126
+ # cache.write('city', "Duckburgh")
127
+ # cache.read('city') # => "Duckburgh"
117
128
  #
118
129
  # Keys are always translated into Strings and are case sensitive. When an
119
- # object is specified as a key, its +cache_key+ method will be called if it
120
- # is defined. Otherwise, the +to_param+ method will be called. Hashes and
121
- # Arrays can be used as keys. The elements will be delimited by slashes
122
- # and Hashes elements will be sorted by key so they are consistent.
130
+ # object is specified as a key and has a +cache_key+ method defined, this
131
+ # method will be called to define the key. Otherwise, the +to_param+
132
+ # method will be called. Hashes and Arrays can also be used as keys. The
133
+ # elements will be delimited by slashes, and the elements within a Hash
134
+ # will be sorted by key so they are consistent.
123
135
  #
124
- # cache.read("city") == cache.read(:city) # => true
136
+ # cache.read('city') == cache.read(:city) # => true
125
137
  #
126
138
  # Nil values can be cached.
127
139
  #
128
- # If your cache is on a shared infrastructure, you can define a namespace for
129
- # your cache entries. If a namespace is defined, it will be prefixed on to every
130
- # key. The namespace can be either a static value or a Proc. If it is a Proc, it
131
- # will be invoked when each key is evaluated so that you can use application logic
132
- # to invalidate keys.
140
+ # If your cache is on a shared infrastructure, you can define a namespace
141
+ # for your cache entries. If a namespace is defined, it will be prefixed on
142
+ # to every key. The namespace can be either a static value or a Proc. If it
143
+ # is a Proc, it will be invoked when each key is evaluated so that you can
144
+ # use application logic to invalidate keys.
133
145
  #
134
- # cache.namespace = lambda { @last_mod_time } # Set the namespace to a variable
146
+ # cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
135
147
  # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
136
148
  #
137
- #
138
- # Caches can also store values in a compressed format to save space and reduce
139
- # time spent sending data. Since there is some overhead, values must be large
140
- # enough to warrant compression. To turn on compression either pass
141
- # <tt>:compress => true</tt> in the initializer or to +fetch+ or +write+.
142
- # To specify the threshold at which to compress values, set
143
- # <tt>:compress_threshold</tt>. The default threshold is 32K.
149
+ # Caches can also store values in a compressed format to save space and
150
+ # reduce time spent sending data. Since there is overhead, values must be
151
+ # large enough to warrant compression. To turn on compression either pass
152
+ # <tt>compress: true</tt> in the initializer or as an option to +fetch+
153
+ # or +write+. To specify the threshold at which to compress values, set the
154
+ # <tt>:compress_threshold</tt> option. The default threshold is 16K.
144
155
  class Store
145
-
146
156
  cattr_accessor :logger, :instance_writer => true
147
157
 
148
158
  attr_reader :silence, :options
149
159
  alias :silence? :silence
150
160
 
151
- # Create a new cache. The options will be passed to any write method calls except
152
- # for :namespace which can be used to set the global namespace for the cache.
153
- def initialize (options = nil)
161
+ # Creates a new cache. The options will be passed to any write method calls
162
+ # except for <tt>:namespace</tt> which can be used to set the global
163
+ # namespace for the cache.
164
+ def initialize(options = nil)
154
165
  @options = options ? options.dup : {}
155
166
  end
156
167
 
157
- # Silence the logger.
168
+ # Silences the logger.
158
169
  def silence!
159
170
  @silence = true
160
171
  self
161
172
  end
162
173
 
163
- # Silence the logger within a block.
174
+ # Silences the logger within a block.
164
175
  def mute
165
176
  previous_silence, @silence = defined?(@silence) && @silence, true
166
177
  yield
@@ -168,137 +179,128 @@ module ActiveSupport
168
179
  @silence = previous_silence
169
180
  end
170
181
 
171
- # Set to true if cache stores should be instrumented. Default is false.
172
- def self.instrument=(boolean)
173
- Thread.current[:instrument_cache_store] = boolean
174
- end
175
-
176
- def self.instrument
177
- Thread.current[:instrument_cache_store] || false
178
- end
179
-
180
182
  # Fetches data from the cache, using the given key. If there is data in
181
183
  # the cache with the given key, then that data is returned.
182
184
  #
183
- # If there is no such data in the cache (a cache miss occurred),
184
- # then nil will be returned. However, if a block has been passed, then
185
- # that block will be run in the event of a cache miss. The return value
186
- # of the block will be written to the cache under the given cache key,
187
- # and that return value will be returned.
185
+ # If there is no such data in the cache (a cache miss), then +nil+ will be
186
+ # returned. However, if a block has been passed, that block will be passed
187
+ # the key and executed in the event of a cache miss. The return value of the
188
+ # block will be written to the cache under the given cache key, and that
189
+ # return value will be returned.
188
190
  #
189
- # cache.write("today", "Monday")
190
- # cache.fetch("today") # => "Monday"
191
+ # cache.write('today', 'Monday')
192
+ # cache.fetch('today') # => "Monday"
191
193
  #
192
- # cache.fetch("city") # => nil
193
- # cache.fetch("city") do
194
- # "Duckburgh"
194
+ # cache.fetch('city') # => nil
195
+ # cache.fetch('city') do
196
+ # 'Duckburgh'
195
197
  # end
196
- # cache.fetch("city") # => "Duckburgh"
198
+ # cache.fetch('city') # => "Duckburgh"
197
199
  #
198
200
  # You may also specify additional options via the +options+ argument.
199
- # Setting <tt>:force => true</tt> will force a cache miss:
201
+ # Setting <tt>force: true</tt> forces a cache "miss," meaning we treat
202
+ # the cache value as missing even if it's present. Passing a block is
203
+ # required when `force` is true so this always results in a cache write.
200
204
  #
201
- # cache.write("today", "Monday")
202
- # cache.fetch("today", :force => true) # => nil
205
+ # cache.write('today', 'Monday')
206
+ # cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday'
207
+ # cache.fetch('today', force: true) # => ArgumentError
208
+ #
209
+ # The `:force` option is useful when you're calling some other method to
210
+ # ask whether you should force a cache write. Otherwise, it's clearer to
211
+ # just call `Cache#write`.
203
212
  #
204
213
  # Setting <tt>:compress</tt> will store a large cache entry set by the call
205
214
  # in a compressed format.
206
215
  #
207
- #
208
- # Setting <tt>:expires_in</tt> will set an expiration time on the cache. All caches
209
- # support auto expiring content after a specified number of seconds. This value can
210
- # be specified as an option to the construction in which call all entries will be
211
- # affected. Or it can be supplied to the +fetch+ or +write+ method for just one entry.
212
- #
213
- # cache = ActiveSupport::Cache::MemoryStore.new(:expires_in => 5.minutes)
214
- # cache.write(key, value, :expires_in => 1.minute) # Set a lower value for one entry
215
- #
216
- # Setting <tt>:race_condition_ttl</tt> is very useful in situations where a cache entry
217
- # is used very frequently and is under heavy load. If a cache expires and due to heavy load
218
- # seven different processes will try to read data natively and then they all will try to
219
- # write to cache. To avoid that case the first process to find an expired cache entry will
220
- # bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>. Yes
221
- # this process is extending the time for a stale value by another few seconds. Because
222
- # of extended life of the previous cache, other processes will continue to use slightly
223
- # stale data for a just a big longer. In the meantime that first process will go ahead
224
- # and will write into cache the new value. After that all the processes will start
225
- # getting new value. The key is to keep <tt>:race_condition_ttl</tt> small.
226
- #
227
- # If the process regenerating the entry errors out, the entry will be regenerated
228
- # after the specified number of seconds. Also note that the life of stale cache is
229
- # extended only if it expired recently. Otherwise a new value is generated and
230
- # <tt>:race_condition_ttl</tt> does not play any role.
216
+ # Setting <tt>:expires_in</tt> will set an expiration time on the cache.
217
+ # All caches support auto-expiring content after a specified number of
218
+ # seconds. This value can be specified as an option to the constructor
219
+ # (in which case all entries will be affected), or it can be supplied to
220
+ # the +fetch+ or +write+ method to effect just one entry.
221
+ #
222
+ # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
223
+ # cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
224
+ #
225
+ # Setting <tt>:race_condition_ttl</tt> is very useful in situations where
226
+ # a cache entry is used very frequently and is under heavy load. If a
227
+ # cache expires and due to heavy load several different processes will try
228
+ # to read data natively and then they all will try to write to cache. To
229
+ # avoid that case the first process to find an expired cache entry will
230
+ # bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.
231
+ # Yes, this process is extending the time for a stale value by another few
232
+ # seconds. Because of extended life of the previous cache, other processes
233
+ # will continue to use slightly stale data for a just a bit longer. In the
234
+ # meantime that first process will go ahead and will write into cache the
235
+ # new value. After that all the processes will start getting the new value.
236
+ # The key is to keep <tt>:race_condition_ttl</tt> small.
237
+ #
238
+ # If the process regenerating the entry errors out, the entry will be
239
+ # regenerated after the specified number of seconds. Also note that the
240
+ # life of stale cache is extended only if it expired recently. Otherwise
241
+ # a new value is generated and <tt>:race_condition_ttl</tt> does not play
242
+ # any role.
231
243
  #
232
244
  # # Set all values to expire after one minute.
233
- # cache = ActiveSupport::Cache::MemoryCache.new(:expires_in => 1.minute)
245
+ # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
234
246
  #
235
- # cache.write("foo", "original value")
247
+ # cache.write('foo', 'original value')
236
248
  # val_1 = nil
237
249
  # val_2 = nil
238
250
  # sleep 60
239
251
  #
240
252
  # Thread.new do
241
- # val_1 = cache.fetch("foo", :race_condition_ttl => 10) do
253
+ # val_1 = cache.fetch('foo', race_condition_ttl: 10) do
242
254
  # sleep 1
243
- # "new value 1"
255
+ # 'new value 1'
244
256
  # end
245
257
  # end
246
258
  #
247
259
  # Thread.new do
248
- # val_2 = cache.fetch("foo", :race_condition_ttl => 10) do
249
- # "new value 2"
260
+ # val_2 = cache.fetch('foo', race_condition_ttl: 10) do
261
+ # 'new value 2'
250
262
  # end
251
263
  # end
252
264
  #
253
- # # val_1 => "new value 1"
254
- # # val_2 => "original value"
255
- # # sleep 10 # First thread extend the life of cache by another 10 seconds
256
- # # cache.fetch("foo") => "new value 1"
265
+ # cache.fetch('foo') # => "original value"
266
+ # sleep 10 # First thread extended the life of cache by another 10 seconds
267
+ # cache.fetch('foo') # => "new value 1"
268
+ # val_1 # => "new value 1"
269
+ # val_2 # => "original value"
257
270
  #
258
271
  # Other options will be handled by the specific cache store implementation.
259
- # Internally, #fetch calls #read_entry, and calls #write_entry on a cache miss.
260
- # +options+ will be passed to the #read and #write calls.
272
+ # Internally, #fetch calls #read_entry, and calls #write_entry on a cache
273
+ # miss. +options+ will be passed to the #read and #write calls.
261
274
  #
262
275
  # For example, MemCacheStore's #write method supports the +:raw+
263
276
  # option, which tells the memcached server to store all values as strings.
264
277
  # We can use this option with #fetch too:
265
278
  #
266
279
  # cache = ActiveSupport::Cache::MemCacheStore.new
267
- # cache.fetch("foo", :force => true, :raw => true) do
280
+ # cache.fetch("foo", force: true, raw: true) do
268
281
  # :bar
269
282
  # end
270
- # cache.fetch("foo") # => "bar"
283
+ # cache.fetch('foo') # => "bar"
271
284
  def fetch(name, options = nil)
272
285
  if block_given?
273
286
  options = merged_options(options)
274
- key = namespaced_key(name, options)
275
- unless options[:force]
276
- entry = instrument(:read, name, options) do |payload|
277
- payload[:super_operation] = :fetch if payload
278
- read_entry(key, options)
279
- end
280
- end
281
- if entry && entry.expired?
282
- race_ttl = options[:race_condition_ttl].to_f
283
- if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl
284
- entry.expires_at = Time.now + race_ttl
285
- write_entry(key, entry, :expires_in => race_ttl * 2)
286
- else
287
- delete_entry(key, options)
288
- end
289
- entry = nil
287
+ key = normalize_key(name, options)
288
+
289
+ entry = nil
290
+ instrument(:read, name, options) do |payload|
291
+ cached_entry = read_entry(key, options) unless options[:force]
292
+ entry = handle_expired_entry(cached_entry, key, options)
293
+ payload[:super_operation] = :fetch if payload
294
+ payload[:hit] = !!entry if payload
290
295
  end
291
296
 
292
297
  if entry
293
- instrument(:fetch_hit, name, options) { |payload| }
294
- entry.value
298
+ get_entry_value(entry, name, options)
295
299
  else
296
- result = instrument(:generate, name, options) do |payload|
297
- yield
298
- end
299
- write(name, result, options)
300
- result
300
+ save_block_result_to_cache(name, options) { |_name| yield _name }
301
301
  end
302
+ elsif options && options[:force]
303
+ raise ArgumentError, 'Missing block: Calling `Cache#fetch` with `force: true` requires a block.'
302
304
  else
303
305
  read(name, options)
304
306
  end
@@ -306,12 +308,12 @@ module ActiveSupport
306
308
 
307
309
  # Fetches data from the cache, using the given key. If there is data in
308
310
  # the cache with the given key, then that data is returned. Otherwise,
309
- # nil is returned.
311
+ # +nil+ is returned.
310
312
  #
311
313
  # Options are passed to the underlying cache implementation.
312
314
  def read(name, options = nil)
313
315
  options = merged_options(options)
314
- key = namespaced_key(name, options)
316
+ key = normalize_key(name, options)
315
317
  instrument(:read, name, options) do |payload|
316
318
  entry = read_entry(key, options)
317
319
  if entry
@@ -330,7 +332,7 @@ module ActiveSupport
330
332
  end
331
333
  end
332
334
 
333
- # Read multiple values at once from the cache. Options can be passed
335
+ # Reads multiple values at once from the cache. Options can be passed
334
336
  # in the last argument.
335
337
  #
336
338
  # Some cache implementation may optimize this method.
@@ -339,9 +341,10 @@ module ActiveSupport
339
341
  def read_multi(*names)
340
342
  options = names.extract_options!
341
343
  options = merged_options(options)
344
+
342
345
  results = {}
343
346
  names.each do |name|
344
- key = namespaced_key(name, options)
347
+ key = normalize_key(name, options)
345
348
  entry = read_entry(key, options)
346
349
  if entry
347
350
  if entry.expired?
@@ -354,14 +357,45 @@ module ActiveSupport
354
357
  results
355
358
  end
356
359
 
360
+ # Fetches data from the cache, using the given keys. If there is data in
361
+ # the cache with the given keys, then that data is returned. Otherwise,
362
+ # the supplied block is called for each key for which there was no data,
363
+ # and the result will be written to the cache and returned.
364
+ #
365
+ # Options are passed to the underlying cache implementation.
366
+ #
367
+ # Returns a hash with the data for each of the names. For example:
368
+ #
369
+ # cache.write("bim", "bam")
370
+ # cache.fetch_multi("bim", "unknown_key") do |key|
371
+ # "Fallback value for key: #{key}"
372
+ # end
373
+ # # => { "bim" => "bam",
374
+ # # "unknown_key" => "Fallback value for key: unknown_key" }
375
+ #
376
+ def fetch_multi(*names)
377
+ options = names.extract_options!
378
+ options = merged_options(options)
379
+ results = read_multi(*names, options)
380
+
381
+ names.each_with_object({}) do |name, memo|
382
+ memo[name] = results.fetch(name) do
383
+ value = yield name
384
+ write(name, value, options)
385
+ value
386
+ end
387
+ end
388
+ end
389
+
357
390
  # Writes the value to the cache, with the key.
358
391
  #
359
392
  # Options are passed to the underlying cache implementation.
360
393
  def write(name, value, options = nil)
361
394
  options = merged_options(options)
362
- instrument(:write, name, options) do |payload|
395
+
396
+ instrument(:write, name, options) do
363
397
  entry = Entry.new(value, options)
364
- write_entry(namespaced_key(name, options), entry, options)
398
+ write_entry(normalize_key(name, options), entry, options)
365
399
  end
366
400
  end
367
401
 
@@ -370,27 +404,25 @@ module ActiveSupport
370
404
  # Options are passed to the underlying cache implementation.
371
405
  def delete(name, options = nil)
372
406
  options = merged_options(options)
373
- instrument(:delete, name) do |payload|
374
- delete_entry(namespaced_key(name, options), options)
407
+
408
+ instrument(:delete, name) do
409
+ delete_entry(normalize_key(name, options), options)
375
410
  end
376
411
  end
377
412
 
378
- # Return true if the cache contains an entry for the given key.
413
+ # Returns +true+ if the cache contains an entry for the given key.
379
414
  #
380
415
  # Options are passed to the underlying cache implementation.
381
416
  def exist?(name, options = nil)
382
417
  options = merged_options(options)
383
- instrument(:exist?, name) do |payload|
384
- entry = read_entry(namespaced_key(name, options), options)
385
- if entry && !entry.expired?
386
- true
387
- else
388
- false
389
- end
418
+
419
+ instrument(:exist?, name) do
420
+ entry = read_entry(normalize_key(name, options), options)
421
+ (entry && !entry.expired?) || false
390
422
  end
391
423
  end
392
424
 
393
- # Delete all entries with keys matching the pattern.
425
+ # Deletes all entries with keys matching the pattern.
394
426
  #
395
427
  # Options are passed to the underlying cache implementation.
396
428
  #
@@ -399,7 +431,7 @@ module ActiveSupport
399
431
  raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
400
432
  end
401
433
 
402
- # Increment an integer value in the cache.
434
+ # Increments an integer value in the cache.
403
435
  #
404
436
  # Options are passed to the underlying cache implementation.
405
437
  #
@@ -408,7 +440,7 @@ module ActiveSupport
408
440
  raise NotImplementedError.new("#{self.class.name} does not support increment")
409
441
  end
410
442
 
411
- # Increment an integer value in the cache.
443
+ # Decrements an integer value in the cache.
412
444
  #
413
445
  # Options are passed to the underlying cache implementation.
414
446
  #
@@ -417,7 +449,7 @@ module ActiveSupport
417
449
  raise NotImplementedError.new("#{self.class.name} does not support decrement")
418
450
  end
419
451
 
420
- # Cleanup the cache by removing expired entries.
452
+ # Cleanups the cache by removing expired entries.
421
453
  #
422
454
  # Options are passed to the underlying cache implementation.
423
455
  #
@@ -426,10 +458,10 @@ module ActiveSupport
426
458
  raise NotImplementedError.new("#{self.class.name} does not support cleanup")
427
459
  end
428
460
 
429
- # Clear the entire cache. Be careful with this method since it could
461
+ # Clears the entire cache. Be careful with this method since it could
430
462
  # affect other processes if shared cache is being used.
431
463
  #
432
- # Options are passed to the underlying cache implementation.
464
+ # The options hash is passed to the underlying cache implementation.
433
465
  #
434
466
  # All implementations may not support this method.
435
467
  def clear(options = nil)
@@ -437,9 +469,10 @@ module ActiveSupport
437
469
  end
438
470
 
439
471
  protected
440
- # Add the namespace defined in the options to a pattern designed to match keys.
441
- # Implementations that support delete_matched should call this method to translate
442
- # a pattern that matches names into one that matches namespaced keys.
472
+ # Adds the namespace defined in the options to a pattern designed to
473
+ # match keys. Implementations that support delete_matched should call
474
+ # this method to translate a pattern that matches names into one that
475
+ # matches namespaced keys.
443
476
  def key_matcher(pattern, options)
444
477
  prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
445
478
  if prefix
@@ -455,23 +488,26 @@ module ActiveSupport
455
488
  end
456
489
  end
457
490
 
458
- # Read an entry from the cache implementation. Subclasses must implement this method.
491
+ # Reads an entry from the cache implementation. Subclasses must implement
492
+ # this method.
459
493
  def read_entry(key, options) # :nodoc:
460
494
  raise NotImplementedError.new
461
495
  end
462
496
 
463
- # Write an entry to the cache implementation. Subclasses must implement this method.
497
+ # Writes an entry to the cache implementation. Subclasses must implement
498
+ # this method.
464
499
  def write_entry(key, entry, options) # :nodoc:
465
500
  raise NotImplementedError.new
466
501
  end
467
502
 
468
- # Delete an entry from the cache implementation. Subclasses must implement this method.
503
+ # Deletes an entry from the cache implementation. Subclasses must
504
+ # implement this method.
469
505
  def delete_entry(key, options) # :nodoc:
470
506
  raise NotImplementedError.new
471
507
  end
472
508
 
473
509
  private
474
- # Merge the default options with ones specific to a method call.
510
+ # Merges the default options with ones specific to a method call.
475
511
  def merged_options(call_options) # :nodoc:
476
512
  if call_options
477
513
  options.merge(call_options)
@@ -480,8 +516,8 @@ module ActiveSupport
480
516
  end
481
517
  end
482
518
 
483
- # Expand key to be a consistent string value. Invoke +cache_key+ if
484
- # object responds to +cache_key+. Otherwise, to_param method will be
519
+ # Expands key to be a consistent string value. Invokes +cache_key+ if
520
+ # object responds to +cache_key+. Otherwise, +to_param+ method will be
485
521
  # called. If the key is a Hash, then keys will be sorted alphabetically.
486
522
  def expanded_key(key) # :nodoc:
487
523
  return key.cache_key.to_s if key.respond_to?(:cache_key)
@@ -500,8 +536,9 @@ module ActiveSupport
500
536
  key.to_param
501
537
  end
502
538
 
503
- # Prefix a key with the namespace. Namespace and key will be delimited with a colon.
504
- def namespaced_key(key, options)
539
+ # Prefixes a key with the namespace. Namespace and key will be delimited
540
+ # with a colon.
541
+ def normalize_key(key, options)
505
542
  key = expanded_key(key)
506
543
  namespace = options[:namespace] if options
507
544
  prefix = namespace.is_a?(Proc) ? namespace.call : namespace
@@ -509,127 +546,156 @@ module ActiveSupport
509
546
  key
510
547
  end
511
548
 
549
+ def namespaced_key(*args)
550
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
551
+ `namespaced_key` is deprecated and will be removed from Rails 5.1.
552
+ Please use `normalize_key` which will return a fully resolved key.
553
+ MESSAGE
554
+ normalize_key(*args)
555
+ end
556
+
512
557
  def instrument(operation, key, options = nil)
513
- log(operation, key, options)
558
+ log { "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" }
514
559
 
515
- if self.class.instrument
516
- payload = { :key => key }
517
- payload.merge!(options) if options.is_a?(Hash)
518
- ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
519
- else
520
- yield(nil)
521
- end
560
+ payload = { :key => key }
561
+ payload.merge!(options) if options.is_a?(Hash)
562
+ ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
522
563
  end
523
564
 
524
- def log(operation, key, options = nil)
565
+ def log
525
566
  return unless logger && logger.debug? && !silence?
526
- logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
567
+ logger.debug(yield)
527
568
  end
528
- end
529
569
 
530
- # Entry that is put into caches. It supports expiration time on entries and can compress values
531
- # to save space in the cache.
532
- class Entry
533
- attr_reader :created_at, :expires_in
570
+ def handle_expired_entry(entry, key, options)
571
+ if entry && entry.expired?
572
+ race_ttl = options[:race_condition_ttl].to_i
573
+ if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
574
+ # When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
575
+ # for a brief period while the entry is being recalculated.
576
+ entry.expires_at = Time.now + race_ttl
577
+ write_entry(key, entry, :expires_in => race_ttl * 2)
578
+ else
579
+ delete_entry(key, options)
580
+ end
581
+ entry = nil
582
+ end
583
+ entry
584
+ end
534
585
 
535
- DEFAULT_COMPRESS_LIMIT = 16.kilobytes
586
+ def get_entry_value(entry, name, options)
587
+ instrument(:fetch_hit, name, options) { }
588
+ entry.value
589
+ end
536
590
 
537
- class << self
538
- # Create an entry with internal attributes set. This method is intended to be
539
- # used by implementations that store cache entries in a native format instead
540
- # of as serialized Ruby objects.
541
- def create (raw_value, created_at, options = {})
542
- entry = new(nil)
543
- entry.instance_variable_set(:@value, raw_value)
544
- entry.instance_variable_set(:@created_at, created_at.to_f)
545
- entry.instance_variable_set(:@compressed, !!options[:compressed])
546
- entry.instance_variable_set(:@expires_in, options[:expires_in])
547
- entry
591
+ def save_block_result_to_cache(name, options)
592
+ result = instrument(:generate, name, options) do
593
+ yield(name)
594
+ end
595
+
596
+ write(name, result, options)
597
+ result
548
598
  end
549
- end
599
+ end
550
600
 
551
- # Create a new cache entry for the specified value. Options supported are
601
+ # This class is used to represent cache entries. Cache entries have a value and an optional
602
+ # expiration time. The expiration time is used to support the :race_condition_ttl option
603
+ # on the cache.
604
+ #
605
+ # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
606
+ # using short instance variable names that are lazily defined.
607
+ class Entry # :nodoc:
608
+ DEFAULT_COMPRESS_LIMIT = 16.kilobytes
609
+
610
+ # Creates a new cache entry for the specified value. Options supported are
552
611
  # +:compress+, +:compress_threshold+, and +:expires_in+.
553
612
  def initialize(value, options = {})
554
- @compressed = false
555
- @expires_in = options[:expires_in]
556
- @expires_in = @expires_in.to_f if @expires_in
557
- @created_at = Time.now.to_f
558
- if value
559
- if should_compress?(value, options)
560
- @value = Zlib::Deflate.deflate(Marshal.dump(value))
561
- @compressed = true
562
- else
563
- @value = value
564
- end
613
+ if should_compress?(value, options)
614
+ @value = compress(value)
615
+ @compressed = true
565
616
  else
566
- @value = nil
617
+ @value = value
567
618
  end
568
- end
569
619
 
570
- # Get the raw value. This value may be serialized and compressed.
571
- def raw_value
572
- @value
620
+ @created_at = Time.now.to_f
621
+ @expires_in = options[:expires_in]
622
+ @expires_in = @expires_in.to_f if @expires_in
573
623
  end
574
624
 
575
- # Get the value stored in the cache.
576
625
  def value
577
- if @value
578
- val = compressed? ? Marshal.load(Zlib::Inflate.inflate(@value)) : @value
579
- unless val.frozen?
580
- val.freeze rescue nil
581
- end
582
- val
583
- end
584
- end
585
-
586
- def compressed?
587
- @compressed
626
+ compressed? ? uncompress(@value) : @value
588
627
  end
589
628
 
590
- # Check if the entry is expired. The +expires_in+ parameter can override the
591
- # value set when the entry was created.
629
+ # Checks if the entry is expired. The +expires_in+ parameter can override
630
+ # the value set when the entry was created.
592
631
  def expired?
593
632
  @expires_in && @created_at + @expires_in <= Time.now.to_f
594
633
  end
595
634
 
596
- # Set a new time when the entry will expire.
597
- def expires_at=(time)
598
- if time
599
- @expires_in = time.to_f - @created_at
635
+ def expires_at
636
+ @expires_in ? @created_at + @expires_in : nil
637
+ end
638
+
639
+ def expires_at=(value)
640
+ if value
641
+ @expires_in = value.to_f - @created_at
600
642
  else
601
643
  @expires_in = nil
602
644
  end
603
645
  end
604
646
 
605
- # Seconds since the epoch when the entry will expire.
606
- def expires_at
607
- @expires_in ? @created_at + @expires_in : nil
608
- end
609
-
610
- # Returns the size of the cached value. This could be less than value.size
611
- # if the data is compressed.
647
+ # Returns the size of the cached value. This could be less than
648
+ # <tt>value.size</tt> if the data is compressed.
612
649
  def size
613
- if @value.nil?
614
- 0
615
- elsif @value.respond_to?(:bytesize)
616
- @value.bytesize
650
+ if defined?(@s)
651
+ @s
617
652
  else
618
- Marshal.dump(@value).bytesize
653
+ case value
654
+ when NilClass
655
+ 0
656
+ when String
657
+ @value.bytesize
658
+ else
659
+ @s = Marshal.dump(@value).bytesize
660
+ end
661
+ end
662
+ end
663
+
664
+ # Duplicates the value in a class. This is used by cache implementations that don't natively
665
+ # serialize entries to protect against accidental cache modifications.
666
+ def dup_value!
667
+ if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
668
+ if @value.is_a?(String)
669
+ @value = @value.dup
670
+ else
671
+ @value = Marshal.load(Marshal.dump(@value))
672
+ end
619
673
  end
620
674
  end
621
675
 
622
676
  private
623
677
  def should_compress?(value, options)
624
- if options[:compress] && value
625
- unless value.is_a?(Numeric)
626
- compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
627
- serialized_value = value.is_a?(String) ? value : Marshal.dump(value)
628
- return true if serialized_value.size >= compress_threshold
629
- end
678
+ if value && options[:compress]
679
+ compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
680
+ serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
681
+
682
+ return true if serialized_value_size >= compress_threshold
630
683
  end
684
+
631
685
  false
632
686
  end
687
+
688
+ def compressed?
689
+ defined?(@compressed) ? @compressed : false
690
+ end
691
+
692
+ def compress(value)
693
+ Zlib::Deflate.deflate(Marshal.dump(value))
694
+ end
695
+
696
+ def uncompress(value)
697
+ Marshal.load(Zlib::Inflate.inflate(value))
698
+ end
633
699
  end
634
700
  end
635
701
  end