activesupport 4.2.0 → 5.2.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 (254) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +366 -232
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +4 -5
  5. data/lib/active_support.rb +17 -7
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +48 -0
  8. data/lib/active_support/backtrace_cleaner.rb +7 -5
  9. data/lib/active_support/benchmarkable.rb +6 -4
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache.rb +271 -177
  12. data/lib/active_support/cache/file_store.rb +41 -35
  13. data/lib/active_support/cache/mem_cache_store.rb +97 -88
  14. data/lib/active_support/cache/memory_store.rb +27 -30
  15. data/lib/active_support/cache/null_store.rb +7 -8
  16. data/lib/active_support/cache/redis_cache_store.rb +454 -0
  17. data/lib/active_support/cache/strategy/local_cache.rb +67 -34
  18. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  19. data/lib/active_support/callbacks.rb +654 -560
  20. data/lib/active_support/concern.rb +5 -3
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  22. data/lib/active_support/concurrency/share_lock.rb +227 -0
  23. data/lib/active_support/configurable.rb +8 -5
  24. data/lib/active_support/core_ext.rb +3 -1
  25. data/lib/active_support/core_ext/array.rb +9 -6
  26. data/lib/active_support/core_ext/array/access.rb +29 -1
  27. data/lib/active_support/core_ext/array/conversions.rb +22 -18
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  30. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  31. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -3
  32. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  33. data/lib/active_support/core_ext/benchmark.rb +3 -1
  34. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  35. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  36. data/lib/active_support/core_ext/class.rb +4 -3
  37. data/lib/active_support/core_ext/class/attribute.rb +41 -22
  38. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  39. data/lib/active_support/core_ext/class/subclasses.rb +20 -8
  40. data/lib/active_support/core_ext/date.rb +6 -4
  41. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  42. data/lib/active_support/core_ext/date/blank.rb +14 -0
  43. data/lib/active_support/core_ext/date/calculations.rb +11 -9
  44. data/lib/active_support/core_ext/date/conversions.rb +31 -23
  45. data/lib/active_support/core_ext/date/zones.rb +4 -2
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +179 -56
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
  49. data/lib/active_support/core_ext/date_time.rb +7 -4
  50. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  51. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  52. data/lib/active_support/core_ext/date_time/calculations.rb +58 -20
  53. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  54. data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
  55. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  56. data/lib/active_support/core_ext/enumerable.rb +107 -28
  57. data/lib/active_support/core_ext/file.rb +3 -1
  58. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  59. data/lib/active_support/core_ext/hash.rb +11 -9
  60. data/lib/active_support/core_ext/hash/compact.rb +24 -15
  61. data/lib/active_support/core_ext/hash/conversions.rb +63 -43
  62. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  63. data/lib/active_support/core_ext/hash/except.rb +11 -8
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  65. data/lib/active_support/core_ext/hash/keys.rb +33 -27
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -8
  68. data/lib/active_support/core_ext/hash/transform_values.rb +16 -7
  69. data/lib/active_support/core_ext/integer.rb +5 -3
  70. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  71. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  72. data/lib/active_support/core_ext/integer/time.rb +11 -33
  73. data/lib/active_support/core_ext/kernel.rb +6 -5
  74. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  75. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  76. data/lib/active_support/core_ext/kernel/reporting.rb +4 -83
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  78. data/lib/active_support/core_ext/load_error.rb +3 -22
  79. data/lib/active_support/core_ext/marshal.rb +13 -10
  80. data/lib/active_support/core_ext/module.rb +14 -11
  81. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  82. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  83. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +43 -40
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +11 -12
  87. data/lib/active_support/core_ext/module/delegation.rb +121 -39
  88. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  89. data/lib/active_support/core_ext/module/introspection.rb +9 -9
  90. data/lib/active_support/core_ext/module/reachable.rb +5 -2
  91. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/name_error.rb +22 -2
  94. data/lib/active_support/core_ext/numeric.rb +6 -3
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +79 -74
  97. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +35 -38
  99. data/lib/active_support/core_ext/object.rb +14 -13
  100. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  101. data/lib/active_support/core_ext/object/blank.rb +29 -4
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  104. data/lib/active_support/core_ext/object/duplicable.rb +98 -45
  105. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +49 -19
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +6 -4
  110. data/lib/active_support/core_ext/object/try.rb +70 -22
  111. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  112. data/lib/active_support/core_ext/range.rb +7 -4
  113. data/lib/active_support/core_ext/range/conversions.rb +27 -7
  114. data/lib/active_support/core_ext/range/each.rb +19 -17
  115. data/lib/active_support/core_ext/range/include_range.rb +21 -19
  116. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  117. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  118. data/lib/active_support/core_ext/regexp.rb +6 -0
  119. data/lib/active_support/core_ext/securerandom.rb +25 -0
  120. data/lib/active_support/core_ext/string.rb +15 -13
  121. data/lib/active_support/core_ext/string/access.rb +9 -7
  122. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  123. data/lib/active_support/core_ext/string/conversions.rb +8 -5
  124. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  125. data/lib/active_support/core_ext/string/filters.rb +10 -8
  126. data/lib/active_support/core_ext/string/indent.rb +6 -4
  127. data/lib/active_support/core_ext/string/inflections.rb +61 -24
  128. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  129. data/lib/active_support/core_ext/string/multibyte.rb +15 -7
  130. data/lib/active_support/core_ext/string/output_safety.rb +35 -35
  131. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  132. data/lib/active_support/core_ext/string/strip.rb +4 -5
  133. data/lib/active_support/core_ext/string/zones.rb +4 -2
  134. data/lib/active_support/core_ext/time.rb +7 -5
  135. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  136. data/lib/active_support/core_ext/time/calculations.rb +101 -51
  137. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  138. data/lib/active_support/core_ext/time/conversions.rb +20 -13
  139. data/lib/active_support/core_ext/time/zones.rb +41 -7
  140. data/lib/active_support/core_ext/uri.rb +5 -4
  141. data/lib/active_support/current_attributes.rb +195 -0
  142. data/lib/active_support/dependencies.rb +143 -160
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/deprecation.rb +12 -9
  146. data/lib/active_support/deprecation/behaviors.rb +41 -12
  147. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  149. data/lib/active_support/deprecation/method_wrappers.rb +54 -21
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
  151. data/lib/active_support/deprecation/reporting.rb +32 -12
  152. data/lib/active_support/descendants_tracker.rb +2 -0
  153. data/lib/active_support/digest.rb +20 -0
  154. data/lib/active_support/duration.rb +326 -30
  155. data/lib/active_support/duration/iso8601_parser.rb +125 -0
  156. data/lib/active_support/duration/iso8601_serializer.rb +55 -0
  157. data/lib/active_support/encrypted_configuration.rb +49 -0
  158. data/lib/active_support/encrypted_file.rb +99 -0
  159. data/lib/active_support/evented_file_update_checker.rb +205 -0
  160. data/lib/active_support/execution_wrapper.rb +128 -0
  161. data/lib/active_support/executor.rb +8 -0
  162. data/lib/active_support/file_update_checker.rb +63 -37
  163. data/lib/active_support/gem_version.rb +4 -2
  164. data/lib/active_support/gzip.rb +7 -5
  165. data/lib/active_support/hash_with_indifferent_access.rb +130 -30
  166. data/lib/active_support/i18n.rb +8 -6
  167. data/lib/active_support/i18n_railtie.rb +34 -14
  168. data/lib/active_support/inflections.rb +13 -11
  169. data/lib/active_support/inflector.rb +7 -5
  170. data/lib/active_support/inflector/inflections.rb +61 -12
  171. data/lib/active_support/inflector/methods.rb +161 -136
  172. data/lib/active_support/inflector/transliterate.rb +48 -27
  173. data/lib/active_support/json.rb +4 -2
  174. data/lib/active_support/json/decoding.rb +16 -13
  175. data/lib/active_support/json/encoding.rb +15 -57
  176. data/lib/active_support/key_generator.rb +25 -25
  177. data/lib/active_support/lazy_load_hooks.rb +50 -20
  178. data/lib/active_support/locale/en.yml +2 -0
  179. data/lib/active_support/log_subscriber.rb +13 -10
  180. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  181. data/lib/active_support/logger.rb +54 -3
  182. data/lib/active_support/logger_silence.rb +12 -7
  183. data/lib/active_support/logger_thread_safe_level.rb +33 -0
  184. data/lib/active_support/message_encryptor.rb +173 -51
  185. data/lib/active_support/message_verifier.rb +150 -17
  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 +4 -2
  190. data/lib/active_support/multibyte/chars.rb +37 -24
  191. data/lib/active_support/multibyte/unicode.rb +100 -96
  192. data/lib/active_support/notifications.rb +11 -7
  193. data/lib/active_support/notifications/fanout.rb +10 -8
  194. data/lib/active_support/notifications/instrumenter.rb +27 -7
  195. data/lib/active_support/number_helper.rb +94 -68
  196. data/lib/active_support/number_helper/number_converter.rb +13 -11
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -9
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +9 -3
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +11 -9
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +9 -8
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +13 -4
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +23 -56
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/option_merger.rb +3 -1
  206. data/lib/active_support/ordered_hash.rb +6 -4
  207. data/lib/active_support/ordered_options.rb +22 -4
  208. data/lib/active_support/per_thread_registry.rb +13 -6
  209. data/lib/active_support/proxy_object.rb +2 -0
  210. data/lib/active_support/rails.rb +16 -8
  211. data/lib/active_support/railtie.rb +43 -9
  212. data/lib/active_support/reloader.rb +131 -0
  213. data/lib/active_support/rescuable.rb +108 -53
  214. data/lib/active_support/security_utils.rb +17 -6
  215. data/lib/active_support/string_inquirer.rb +11 -3
  216. data/lib/active_support/subscriber.rb +15 -14
  217. data/lib/active_support/tagged_logging.rb +14 -11
  218. data/lib/active_support/test_case.rb +18 -46
  219. data/lib/active_support/testing/assertions.rb +137 -20
  220. data/lib/active_support/testing/autorun.rb +4 -2
  221. data/lib/active_support/testing/constant_lookup.rb +2 -1
  222. data/lib/active_support/testing/declarative.rb +3 -1
  223. data/lib/active_support/testing/deprecation.rb +14 -10
  224. data/lib/active_support/testing/file_fixtures.rb +36 -0
  225. data/lib/active_support/testing/isolation.rb +34 -25
  226. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  227. data/lib/active_support/testing/setup_and_teardown.rb +12 -3
  228. data/lib/active_support/testing/stream.rb +44 -0
  229. data/lib/active_support/testing/tagged_logging.rb +3 -1
  230. data/lib/active_support/testing/time_helpers.rb +96 -27
  231. data/lib/active_support/time.rb +14 -12
  232. data/lib/active_support/time_with_zone.rb +195 -53
  233. data/lib/active_support/values/time_zone.rb +200 -61
  234. data/lib/active_support/values/unicode_tables.dat +0 -0
  235. data/lib/active_support/version.rb +3 -1
  236. data/lib/active_support/xml_mini.rb +69 -51
  237. data/lib/active_support/xml_mini/jdom.rb +116 -113
  238. data/lib/active_support/xml_mini/libxml.rb +17 -16
  239. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  240. data/lib/active_support/xml_mini/nokogiri.rb +15 -15
  241. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  242. data/lib/active_support/xml_mini/rexml.rb +17 -16
  243. metadata +55 -43
  244. data/lib/active_support/concurrency/latch.rb +0 -27
  245. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -14
  246. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  247. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  248. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  249. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -11
  250. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  251. data/lib/active_support/core_ext/object/itself.rb +0 -15
  252. data/lib/active_support/core_ext/struct.rb +0 -6
  253. data/lib/active_support/core_ext/thread.rb +0 -86
  254. data/lib/active_support/core_ext/time/marshal.rb +0 -30
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2005-2014 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
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
17
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
18
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
19
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc CHANGED
@@ -10,18 +10,18 @@ outside of Rails.
10
10
 
11
11
  The latest version of Active Support can be installed with RubyGems:
12
12
 
13
- % [sudo] gem install activesupport
13
+ $ gem install activesupport
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/4-2-stable/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,11 +30,10 @@ 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
 
37
37
  Feature requests should be discussed on the rails-core mailing list here:
38
38
 
39
39
  * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
40
-
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #--
2
- # Copyright (c) 2005-2014 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
@@ -21,21 +23,27 @@
21
23
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
24
  #++
23
25
 
24
- require 'securerandom'
26
+ require "securerandom"
25
27
  require "active_support/dependencies/autoload"
26
28
  require "active_support/version"
27
29
  require "active_support/logger"
28
30
  require "active_support/lazy_load_hooks"
31
+ require "active_support/core_ext/date_and_time/compatibility"
29
32
 
30
33
  module ActiveSupport
31
34
  extend ActiveSupport::Autoload
32
35
 
33
36
  autoload :Concern
37
+ autoload :CurrentAttributes
34
38
  autoload :Dependencies
35
39
  autoload :DescendantsTracker
40
+ autoload :ExecutionWrapper
41
+ autoload :Executor
36
42
  autoload :FileUpdateChecker
43
+ autoload :EventedFileUpdateChecker
37
44
  autoload :LogSubscriber
38
45
  autoload :Notifications
46
+ autoload :Reloader
39
47
 
40
48
  eager_autoload do
41
49
  autoload :BacktraceCleaner
@@ -45,6 +53,7 @@ module ActiveSupport
45
53
  autoload :Callbacks
46
54
  autoload :Configurable
47
55
  autoload :Deprecation
56
+ autoload :Digest
48
57
  autoload :Gzip
49
58
  autoload :Inflector
50
59
  autoload :JSON
@@ -59,6 +68,7 @@ module ActiveSupport
59
68
  autoload :StringInquirer
60
69
  autoload :TaggedLogging
61
70
  autoload :XmlMini
71
+ autoload :ArrayInquirer
62
72
  end
63
73
 
64
74
  autoload :Rescuable
@@ -71,14 +81,14 @@ module ActiveSupport
71
81
  NumberHelper.eager_load!
72
82
  end
73
83
 
74
- @@test_order = nil
84
+ cattr_accessor :test_order # :nodoc:
75
85
 
76
- def self.test_order=(new_order) # :nodoc:
77
- @@test_order = new_order
86
+ def self.to_time_preserves_timezone
87
+ DateAndTime::Compatibility.preserve_timezone
78
88
  end
79
89
 
80
- def self.test_order # :nodoc:
81
- @@test_order
90
+ def self.to_time_preserves_timezone=(value)
91
+ DateAndTime::Compatibility.preserve_timezone = value
82
92
  end
83
93
  end
84
94
 
@@ -1,3 +1,5 @@
1
- require 'active_support'
2
- require 'active_support/time'
3
- require 'active_support/core_ext'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/time"
5
+ require "active_support/core_ext"
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # Wrapping an array in an +ArrayInquirer+ gives a friendlier way to check
5
+ # its string-like contents:
6
+ #
7
+ # variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
8
+ #
9
+ # variants.phone? # => true
10
+ # variants.tablet? # => true
11
+ # variants.desktop? # => false
12
+ class ArrayInquirer < Array
13
+ # Passes each element of +candidates+ collection to ArrayInquirer collection.
14
+ # The method returns true if any element from the ArrayInquirer collection
15
+ # is equal to the stringified or symbolized form of any element in the +candidates+ collection.
16
+ #
17
+ # If +candidates+ collection is not given, method returns true.
18
+ #
19
+ # variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
20
+ #
21
+ # variants.any? # => true
22
+ # variants.any?(:phone, :tablet) # => true
23
+ # variants.any?('phone', 'desktop') # => true
24
+ # variants.any?(:desktop, :watch) # => false
25
+ def any?(*candidates)
26
+ if candidates.none?
27
+ super
28
+ else
29
+ candidates.any? do |candidate|
30
+ include?(candidate.to_sym) || include?(candidate.to_s)
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+ def respond_to_missing?(name, include_private = false)
37
+ (name[-1] == "?") || super
38
+ end
39
+
40
+ def method_missing(name, *args)
41
+ if name[-1] == "?"
42
+ any?(name[0..-2])
43
+ else
44
+ super
45
+ end
46
+ end
47
+ end
48
+ end
@@ -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
@@ -12,9 +14,9 @@ module ActiveSupport
12
14
  # is to exclude the output of a noisy library from the backtrace, so that you
13
15
  # can focus on the rest.
14
16
  #
15
- # bc = BacktraceCleaner.new
17
+ # bc = ActiveSupport::BacktraceCleaner.new
16
18
  # bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
17
- # bc.add_silencer { |line| line =~ /mongrel|rubygems/ } # skip any lines from mongrel or rubygems
19
+ # bc.add_silencer { |line| line =~ /puma|rubygems/ } # skip any lines from puma or rubygems
18
20
  # bc.clean(exception.backtrace) # perform the cleanup
19
21
  #
20
22
  # To reconfigure an existing BacktraceCleaner (like the default one in Rails)
@@ -25,7 +27,7 @@ module ActiveSupport
25
27
  # of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
26
28
  # These two methods will give you a completely untouched backtrace.
27
29
  #
28
- # Inspired by the Quiet Backtrace gem by Thoughtbot.
30
+ # Inspired by the Quiet Backtrace gem by thoughtbot.
29
31
  class BacktraceCleaner
30
32
  def initialize
31
33
  @filters, @silencers = [], []
@@ -59,8 +61,8 @@ module ActiveSupport
59
61
  # Adds a silencer from the block provided. If the silencer returns +true+
60
62
  # for a given line, it will be excluded from the clean backtrace.
61
63
  #
62
- # # Will reject all lines that include the word "mongrel", like "/gems/mongrel/server.rb" or "/app/my_mongrel_server/rb"
63
- # backtrace_cleaner.add_silencer { |line| line =~ /mongrel/ }
64
+ # # Will reject all lines that include the word "puma", like "/gems/puma/server.rb" or "/app/my_puma_server/rb"
65
+ # backtrace_cleaner.add_silencer { |line| line =~ /puma/ }
64
66
  def add_silencer(&block)
65
67
  @silencers << block
66
68
  end
@@ -1,5 +1,7 @@
1
- require 'active_support/core_ext/benchmark'
2
- require 'active_support/core_ext/hash/keys'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/benchmark"
4
+ require "active_support/core_ext/hash/keys"
3
5
 
4
6
  module ActiveSupport
5
7
  module Benchmarkable
@@ -38,8 +40,8 @@ module ActiveSupport
38
40
  options[:level] ||= :info
39
41
 
40
42
  result = nil
41
- ms = Benchmark.ms { result = options[:silence] ? silence { yield } : yield }
42
- logger.send(options[:level], '%s (%.1fms)' % [ message, ms ])
43
+ ms = Benchmark.ms { result = options[:silence] ? logger.silence { yield } : yield }
44
+ logger.send(options[:level], "%s (%.1fms)" % [ message, ms ])
43
45
  result
44
46
  else
45
47
  yield
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
- require 'builder'
4
+ require "builder"
3
5
  rescue LoadError => e
4
6
  $stderr.puts "You don't have builder installed in your application. Please add it to your Gemfile and run bundle install"
5
7
  raise e
@@ -1,33 +1,33 @@
1
- require 'benchmark'
2
- require 'zlib'
3
- require 'active_support/core_ext/array/extract_options'
4
- require 'active_support/core_ext/array/wrap'
5
- require 'active_support/core_ext/benchmark'
6
- require 'active_support/core_ext/module/attribute_accessors'
7
- require 'active_support/core_ext/numeric/bytes'
8
- require 'active_support/core_ext/numeric/time'
9
- require 'active_support/core_ext/object/to_param'
10
- require 'active_support/core_ext/string/inflections'
11
- require 'active_support/deprecation'
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+ require "active_support/core_ext/array/extract_options"
5
+ require "active_support/core_ext/array/wrap"
6
+ require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/core_ext/numeric/bytes"
8
+ require "active_support/core_ext/numeric/time"
9
+ require "active_support/core_ext/object/to_param"
10
+ require "active_support/core_ext/string/inflections"
12
11
 
13
12
  module ActiveSupport
14
13
  # See ActiveSupport::Cache::Store for documentation.
15
14
  module Cache
16
- autoload :FileStore, 'active_support/cache/file_store'
17
- autoload :MemoryStore, 'active_support/cache/memory_store'
18
- autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
19
- 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"
20
20
 
21
21
  # These options mean something to all cache implementations. Individual cache
22
22
  # implementations may support additional options.
23
23
  UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
24
24
 
25
25
  module Strategy
26
- autoload :LocalCache, 'active_support/cache/strategy/local_cache'
26
+ autoload :LocalCache, "active_support/cache/strategy/local_cache"
27
27
  end
28
28
 
29
29
  class << self
30
- # Creates a new CacheStore object according to the given options.
30
+ # Creates a new Store object according to the given options.
31
31
  #
32
32
  # If no arguments are passed to this method, then a new
33
33
  # ActiveSupport::Cache::MemoryStore object will be returned.
@@ -73,12 +73,12 @@ module ActiveSupport
73
73
  # each of elements in the array will be turned into parameters/keys and
74
74
  # concatenated into a single key. For example:
75
75
  #
76
- # expand_cache_key([:foo, :bar]) # => "foo/bar"
77
- # expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
76
+ # ActiveSupport::Cache.expand_cache_key([:foo, :bar]) # => "foo/bar"
77
+ # ActiveSupport::Cache.expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
78
78
  #
79
79
  # The +key+ argument can also respond to +cache_key+ or +to_param+.
80
80
  def expand_cache_key(key, namespace = nil)
81
- expanded_cache_key = namespace ? "#{namespace}/" : ""
81
+ expanded_cache_key = (namespace ? "#{namespace}/" : "").dup
82
82
 
83
83
  if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
84
84
  expanded_cache_key << "#{prefix}/"
@@ -91,16 +91,19 @@ module ActiveSupport
91
91
  private
92
92
  def retrieve_cache_key(key)
93
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
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
98
99
  end.to_s
99
100
  end
100
101
 
101
102
  # Obtains the specified cache store class, given the name of the +store+.
102
103
  # Raises an error when the store class cannot be found.
103
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.
104
107
  require "active_support/cache/#{store}"
105
108
  rescue LoadError => e
106
109
  raise "Could not find cache store adapter for #{store} (#{e})"
@@ -146,32 +149,48 @@ module ActiveSupport
146
149
  # cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
147
150
  # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
148
151
  #
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.
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.
155
157
  class Store
156
- cattr_accessor :logger, :instance_writer => true
158
+ cattr_accessor :logger, instance_writer: true
157
159
 
158
160
  attr_reader :silence, :options
159
161
  alias :silence? :silence
160
162
 
161
- # Create a new cache. The options will be passed to any write method calls
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
+
180
+ # Creates a new cache. The options will be passed to any write method calls
162
181
  # except for <tt>:namespace</tt> which can be used to set the global
163
182
  # namespace for the cache.
164
183
  def initialize(options = nil)
165
184
  @options = options ? options.dup : {}
166
185
  end
167
186
 
168
- # Silence the logger.
187
+ # Silences the logger.
169
188
  def silence!
170
189
  @silence = true
171
190
  self
172
191
  end
173
192
 
174
- # Silence the logger within a block.
193
+ # Silences the logger within a block.
175
194
  def mute
176
195
  previous_silence, @silence = defined?(@silence) && @silence, true
177
196
  yield
@@ -179,18 +198,6 @@ module ActiveSupport
179
198
  @silence = previous_silence
180
199
  end
181
200
 
182
- # :deprecated:
183
- def self.instrument=(boolean)
184
- ActiveSupport::Deprecation.warn "ActiveSupport::Cache.instrument= is deprecated and will be removed in Rails 5. Instrumentation is now always on so you can safely stop using it."
185
- true
186
- end
187
-
188
- # :deprecated:
189
- def self.instrument
190
- ActiveSupport::Deprecation.warn "ActiveSupport::Cache.instrument is deprecated and will be removed in Rails 5. Instrumentation is now always on so you can safely stop using it."
191
- true
192
- end
193
-
194
201
  # Fetches data from the cache, using the given key. If there is data in
195
202
  # the cache with the given key, then that data is returned.
196
203
  #
@@ -210,13 +217,19 @@ module ActiveSupport
210
217
  # cache.fetch('city') # => "Duckburgh"
211
218
  #
212
219
  # You may also specify additional options via the +options+ argument.
213
- # Setting <tt>force: true</tt> will force a cache miss:
220
+ # Setting <tt>force: true</tt> forces a cache "miss," meaning we treat
221
+ # the cache value as missing even if it's present. Passing a block is
222
+ # required when +force+ is true so this always results in a cache write.
214
223
  #
215
224
  # cache.write('today', 'Monday')
216
- # cache.fetch('today', force: true) # => nil
225
+ # cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday'
226
+ # cache.fetch('today', force: true) # => ArgumentError
217
227
  #
218
- # Setting <tt>:compress</tt> will store a large cache entry set by the call
219
- # in a compressed format.
228
+ # The +:force+ option is useful when you're calling some other method to
229
+ # ask whether you should force a cache write. Otherwise, it's clearer to
230
+ # just call <tt>Cache#write</tt>.
231
+ #
232
+ # Setting <tt>compress: false</tt> disables compression of the cache entry.
220
233
  #
221
234
  # Setting <tt>:expires_in</tt> will set an expiration time on the cache.
222
235
  # All caches support auto-expiring content after a specified number of
@@ -227,6 +240,10 @@ module ActiveSupport
227
240
  # cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
228
241
  # cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
229
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
+ #
230
247
  # Setting <tt>:race_condition_ttl</tt> is very useful in situations where
231
248
  # a cache entry is used very frequently and is under heavy load. If a
232
249
  # cache expires and due to heavy load several different processes will try
@@ -255,22 +272,23 @@ module ActiveSupport
255
272
  # sleep 60
256
273
  #
257
274
  # Thread.new do
258
- # val_1 = cache.fetch('foo', race_condition_ttl: 10) do
275
+ # val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
259
276
  # sleep 1
260
277
  # 'new value 1'
261
278
  # end
262
279
  # end
263
280
  #
264
281
  # Thread.new do
265
- # val_2 = cache.fetch('foo', race_condition_ttl: 10) do
282
+ # val_2 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
266
283
  # 'new value 2'
267
284
  # end
268
285
  # end
269
286
  #
270
- # # val_1 => "new value 1"
271
- # # val_2 => "original value"
272
- # # sleep 10 # First thread extend the life of cache by another 10 seconds
273
- # # cache.fetch('foo') => "new value 1"
287
+ # cache.fetch('foo') # => "original value"
288
+ # sleep 10 # First thread extended the life of cache by another 10 seconds
289
+ # cache.fetch('foo') # => "new value 1"
290
+ # val_1 # => "new value 1"
291
+ # val_2 # => "original value"
274
292
  #
275
293
  # Other options will be handled by the specific cache store implementation.
276
294
  # Internally, #fetch calls #read_entry, and calls #write_entry on a cache
@@ -288,36 +306,53 @@ module ActiveSupport
288
306
  def fetch(name, options = nil)
289
307
  if block_given?
290
308
  options = merged_options(options)
291
- key = namespaced_key(name, options)
309
+ key = normalize_key(name, options)
292
310
 
293
- cached_entry = find_cached_entry(key, name, options) unless options[:force]
294
- entry = handle_expired_entry(cached_entry, key, options)
311
+ entry = nil
312
+ instrument(:read, name, options) do |payload|
313
+ cached_entry = read_entry(key, options) unless options[:force]
314
+ entry = handle_expired_entry(cached_entry, key, options)
315
+ entry = nil if entry && entry.mismatched?(normalize_version(name, options))
316
+ payload[:super_operation] = :fetch if payload
317
+ payload[:hit] = !!entry if payload
318
+ end
295
319
 
296
320
  if entry
297
321
  get_entry_value(entry, name, options)
298
322
  else
299
323
  save_block_result_to_cache(name, options) { |_name| yield _name }
300
324
  end
325
+ elsif options && options[:force]
326
+ raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
301
327
  else
302
328
  read(name, options)
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 = namespaced_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
@@ -329,7 +364,7 @@ module ActiveSupport
329
364
  end
330
365
  end
331
366
 
332
- # Read multiple values at once from the cache. Options can be passed
367
+ # Reads multiple values at once from the cache. Options can be passed
333
368
  # in the last argument.
334
369
  #
335
370
  # Some cache implementation may optimize this method.
@@ -338,44 +373,64 @@ module ActiveSupport
338
373
  def read_multi(*names)
339
374
  options = names.extract_options!
340
375
  options = merged_options(options)
341
- results = {}
342
- names.each do |name|
343
- key = namespaced_key(name, options)
344
- entry = read_entry(key, options)
345
- if entry
346
- if entry.expired?
347
- delete_entry(key, options)
348
- else
349
- results[name] = entry.value
350
- end
376
+
377
+ instrument :read_multi, names, options do |payload|
378
+ read_multi_entries(names, options).tap do |results|
379
+ payload[:hits] = results.keys
351
380
  end
352
381
  end
353
- 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
354
395
  end
355
396
 
356
397
  # Fetches data from the cache, using the given keys. If there is data in
357
398
  # the cache with the given keys, then that data is returned. Otherwise,
358
399
  # the supplied block is called for each key for which there was no data,
359
400
  # and the result will be written to the cache and returned.
401
+ # Therefore, you need to pass a block that returns the data to be written
402
+ # to the cache. If you do not want to write the cache when the cache is
403
+ # not found, use #read_multi.
360
404
  #
361
405
  # Options are passed to the underlying cache implementation.
362
406
  #
363
407
  # Returns a hash with the data for each of the names. For example:
364
408
  #
365
409
  # cache.write("bim", "bam")
366
- # cache.fetch_multi("bim", "boom") { |key| key * 2 }
367
- # # => { "bam" => "bam", "boom" => "boomboom" }
410
+ # cache.fetch_multi("bim", "unknown_key") do |key|
411
+ # "Fallback value for key: #{key}"
412
+ # end
413
+ # # => { "bim" => "bam",
414
+ # # "unknown_key" => "Fallback value for key: unknown_key" }
368
415
  #
369
416
  def fetch_multi(*names)
417
+ raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
418
+
370
419
  options = names.extract_options!
371
420
  options = merged_options(options)
372
- results = read_multi(*names, options)
373
421
 
374
- names.each_with_object({}) do |name, memo|
375
- memo[name] = results.fetch(name) do
376
- value = yield name
377
- write(name, value, options)
378
- 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
379
434
  end
380
435
  end
381
436
  end
@@ -387,8 +442,8 @@ module ActiveSupport
387
442
  options = merged_options(options)
388
443
 
389
444
  instrument(:write, name, options) do
390
- entry = Entry.new(value, options)
391
- write_entry(namespaced_key(name, options), entry, options)
445
+ entry = Entry.new(value, options.merge(version: normalize_version(name, options)))
446
+ write_entry(normalize_key(name, options), entry, options)
392
447
  end
393
448
  end
394
449
 
@@ -399,7 +454,7 @@ module ActiveSupport
399
454
  options = merged_options(options)
400
455
 
401
456
  instrument(:delete, name) do
402
- delete_entry(namespaced_key(name, options), options)
457
+ delete_entry(normalize_key(name, options), options)
403
458
  end
404
459
  end
405
460
 
@@ -410,12 +465,12 @@ module ActiveSupport
410
465
  options = merged_options(options)
411
466
 
412
467
  instrument(:exist?, name) do
413
- entry = read_entry(namespaced_key(name, options), options)
414
- (entry && !entry.expired?) || false
468
+ entry = read_entry(normalize_key(name, options), options)
469
+ (entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
415
470
  end
416
471
  end
417
472
 
418
- # Delete all entries with keys matching the pattern.
473
+ # Deletes all entries with keys matching the pattern.
419
474
  #
420
475
  # Options are passed to the underlying cache implementation.
421
476
  #
@@ -424,7 +479,7 @@ module ActiveSupport
424
479
  raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
425
480
  end
426
481
 
427
- # Increment an integer value in the cache.
482
+ # Increments an integer value in the cache.
428
483
  #
429
484
  # Options are passed to the underlying cache implementation.
430
485
  #
@@ -433,7 +488,7 @@ module ActiveSupport
433
488
  raise NotImplementedError.new("#{self.class.name} does not support increment")
434
489
  end
435
490
 
436
- # Decrement an integer value in the cache.
491
+ # Decrements an integer value in the cache.
437
492
  #
438
493
  # Options are passed to the underlying cache implementation.
439
494
  #
@@ -442,7 +497,7 @@ module ActiveSupport
442
497
  raise NotImplementedError.new("#{self.class.name} does not support decrement")
443
498
  end
444
499
 
445
- # Cleanup the cache by removing expired entries.
500
+ # Cleanups the cache by removing expired entries.
446
501
  #
447
502
  # Options are passed to the underlying cache implementation.
448
503
  #
@@ -451,7 +506,7 @@ module ActiveSupport
451
506
  raise NotImplementedError.new("#{self.class.name} does not support cleanup")
452
507
  end
453
508
 
454
- # Clear the entire cache. Be careful with this method since it could
509
+ # Clears the entire cache. Be careful with this method since it could
455
510
  # affect other processes if shared cache is being used.
456
511
  #
457
512
  # The options hash is passed to the underlying cache implementation.
@@ -461,16 +516,16 @@ module ActiveSupport
461
516
  raise NotImplementedError.new("#{self.class.name} does not support clear")
462
517
  end
463
518
 
464
- protected
465
- # Add the namespace defined in the options to a pattern designed to
519
+ private
520
+ # Adds the namespace defined in the options to a pattern designed to
466
521
  # match keys. Implementations that support delete_matched should call
467
522
  # this method to translate a pattern that matches names into one that
468
523
  # matches namespaced keys.
469
- def key_matcher(pattern, options)
524
+ def key_matcher(pattern, options) # :doc:
470
525
  prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
471
526
  if prefix
472
527
  source = pattern.source
473
- if source.start_with?('^')
528
+ if source.start_with?("^")
474
529
  source = source[1, source.length]
475
530
  else
476
531
  source = ".*#{source[0, source.length]}"
@@ -481,27 +536,56 @@ module ActiveSupport
481
536
  end
482
537
  end
483
538
 
484
- # Read an entry from the cache implementation. Subclasses must implement
539
+ # Reads an entry from the cache implementation. Subclasses must implement
485
540
  # this method.
486
- def read_entry(key, options) # :nodoc:
541
+ def read_entry(key, options)
487
542
  raise NotImplementedError.new
488
543
  end
489
544
 
490
- # Write an entry to the cache implementation. Subclasses must implement
545
+ # Writes an entry to the cache implementation. Subclasses must implement
491
546
  # this method.
492
- def write_entry(key, entry, options) # :nodoc:
547
+ def write_entry(key, entry, options)
493
548
  raise NotImplementedError.new
494
549
  end
495
550
 
496
- # Delete an entry from the cache implementation. Subclasses must
551
+ # Reads multiple entries from the cache implementation. Subclasses MAY
497
552
  # implement this method.
498
- def delete_entry(key, options) # :nodoc:
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
+
581
+ # Deletes an entry from the cache implementation. Subclasses must
582
+ # implement this method.
583
+ def delete_entry(key, options)
499
584
  raise NotImplementedError.new
500
585
  end
501
586
 
502
- private
503
- # Merge the default options with ones specific to a method call.
504
- def merged_options(call_options) # :nodoc:
587
+ # Merges the default options with ones specific to a method call.
588
+ def merged_options(call_options)
505
589
  if call_options
506
590
  options.merge(call_options)
507
591
  else
@@ -509,64 +593,89 @@ module ActiveSupport
509
593
  end
510
594
  end
511
595
 
512
- # Expand key to be a consistent string value. Invoke +cache_key+ if
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
+
626
+ # Expands key to be a consistent string value. Invokes +cache_key+ if
513
627
  # object responds to +cache_key+. Otherwise, +to_param+ method will be
514
628
  # called. If the key is a Hash, then keys will be sorted alphabetically.
515
- def expanded_key(key) # :nodoc:
629
+ def expanded_key(key)
516
630
  return key.cache_key.to_s if key.respond_to?(:cache_key)
517
631
 
518
632
  case key
519
633
  when Array
520
634
  if key.size > 1
521
- key = key.collect{|element| expanded_key(element)}
635
+ key = key.collect { |element| expanded_key(element) }
522
636
  else
523
637
  key = key.first
524
638
  end
525
639
  when Hash
526
- key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
640
+ key = key.sort_by { |k, _| k.to_s }.collect { |k, v| "#{k}=#{v}" }
527
641
  end
528
642
 
529
643
  key.to_param
530
644
  end
531
645
 
532
- # Prefix a key with the namespace. Namespace and key will be delimited
533
- # with a colon.
534
- def namespaced_key(key, options)
535
- key = expanded_key(key)
536
- namespace = options[:namespace] if options
537
- prefix = namespace.is_a?(Proc) ? namespace.call : namespace
538
- key = "#{prefix}:#{key}" if prefix
539
- 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
540
656
  end
541
657
 
542
658
  def instrument(operation, key, options = nil)
543
- log(operation, key, options)
659
+ log { "Cache #{operation}: #{normalize_key(key, options)}#{options.blank? ? "" : " (#{options.inspect})"}" }
544
660
 
545
- payload = { :key => key }
661
+ payload = { key: key }
546
662
  payload.merge!(options) if options.is_a?(Hash)
547
- ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
663
+ ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload) { yield(payload) }
548
664
  end
549
665
 
550
- def log(operation, key, options = nil)
666
+ def log
551
667
  return unless logger && logger.debug? && !silence?
552
- logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
553
- end
554
-
555
- def find_cached_entry(key, name, options)
556
- instrument(:read, name, options) do |payload|
557
- payload[:super_operation] = :fetch if payload
558
- read_entry(key, options)
559
- end
668
+ logger.debug(yield)
560
669
  end
561
670
 
562
671
  def handle_expired_entry(entry, key, options)
563
672
  if entry && entry.expired?
564
673
  race_ttl = options[:race_condition_ttl].to_i
565
- if race_ttl && (Time.now.to_f - entry.expires_at <= race_ttl)
566
- # When an entry has :race_condition_ttl defined, put the stale entry back into the cache
567
- # for a brief period while the entry is begin recalculated.
674
+ if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
675
+ # When an entry has a positive :race_condition_ttl defined, put the stale entry back into the cache
676
+ # for a brief period while the entry is being recalculated.
568
677
  entry.expires_at = Time.now + race_ttl
569
- write_entry(key, entry, :expires_in => race_ttl * 2)
678
+ write_entry(key, entry, expires_in: race_ttl * 2)
570
679
  else
571
680
  delete_entry(key, options)
572
681
  end
@@ -576,12 +685,12 @@ module ActiveSupport
576
685
  end
577
686
 
578
687
  def get_entry_value(entry, name, options)
579
- instrument(:fetch_hit, name, options) { |payload| }
688
+ instrument(:fetch_hit, name, options) {}
580
689
  entry.value
581
690
  end
582
691
 
583
692
  def save_block_result_to_cache(name, options)
584
- result = instrument(:generate, name, options) do |payload|
693
+ result = instrument(:generate, name, options) do
585
694
  yield(name)
586
695
  end
587
696
 
@@ -590,39 +699,43 @@ module ActiveSupport
590
699
  end
591
700
  end
592
701
 
593
- # This class is used to represent cache entries. Cache entries have a value and an optional
594
- # expiration time. The expiration time is used to support the :race_condition_ttl option
595
- # 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.
596
706
  #
597
707
  # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
598
708
  # using short instance variable names that are lazily defined.
599
709
  class Entry # :nodoc:
600
- DEFAULT_COMPRESS_LIMIT = 16.kilobytes
710
+ attr_reader :version
711
+
712
+ DEFAULT_COMPRESS_LIMIT = 1.kilobyte
601
713
 
602
- # Create a new cache entry for the specified value. Options supported are
714
+ # Creates a new cache entry for the specified value. Options supported are
603
715
  # +:compress+, +:compress_threshold+, and +:expires_in+.
604
716
  def initialize(value, options = {})
605
- if should_compress?(value, options)
606
- @value = compress(value)
607
- @compressed = true
608
- else
609
- @value = value
717
+ @value = value
718
+ if should_compress?(options)
719
+ compress!
610
720
  end
611
721
 
722
+ @version = options[:version]
612
723
  @created_at = Time.now.to_f
613
724
  @expires_in = options[:expires_in]
614
725
  @expires_in = @expires_in.to_f if @expires_in
615
726
  end
616
727
 
617
728
  def value
618
- convert_version_4beta1_entry! if defined?(@v)
619
729
  compressed? ? uncompress(@value) : @value
620
730
  end
621
731
 
622
- # Check if the entry is expired. The +expires_in+ parameter can override
732
+ def mismatched?(version)
733
+ @version && version && @version != version
734
+ end
735
+
736
+ # Checks if the entry is expired. The +expires_in+ parameter can override
623
737
  # the value set when the entry was created.
624
738
  def expired?
625
- convert_version_4beta1_entry! if defined?(@value)
626
739
  @expires_in && @created_at + @expires_in <= Time.now.to_f
627
740
  end
628
741
 
@@ -655,11 +768,9 @@ module ActiveSupport
655
768
  end
656
769
  end
657
770
 
658
- # Duplicate the value in a class. This is used by cache implementations that don't natively
771
+ # Duplicates the value in a class. This is used by cache implementations that don't natively
659
772
  # serialize entries to protect against accidental cache modifications.
660
773
  def dup_value!
661
- convert_version_4beta1_entry! if defined?(@v)
662
-
663
774
  if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
664
775
  if @value.is_a?(String)
665
776
  @value = @value.dup
@@ -670,47 +781,30 @@ module ActiveSupport
670
781
  end
671
782
 
672
783
  private
673
- def should_compress?(value, options)
674
- if value && options[:compress]
675
- compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
676
- serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
784
+ def should_compress?(options)
785
+ if @value && options.fetch(:compress, true)
786
+ compress_threshold = options.fetch(:compress_threshold, DEFAULT_COMPRESS_LIMIT)
787
+ serialized_value_size = (@value.is_a?(String) ? @value : marshaled_value).bytesize
677
788
 
678
- return true if serialized_value_size >= compress_threshold
789
+ serialized_value_size >= compress_threshold
679
790
  end
680
-
681
- false
682
791
  end
683
792
 
684
793
  def compressed?
685
794
  defined?(@compressed) ? @compressed : false
686
795
  end
687
796
 
688
- def compress(value)
689
- Zlib::Deflate.deflate(Marshal.dump(value))
797
+ def compress!
798
+ @value = Zlib::Deflate.deflate(marshaled_value)
799
+ @compressed = true
690
800
  end
691
801
 
692
802
  def uncompress(value)
693
803
  Marshal.load(Zlib::Inflate.inflate(value))
694
804
  end
695
805
 
696
- # The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
697
- # to ensure that cache entries created under the old version still work with the new class definition.
698
- def convert_version_4beta1_entry!
699
- if defined?(@v)
700
- @value = @v
701
- remove_instance_variable(:@v)
702
- end
703
-
704
- if defined?(@c)
705
- @compressed = @c
706
- remove_instance_variable(:@c)
707
- end
708
-
709
- if defined?(@x) && @x
710
- @created_at ||= Time.now.to_f
711
- @expires_in = @x - @created_at
712
- remove_instance_variable(:@x)
713
- end
806
+ def marshaled_value
807
+ @marshaled_value ||= Marshal.dump(@value)
714
808
  end
715
809
  end
716
810
  end