activesupport 2.0.5 → 2.1.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 (236) hide show
  1. data/CHANGELOG +183 -5
  2. data/lib/active_support.rb +6 -2
  3. data/lib/active_support/base64.rb +5 -0
  4. data/lib/active_support/basic_object.rb +23 -4
  5. data/lib/active_support/buffered_logger.rb +17 -3
  6. data/lib/active_support/cache.rb +145 -0
  7. data/lib/active_support/cache/compressed_mem_cache_store.rb +15 -0
  8. data/lib/active_support/cache/drb_store.rb +15 -0
  9. data/lib/active_support/cache/file_store.rb +70 -0
  10. data/lib/active_support/cache/mem_cache_store.rb +100 -0
  11. data/lib/active_support/cache/memory_store.rb +38 -0
  12. data/lib/active_support/callbacks.rb +275 -0
  13. data/lib/active_support/core_ext/array/access.rb +2 -4
  14. data/lib/active_support/core_ext/array/conversions.rb +89 -5
  15. data/lib/active_support/core_ext/array/extract_options.rb +2 -1
  16. data/lib/active_support/core_ext/array/grouping.rb +3 -8
  17. data/lib/active_support/core_ext/array/random_access.rb +1 -1
  18. data/lib/active_support/core_ext/base64.rb +4 -0
  19. data/lib/active_support/core_ext/base64/encoding.rb +13 -0
  20. data/lib/active_support/core_ext/benchmark.rb +12 -0
  21. data/lib/active_support/core_ext/bigdecimal.rb +4 -0
  22. data/lib/active_support/core_ext/bigdecimal/conversions.rb +39 -4
  23. data/lib/active_support/core_ext/blank.rb +5 -2
  24. data/lib/active_support/core_ext/class/attribute_accessors.rb +7 -1
  25. data/lib/active_support/core_ext/class/delegating_attributes.rb +7 -1
  26. data/lib/active_support/core_ext/class/inheritable_attributes.rb +1 -1
  27. data/lib/active_support/core_ext/class/removal.rb +26 -0
  28. data/lib/active_support/core_ext/date/calculations.rb +28 -1
  29. data/lib/active_support/core_ext/date/conversions.rb +1 -0
  30. data/lib/active_support/core_ext/date_time.rb +2 -0
  31. data/lib/active_support/core_ext/date_time/calculations.rb +37 -2
  32. data/lib/active_support/core_ext/date_time/conversions.rb +27 -14
  33. data/lib/active_support/core_ext/enumerable.rb +16 -9
  34. data/lib/active_support/core_ext/exception.rb +8 -0
  35. data/lib/active_support/core_ext/file.rb +6 -6
  36. data/lib/active_support/core_ext/hash/conversions.rb +26 -8
  37. data/lib/active_support/core_ext/hash/indifferent_access.rb +35 -0
  38. data/lib/active_support/core_ext/hash/reverse_merge.rb +4 -1
  39. data/lib/active_support/core_ext/integer/even_odd.rb +10 -5
  40. data/lib/active_support/core_ext/integer/inflections.rb +0 -1
  41. data/lib/active_support/core_ext/kernel/daemonizing.rb +2 -10
  42. data/lib/active_support/core_ext/kernel/reporting.rb +8 -0
  43. data/lib/active_support/core_ext/module/attr_internal.rb +4 -3
  44. data/lib/active_support/core_ext/module/attribute_accessors.rb +11 -1
  45. data/lib/active_support/core_ext/module/delegation.rb +5 -3
  46. data/lib/active_support/core_ext/module/inclusion.rb +19 -0
  47. data/lib/active_support/core_ext/module/introspection.rb +50 -16
  48. data/lib/active_support/core_ext/module/loading.rb +10 -0
  49. data/lib/active_support/core_ext/numeric.rb +3 -1
  50. data/lib/active_support/core_ext/numeric/conversions.rb +19 -0
  51. data/lib/active_support/core_ext/object/instance_variables.rb +52 -0
  52. data/lib/active_support/core_ext/object/misc.rb +1 -1
  53. data/lib/active_support/core_ext/process.rb +1 -0
  54. data/lib/active_support/core_ext/process/daemon.rb +25 -0
  55. data/lib/active_support/core_ext/range/conversions.rb +5 -1
  56. data/lib/active_support/core_ext/range/include_range.rb +8 -0
  57. data/lib/active_support/core_ext/range/overlaps.rb +3 -0
  58. data/lib/active_support/core_ext/string.rb +5 -10
  59. data/lib/active_support/core_ext/string/access.rb +72 -48
  60. data/lib/active_support/core_ext/string/conversions.rb +4 -4
  61. data/lib/active_support/core_ext/string/filters.rb +26 -0
  62. data/lib/active_support/core_ext/string/inflections.rb +56 -64
  63. data/lib/active_support/core_ext/string/iterators.rb +4 -0
  64. data/lib/active_support/core_ext/string/starts_ends_with.rb +12 -4
  65. data/lib/active_support/core_ext/string/unicode.rb +14 -7
  66. data/lib/active_support/core_ext/symbol.rb +1 -1
  67. data/lib/active_support/core_ext/time.rb +2 -0
  68. data/lib/active_support/core_ext/time/calculations.rb +75 -23
  69. data/lib/active_support/core_ext/time/conversions.rb +22 -35
  70. data/lib/active_support/core_ext/time/zones.rb +86 -0
  71. data/lib/active_support/dependencies.rb +92 -80
  72. data/lib/active_support/deprecation.rb +2 -16
  73. data/lib/active_support/duration.rb +4 -4
  74. data/lib/active_support/gzip.rb +25 -0
  75. data/lib/active_support/inflector.rb +92 -69
  76. data/lib/active_support/json.rb +17 -25
  77. data/lib/active_support/json/decoding.rb +1 -1
  78. data/lib/active_support/json/encoders/date.rb +11 -2
  79. data/lib/active_support/json/encoders/date_time.rb +11 -2
  80. data/lib/active_support/json/encoders/enumerable.rb +2 -2
  81. data/lib/active_support/json/encoders/hash.rb +3 -6
  82. data/lib/active_support/json/encoders/object.rb +1 -1
  83. data/lib/active_support/json/encoders/string.rb +8 -2
  84. data/lib/active_support/json/encoders/time.rb +11 -2
  85. data/lib/active_support/json/encoding.rb +0 -1
  86. data/lib/active_support/multibyte/chars.rb +8 -6
  87. data/lib/active_support/multibyte/handlers/utf8_handler.rb +11 -11
  88. data/lib/active_support/ordered_hash.rb +43 -0
  89. data/lib/active_support/ordered_options.rb +0 -38
  90. data/lib/active_support/test_case.rb +10 -2
  91. data/lib/active_support/testing/default.rb +3 -6
  92. data/lib/active_support/testing/setup_and_teardown.rb +93 -0
  93. data/lib/active_support/time_with_zone.rb +283 -0
  94. data/lib/active_support/values/time_zone.rb +336 -123
  95. data/lib/active_support/vendor.rb +12 -0
  96. data/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb +849 -0
  97. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo.rb +33 -0
  98. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/data_timezone.rb +47 -0
  99. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/data_timezone_info.rb +226 -0
  100. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Algiers.rb +55 -0
  101. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Cairo.rb +219 -0
  102. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Casablanca.rb +38 -0
  103. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Harare.rb +18 -0
  104. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Johannesburg.rb +25 -0
  105. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Monrovia.rb +22 -0
  106. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Africa/Nairobi.rb +23 -0
  107. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Argentina/Buenos_Aires.rb +166 -0
  108. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Argentina/San_Juan.rb +170 -0
  109. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Bogota.rb +23 -0
  110. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Caracas.rb +23 -0
  111. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Chicago.rb +283 -0
  112. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Chihuahua.rb +136 -0
  113. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Denver.rb +204 -0
  114. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Godthab.rb +161 -0
  115. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Guatemala.rb +27 -0
  116. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Halifax.rb +274 -0
  117. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Indiana/Indianapolis.rb +149 -0
  118. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Juneau.rb +194 -0
  119. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/La_Paz.rb +22 -0
  120. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Lima.rb +35 -0
  121. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Los_Angeles.rb +232 -0
  122. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Mazatlan.rb +139 -0
  123. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Mexico_City.rb +144 -0
  124. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Monterrey.rb +131 -0
  125. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/New_York.rb +282 -0
  126. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Phoenix.rb +30 -0
  127. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Regina.rb +74 -0
  128. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Santiago.rb +205 -0
  129. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/St_Johns.rb +288 -0
  130. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/America/Tijuana.rb +196 -0
  131. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Almaty.rb +67 -0
  132. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Baghdad.rb +73 -0
  133. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Baku.rb +161 -0
  134. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Bangkok.rb +20 -0
  135. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Chongqing.rb +33 -0
  136. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Dhaka.rb +27 -0
  137. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Hong_Kong.rb +87 -0
  138. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Irkutsk.rb +165 -0
  139. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Jakarta.rb +30 -0
  140. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Jerusalem.rb +163 -0
  141. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kabul.rb +20 -0
  142. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kamchatka.rb +163 -0
  143. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Karachi.rb +28 -0
  144. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Katmandu.rb +20 -0
  145. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kolkata.rb +25 -0
  146. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Krasnoyarsk.rb +163 -0
  147. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kuala_Lumpur.rb +31 -0
  148. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Kuwait.rb +18 -0
  149. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Magadan.rb +163 -0
  150. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Muscat.rb +18 -0
  151. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Novosibirsk.rb +164 -0
  152. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Rangoon.rb +24 -0
  153. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Riyadh.rb +18 -0
  154. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Seoul.rb +34 -0
  155. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Shanghai.rb +35 -0
  156. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Singapore.rb +33 -0
  157. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Taipei.rb +59 -0
  158. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Tashkent.rb +47 -0
  159. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Tbilisi.rb +78 -0
  160. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Tehran.rb +121 -0
  161. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Tokyo.rb +30 -0
  162. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Ulaanbaatar.rb +65 -0
  163. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Urumqi.rb +33 -0
  164. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Vladivostok.rb +164 -0
  165. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Yakutsk.rb +163 -0
  166. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Yekaterinburg.rb +165 -0
  167. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Asia/Yerevan.rb +165 -0
  168. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Atlantic/Azores.rb +270 -0
  169. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Atlantic/Cape_Verde.rb +23 -0
  170. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Atlantic/South_Georgia.rb +18 -0
  171. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Adelaide.rb +187 -0
  172. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Brisbane.rb +35 -0
  173. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Darwin.rb +29 -0
  174. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Hobart.rb +193 -0
  175. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Melbourne.rb +185 -0
  176. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Perth.rb +37 -0
  177. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Australia/Sydney.rb +185 -0
  178. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Etc/UTC.rb +16 -0
  179. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Amsterdam.rb +228 -0
  180. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Athens.rb +185 -0
  181. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Belgrade.rb +163 -0
  182. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Berlin.rb +188 -0
  183. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Bratislava.rb +13 -0
  184. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Brussels.rb +232 -0
  185. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Bucharest.rb +181 -0
  186. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Budapest.rb +197 -0
  187. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Copenhagen.rb +179 -0
  188. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Dublin.rb +276 -0
  189. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Helsinki.rb +163 -0
  190. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Istanbul.rb +218 -0
  191. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Kiev.rb +168 -0
  192. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Lisbon.rb +268 -0
  193. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Ljubljana.rb +13 -0
  194. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/London.rb +288 -0
  195. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Madrid.rb +211 -0
  196. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Minsk.rb +170 -0
  197. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Moscow.rb +181 -0
  198. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Paris.rb +232 -0
  199. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Prague.rb +187 -0
  200. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Riga.rb +176 -0
  201. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Rome.rb +215 -0
  202. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Sarajevo.rb +13 -0
  203. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Skopje.rb +13 -0
  204. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Sofia.rb +173 -0
  205. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Stockholm.rb +165 -0
  206. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Tallinn.rb +172 -0
  207. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Vienna.rb +183 -0
  208. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Vilnius.rb +170 -0
  209. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Warsaw.rb +212 -0
  210. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Europe/Zagreb.rb +13 -0
  211. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Auckland.rb +202 -0
  212. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Fiji.rb +23 -0
  213. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Guam.rb +22 -0
  214. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Honolulu.rb +28 -0
  215. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Majuro.rb +20 -0
  216. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Midway.rb +25 -0
  217. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Noumea.rb +25 -0
  218. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Pago_Pago.rb +26 -0
  219. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Port_Moresby.rb +20 -0
  220. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/definitions/Pacific/Tongatapu.rb +27 -0
  221. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/info_timezone.rb +52 -0
  222. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/linked_timezone.rb +51 -0
  223. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/linked_timezone_info.rb +44 -0
  224. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/offset_rationals.rb +95 -0
  225. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/time_or_datetime.rb +292 -0
  226. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone.rb +508 -0
  227. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_definition.rb +56 -0
  228. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_info.rb +40 -0
  229. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_offset_info.rb +94 -0
  230. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_period.rb +198 -0
  231. data/lib/active_support/vendor/tzinfo-0.3.8/tzinfo/timezone_transition_info.rb +138 -0
  232. data/lib/active_support/version.rb +2 -2
  233. data/lib/active_support/whiny_nil.rb +30 -10
  234. metadata +175 -5
  235. data/lib/active_support/core_ext/rexml.rb +0 -38
  236. data/lib/active_support/testing.rb +0 -1
@@ -12,3 +12,15 @@ begin
12
12
  rescue Gem::LoadError
13
13
  $:.unshift "#{File.dirname(__FILE__)}/vendor/xml-simple-1.0.11"
14
14
  end
15
+
16
+ begin
17
+ gem 'memcache-client', '~> 1.5.0'
18
+ rescue Gem::LoadError
19
+ $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.5.0"
20
+ end
21
+
22
+ begin
23
+ gem 'tzinfo', '~> 0.3.8'
24
+ rescue Gem::LoadError
25
+ $:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.8"
26
+ end
@@ -0,0 +1,849 @@
1
+ # All original code copyright 2005, 2006, 2007 Bob Cottrell, Eric Hodel,
2
+ # The Robot Co-op. All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions
6
+ # are met:
7
+ #
8
+ # 1. Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # 2. Redistributions in binary form must reproduce the above copyright
11
+ # notice, this list of conditions and the following disclaimer in the
12
+ # documentation and/or other materials provided with the distribution.
13
+ # 3. Neither the names of the authors nor the names of their contributors
14
+ # may be used to endorse or promote products derived from this software
15
+ # without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
18
+ # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22
+ # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
23
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24
+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25
+ # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26
+ # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27
+ # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
29
+
30
+ require 'socket'
31
+ require 'thread'
32
+ require 'timeout'
33
+ require 'rubygems'
34
+
35
+ class String
36
+
37
+ ##
38
+ # Uses the ITU-T polynomial in the CRC32 algorithm.
39
+
40
+ def crc32_ITU_T
41
+ n = length
42
+ r = 0xFFFFFFFF
43
+
44
+ n.times do |i|
45
+ r ^= self[i]
46
+ 8.times do
47
+ if (r & 1) != 0 then
48
+ r = (r>>1) ^ 0xEDB88320
49
+ else
50
+ r >>= 1
51
+ end
52
+ end
53
+ end
54
+
55
+ r ^ 0xFFFFFFFF
56
+ end
57
+
58
+ end
59
+
60
+ ##
61
+ # A Ruby client library for memcached.
62
+ #
63
+ # This is intended to provide access to basic memcached functionality. It
64
+ # does not attempt to be complete implementation of the entire API, but it is
65
+ # approaching a complete implementation.
66
+
67
+ class MemCache
68
+
69
+ ##
70
+ # The version of MemCache you are using.
71
+
72
+ VERSION = '1.5.0'
73
+
74
+ ##
75
+ # Default options for the cache object.
76
+
77
+ DEFAULT_OPTIONS = {
78
+ :namespace => nil,
79
+ :readonly => false,
80
+ :multithread => false,
81
+ }
82
+
83
+ ##
84
+ # Default memcached port.
85
+
86
+ DEFAULT_PORT = 11211
87
+
88
+ ##
89
+ # Default memcached server weight.
90
+
91
+ DEFAULT_WEIGHT = 1
92
+
93
+ ##
94
+ # The amount of time to wait for a response from a memcached server. If a
95
+ # response is not completed within this time, the connection to the server
96
+ # will be closed and an error will be raised.
97
+
98
+ attr_accessor :request_timeout
99
+
100
+ ##
101
+ # The namespace for this instance
102
+
103
+ attr_reader :namespace
104
+
105
+ ##
106
+ # The multithread setting for this instance
107
+
108
+ attr_reader :multithread
109
+
110
+ ##
111
+ # The servers this client talks to. Play at your own peril.
112
+
113
+ attr_reader :servers
114
+
115
+ ##
116
+ # Accepts a list of +servers+ and a list of +opts+. +servers+ may be
117
+ # omitted. See +servers=+ for acceptable server list arguments.
118
+ #
119
+ # Valid options for +opts+ are:
120
+ #
121
+ # [:namespace] Prepends this value to all keys added or retrieved.
122
+ # [:readonly] Raises an exeception on cache writes when true.
123
+ # [:multithread] Wraps cache access in a Mutex for thread safety.
124
+ #
125
+ # Other options are ignored.
126
+
127
+ def initialize(*args)
128
+ servers = []
129
+ opts = {}
130
+
131
+ case args.length
132
+ when 0 then # NOP
133
+ when 1 then
134
+ arg = args.shift
135
+ case arg
136
+ when Hash then opts = arg
137
+ when Array then servers = arg
138
+ when String then servers = [arg]
139
+ else raise ArgumentError, 'first argument must be Array, Hash or String'
140
+ end
141
+ when 2 then
142
+ servers, opts = args
143
+ else
144
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 2)"
145
+ end
146
+
147
+ opts = DEFAULT_OPTIONS.merge opts
148
+ @namespace = opts[:namespace]
149
+ @readonly = opts[:readonly]
150
+ @multithread = opts[:multithread]
151
+ @mutex = Mutex.new if @multithread
152
+ @buckets = []
153
+ self.servers = servers
154
+ end
155
+
156
+ ##
157
+ # Returns a string representation of the cache object.
158
+
159
+ def inspect
160
+ "<MemCache: %d servers, %d buckets, ns: %p, ro: %p>" %
161
+ [@servers.length, @buckets.length, @namespace, @readonly]
162
+ end
163
+
164
+ ##
165
+ # Returns whether there is at least one active server for the object.
166
+
167
+ def active?
168
+ not @servers.empty?
169
+ end
170
+
171
+ ##
172
+ # Returns whether or not the cache object was created read only.
173
+
174
+ def readonly?
175
+ @readonly
176
+ end
177
+
178
+ ##
179
+ # Set the servers that the requests will be distributed between. Entries
180
+ # can be either strings of the form "hostname:port" or
181
+ # "hostname:port:weight" or MemCache::Server objects.
182
+
183
+ def servers=(servers)
184
+ # Create the server objects.
185
+ @servers = servers.collect do |server|
186
+ case server
187
+ when String
188
+ host, port, weight = server.split ':', 3
189
+ port ||= DEFAULT_PORT
190
+ weight ||= DEFAULT_WEIGHT
191
+ Server.new self, host, port, weight
192
+ when Server
193
+ if server.memcache.multithread != @multithread then
194
+ raise ArgumentError, "can't mix threaded and non-threaded servers"
195
+ end
196
+ server
197
+ else
198
+ raise TypeError, "cannot convert #{server.class} into MemCache::Server"
199
+ end
200
+ end
201
+
202
+ # Create an array of server buckets for weight selection of servers.
203
+ @buckets = []
204
+ @servers.each do |server|
205
+ server.weight.times { @buckets.push(server) }
206
+ end
207
+ end
208
+
209
+ ##
210
+ # Deceremets the value for +key+ by +amount+ and returns the new value.
211
+ # +key+ must already exist. If +key+ is not an integer, it is assumed to be
212
+ # 0. +key+ can not be decremented below 0.
213
+
214
+ def decr(key, amount = 1)
215
+ server, cache_key = request_setup key
216
+
217
+ if @multithread then
218
+ threadsafe_cache_decr server, cache_key, amount
219
+ else
220
+ cache_decr server, cache_key, amount
221
+ end
222
+ rescue TypeError, SocketError, SystemCallError, IOError => err
223
+ handle_error server, err
224
+ end
225
+
226
+ ##
227
+ # Retrieves +key+ from memcache. If +raw+ is false, the value will be
228
+ # unmarshalled.
229
+
230
+ def get(key, raw = false)
231
+ server, cache_key = request_setup key
232
+
233
+ value = if @multithread then
234
+ threadsafe_cache_get server, cache_key
235
+ else
236
+ cache_get server, cache_key
237
+ end
238
+
239
+ return nil if value.nil?
240
+
241
+ value = Marshal.load value unless raw
242
+
243
+ return value
244
+ rescue TypeError, SocketError, SystemCallError, IOError => err
245
+ handle_error server, err
246
+ end
247
+
248
+ ##
249
+ # Retrieves multiple values from memcached in parallel, if possible.
250
+ #
251
+ # The memcached protocol supports the ability to retrieve multiple
252
+ # keys in a single request. Pass in an array of keys to this method
253
+ # and it will:
254
+ #
255
+ # 1. map the key to the appropriate memcached server
256
+ # 2. send a single request to each server that has one or more key values
257
+ #
258
+ # Returns a hash of values.
259
+ #
260
+ # cache["a"] = 1
261
+ # cache["b"] = 2
262
+ # cache.get_multi "a", "b" # => { "a" => 1, "b" => 2 }
263
+
264
+ def get_multi(*keys)
265
+ raise MemCacheError, 'No active servers' unless active?
266
+
267
+ keys.flatten!
268
+ key_count = keys.length
269
+ cache_keys = {}
270
+ server_keys = Hash.new { |h,k| h[k] = [] }
271
+
272
+ # map keys to servers
273
+ keys.each do |key|
274
+ server, cache_key = request_setup key
275
+ cache_keys[cache_key] = key
276
+ server_keys[server] << cache_key
277
+ end
278
+
279
+ results = {}
280
+
281
+ server_keys.each do |server, keys_for_server|
282
+ keys_for_server = keys_for_server.join ' '
283
+ values = if @multithread then
284
+ threadsafe_cache_get_multi server, keys_for_server
285
+ else
286
+ cache_get_multi server, keys_for_server
287
+ end
288
+ values.each do |key, value|
289
+ results[cache_keys[key]] = Marshal.load value
290
+ end
291
+ end
292
+
293
+ return results
294
+ rescue TypeError, SocketError, SystemCallError, IOError => err
295
+ handle_error server, err
296
+ end
297
+
298
+ ##
299
+ # Increments the value for +key+ by +amount+ and retruns the new value.
300
+ # +key+ must already exist. If +key+ is not an integer, it is assumed to be
301
+ # 0.
302
+
303
+ def incr(key, amount = 1)
304
+ server, cache_key = request_setup key
305
+
306
+ if @multithread then
307
+ threadsafe_cache_incr server, cache_key, amount
308
+ else
309
+ cache_incr server, cache_key, amount
310
+ end
311
+ rescue TypeError, SocketError, SystemCallError, IOError => err
312
+ handle_error server, err
313
+ end
314
+
315
+ ##
316
+ # Add +key+ to the cache with value +value+ that expires in +expiry+
317
+ # seconds. If +raw+ is true, +value+ will not be Marshalled.
318
+ #
319
+ # Warning: Readers should not call this method in the event of a cache miss;
320
+ # see MemCache#add.
321
+
322
+ def set(key, value, expiry = 0, raw = false)
323
+ raise MemCacheError, "Update of readonly cache" if @readonly
324
+ server, cache_key = request_setup key
325
+ socket = server.socket
326
+
327
+ value = Marshal.dump value unless raw
328
+ command = "set #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
329
+
330
+ begin
331
+ @mutex.lock if @multithread
332
+ socket.write command
333
+ result = socket.gets
334
+ raise_on_error_response! result
335
+ result
336
+ rescue SocketError, SystemCallError, IOError => err
337
+ server.close
338
+ raise MemCacheError, err.message
339
+ ensure
340
+ @mutex.unlock if @multithread
341
+ end
342
+ end
343
+
344
+ ##
345
+ # Add +key+ to the cache with value +value+ that expires in +expiry+
346
+ # seconds, but only if +key+ does not already exist in the cache.
347
+ # If +raw+ is true, +value+ will not be Marshalled.
348
+ #
349
+ # Readers should call this method in the event of a cache miss, not
350
+ # MemCache#set or MemCache#[]=.
351
+
352
+ def add(key, value, expiry = 0, raw = false)
353
+ raise MemCacheError, "Update of readonly cache" if @readonly
354
+ server, cache_key = request_setup key
355
+ socket = server.socket
356
+
357
+ value = Marshal.dump value unless raw
358
+ command = "add #{cache_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
359
+
360
+ begin
361
+ @mutex.lock if @multithread
362
+ socket.write command
363
+ result = socket.gets
364
+ raise_on_error_response! result
365
+ result
366
+ rescue SocketError, SystemCallError, IOError => err
367
+ server.close
368
+ raise MemCacheError, err.message
369
+ ensure
370
+ @mutex.unlock if @multithread
371
+ end
372
+ end
373
+
374
+ ##
375
+ # Removes +key+ from the cache in +expiry+ seconds.
376
+
377
+ def delete(key, expiry = 0)
378
+ @mutex.lock if @multithread
379
+
380
+ raise MemCacheError, "No active servers" unless active?
381
+ cache_key = make_cache_key key
382
+ server = get_server_for_key cache_key
383
+
384
+ sock = server.socket
385
+ raise MemCacheError, "No connection to server" if sock.nil?
386
+
387
+ begin
388
+ sock.write "delete #{cache_key} #{expiry}\r\n"
389
+ result = sock.gets
390
+ raise_on_error_response! result
391
+ result
392
+ rescue SocketError, SystemCallError, IOError => err
393
+ server.close
394
+ raise MemCacheError, err.message
395
+ end
396
+ ensure
397
+ @mutex.unlock if @multithread
398
+ end
399
+
400
+ ##
401
+ # Flush the cache from all memcache servers.
402
+
403
+ def flush_all
404
+ raise MemCacheError, 'No active servers' unless active?
405
+ raise MemCacheError, "Update of readonly cache" if @readonly
406
+ begin
407
+ @mutex.lock if @multithread
408
+ @servers.each do |server|
409
+ begin
410
+ sock = server.socket
411
+ raise MemCacheError, "No connection to server" if sock.nil?
412
+ sock.write "flush_all\r\n"
413
+ result = sock.gets
414
+ raise_on_error_response! result
415
+ result
416
+ rescue SocketError, SystemCallError, IOError => err
417
+ server.close
418
+ raise MemCacheError, err.message
419
+ end
420
+ end
421
+ ensure
422
+ @mutex.unlock if @multithread
423
+ end
424
+ end
425
+
426
+ ##
427
+ # Reset the connection to all memcache servers. This should be called if
428
+ # there is a problem with a cache lookup that might have left the connection
429
+ # in a corrupted state.
430
+
431
+ def reset
432
+ @servers.each { |server| server.close }
433
+ end
434
+
435
+ ##
436
+ # Returns statistics for each memcached server. An explanation of the
437
+ # statistics can be found in the memcached docs:
438
+ #
439
+ # http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
440
+ #
441
+ # Example:
442
+ #
443
+ # >> pp CACHE.stats
444
+ # {"localhost:11211"=>
445
+ # {"bytes"=>4718,
446
+ # "pid"=>20188,
447
+ # "connection_structures"=>4,
448
+ # "time"=>1162278121,
449
+ # "pointer_size"=>32,
450
+ # "limit_maxbytes"=>67108864,
451
+ # "cmd_get"=>14532,
452
+ # "version"=>"1.2.0",
453
+ # "bytes_written"=>432583,
454
+ # "cmd_set"=>32,
455
+ # "get_misses"=>0,
456
+ # "total_connections"=>19,
457
+ # "curr_connections"=>3,
458
+ # "curr_items"=>4,
459
+ # "uptime"=>1557,
460
+ # "get_hits"=>14532,
461
+ # "total_items"=>32,
462
+ # "rusage_system"=>0.313952,
463
+ # "rusage_user"=>0.119981,
464
+ # "bytes_read"=>190619}}
465
+ # => nil
466
+
467
+ def stats
468
+ raise MemCacheError, "No active servers" unless active?
469
+ server_stats = {}
470
+
471
+ @servers.each do |server|
472
+ sock = server.socket
473
+ raise MemCacheError, "No connection to server" if sock.nil?
474
+
475
+ value = nil
476
+ begin
477
+ sock.write "stats\r\n"
478
+ stats = {}
479
+ while line = sock.gets do
480
+ raise_on_error_response! line
481
+ break if line == "END\r\n"
482
+ if line =~ /\ASTAT ([\w]+) ([\w\.\:]+)/ then
483
+ name, value = $1, $2
484
+ stats[name] = case name
485
+ when 'version'
486
+ value
487
+ when 'rusage_user', 'rusage_system' then
488
+ seconds, microseconds = value.split(/:/, 2)
489
+ microseconds ||= 0
490
+ Float(seconds) + (Float(microseconds) / 1_000_000)
491
+ else
492
+ if value =~ /\A\d+\Z/ then
493
+ value.to_i
494
+ else
495
+ value
496
+ end
497
+ end
498
+ end
499
+ end
500
+ server_stats["#{server.host}:#{server.port}"] = stats
501
+ rescue SocketError, SystemCallError, IOError => err
502
+ server.close
503
+ raise MemCacheError, err.message
504
+ end
505
+ end
506
+
507
+ server_stats
508
+ end
509
+
510
+ ##
511
+ # Shortcut to get a value from the cache.
512
+
513
+ alias [] get
514
+
515
+ ##
516
+ # Shortcut to save a value in the cache. This method does not set an
517
+ # expiration on the entry. Use set to specify an explicit expiry.
518
+
519
+ def []=(key, value)
520
+ set key, value
521
+ end
522
+
523
+ protected
524
+
525
+ ##
526
+ # Create a key for the cache, incorporating the namespace qualifier if
527
+ # requested.
528
+
529
+ def make_cache_key(key)
530
+ if namespace.nil? then
531
+ key
532
+ else
533
+ "#{@namespace}:#{key}"
534
+ end
535
+ end
536
+
537
+ ##
538
+ # Pick a server to handle the request based on a hash of the key.
539
+
540
+ def get_server_for_key(key)
541
+ raise ArgumentError, "illegal character in key #{key.inspect}" if
542
+ key =~ /\s/
543
+ raise ArgumentError, "key too long #{key.inspect}" if key.length > 250
544
+ raise MemCacheError, "No servers available" if @servers.empty?
545
+ return @servers.first if @servers.length == 1
546
+
547
+ hkey = hash_for key
548
+
549
+ 20.times do |try|
550
+ server = @buckets[hkey % @buckets.nitems]
551
+ return server if server.alive?
552
+ hkey += hash_for "#{try}#{key}"
553
+ end
554
+
555
+ raise MemCacheError, "No servers available"
556
+ end
557
+
558
+ ##
559
+ # Returns an interoperable hash value for +key+. (I think, docs are
560
+ # sketchy for down servers).
561
+
562
+ def hash_for(key)
563
+ (key.crc32_ITU_T >> 16) & 0x7fff
564
+ end
565
+
566
+ ##
567
+ # Performs a raw decr for +cache_key+ from +server+. Returns nil if not
568
+ # found.
569
+
570
+ def cache_decr(server, cache_key, amount)
571
+ socket = server.socket
572
+ socket.write "decr #{cache_key} #{amount}\r\n"
573
+ text = socket.gets
574
+ raise_on_error_response! text
575
+ return nil if text == "NOT_FOUND\r\n"
576
+ return text.to_i
577
+ end
578
+
579
+ ##
580
+ # Fetches the raw data for +cache_key+ from +server+. Returns nil on cache
581
+ # miss.
582
+
583
+ def cache_get(server, cache_key)
584
+ socket = server.socket
585
+ socket.write "get #{cache_key}\r\n"
586
+ keyline = socket.gets # "VALUE <key> <flags> <bytes>\r\n"
587
+
588
+ if keyline.nil? then
589
+ server.close
590
+ raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
591
+ end
592
+
593
+ raise_on_error_response! keyline
594
+ return nil if keyline == "END\r\n"
595
+
596
+ unless keyline =~ /(\d+)\r/ then
597
+ server.close
598
+ raise MemCacheError, "unexpected response #{keyline.inspect}"
599
+ end
600
+ value = socket.read $1.to_i
601
+ socket.read 2 # "\r\n"
602
+ socket.gets # "END\r\n"
603
+ return value
604
+ end
605
+
606
+ ##
607
+ # Fetches +cache_keys+ from +server+ using a multi-get.
608
+
609
+ def cache_get_multi(server, cache_keys)
610
+ values = {}
611
+ socket = server.socket
612
+ socket.write "get #{cache_keys}\r\n"
613
+
614
+ while keyline = socket.gets do
615
+ return values if keyline == "END\r\n"
616
+ raise_on_error_response! keyline
617
+
618
+ unless keyline =~ /\AVALUE (.+) (.+) (.+)/ then
619
+ server.close
620
+ raise MemCacheError, "unexpected response #{keyline.inspect}"
621
+ end
622
+
623
+ key, data_length = $1, $3
624
+ values[$1] = socket.read data_length.to_i
625
+ socket.read(2) # "\r\n"
626
+ end
627
+
628
+ server.close
629
+ raise MemCacheError, "lost connection to #{server.host}:#{server.port}"
630
+ end
631
+
632
+ ##
633
+ # Performs a raw incr for +cache_key+ from +server+. Returns nil if not
634
+ # found.
635
+
636
+ def cache_incr(server, cache_key, amount)
637
+ socket = server.socket
638
+ socket.write "incr #{cache_key} #{amount}\r\n"
639
+ text = socket.gets
640
+ raise_on_error_response! text
641
+ return nil if text == "NOT_FOUND\r\n"
642
+ return text.to_i
643
+ end
644
+
645
+ ##
646
+ # Handles +error+ from +server+.
647
+
648
+ def handle_error(server, error)
649
+ server.close if server
650
+ new_error = MemCacheError.new error.message
651
+ new_error.set_backtrace error.backtrace
652
+ raise new_error
653
+ end
654
+
655
+ ##
656
+ # Performs setup for making a request with +key+ from memcached. Returns
657
+ # the server to fetch the key from and the complete key to use.
658
+
659
+ def request_setup(key)
660
+ raise MemCacheError, 'No active servers' unless active?
661
+ cache_key = make_cache_key key
662
+ server = get_server_for_key cache_key
663
+ raise MemCacheError, 'No connection to server' if server.socket.nil?
664
+ return server, cache_key
665
+ end
666
+
667
+ def threadsafe_cache_decr(server, cache_key, amount) # :nodoc:
668
+ @mutex.lock
669
+ cache_decr server, cache_key, amount
670
+ ensure
671
+ @mutex.unlock
672
+ end
673
+
674
+ def threadsafe_cache_get(server, cache_key) # :nodoc:
675
+ @mutex.lock
676
+ cache_get server, cache_key
677
+ ensure
678
+ @mutex.unlock
679
+ end
680
+
681
+ def threadsafe_cache_get_multi(socket, cache_keys) # :nodoc:
682
+ @mutex.lock
683
+ cache_get_multi socket, cache_keys
684
+ ensure
685
+ @mutex.unlock
686
+ end
687
+
688
+ def threadsafe_cache_incr(server, cache_key, amount) # :nodoc:
689
+ @mutex.lock
690
+ cache_incr server, cache_key, amount
691
+ ensure
692
+ @mutex.unlock
693
+ end
694
+
695
+ def raise_on_error_response!(response)
696
+ if response =~ /\A(?:CLIENT_|SERVER_)?ERROR (.*)/
697
+ raise MemCacheError, $1.strip
698
+ end
699
+ end
700
+
701
+
702
+ ##
703
+ # This class represents a memcached server instance.
704
+
705
+ class Server
706
+
707
+ ##
708
+ # The amount of time to wait to establish a connection with a memcached
709
+ # server. If a connection cannot be established within this time limit,
710
+ # the server will be marked as down.
711
+
712
+ CONNECT_TIMEOUT = 0.25
713
+
714
+ ##
715
+ # The amount of time to wait before attempting to re-establish a
716
+ # connection with a server that is marked dead.
717
+
718
+ RETRY_DELAY = 30.0
719
+
720
+ ##
721
+ # The host the memcached server is running on.
722
+
723
+ attr_reader :host
724
+
725
+ ##
726
+ # The port the memcached server is listening on.
727
+
728
+ attr_reader :port
729
+
730
+ ##
731
+ # The weight given to the server.
732
+
733
+ attr_reader :weight
734
+
735
+ ##
736
+ # The time of next retry if the connection is dead.
737
+
738
+ attr_reader :retry
739
+
740
+ ##
741
+ # A text status string describing the state of the server.
742
+
743
+ attr_reader :status
744
+
745
+ ##
746
+ # Create a new MemCache::Server object for the memcached instance
747
+ # listening on the given host and port, weighted by the given weight.
748
+
749
+ def initialize(memcache, host, port = DEFAULT_PORT, weight = DEFAULT_WEIGHT)
750
+ raise ArgumentError, "No host specified" if host.nil? or host.empty?
751
+ raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero?
752
+
753
+ @memcache = memcache
754
+ @host = host
755
+ @port = port.to_i
756
+ @weight = weight.to_i
757
+
758
+ @multithread = @memcache.multithread
759
+ @mutex = Mutex.new
760
+
761
+ @sock = nil
762
+ @retry = nil
763
+ @status = 'NOT CONNECTED'
764
+ end
765
+
766
+ ##
767
+ # Return a string representation of the server object.
768
+
769
+ def inspect
770
+ "<MemCache::Server: %s:%d [%d] (%s)>" % [@host, @port, @weight, @status]
771
+ end
772
+
773
+ ##
774
+ # Check whether the server connection is alive. This will cause the
775
+ # socket to attempt to connect if it isn't already connected and or if
776
+ # the server was previously marked as down and the retry time has
777
+ # been exceeded.
778
+
779
+ def alive?
780
+ !!socket
781
+ end
782
+
783
+ ##
784
+ # Try to connect to the memcached server targeted by this object.
785
+ # Returns the connected socket object on success or nil on failure.
786
+
787
+ def socket
788
+ @mutex.lock if @multithread
789
+ return @sock if @sock and not @sock.closed?
790
+
791
+ @sock = nil
792
+
793
+ # If the host was dead, don't retry for a while.
794
+ return if @retry and @retry > Time.now
795
+
796
+ # Attempt to connect if not already connected.
797
+ begin
798
+ @sock = timeout CONNECT_TIMEOUT do
799
+ TCPSocket.new @host, @port
800
+ end
801
+ if Socket.constants.include? 'TCP_NODELAY' then
802
+ @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
803
+ end
804
+ @retry = nil
805
+ @status = 'CONNECTED'
806
+ rescue SocketError, SystemCallError, IOError, Timeout::Error => err
807
+ mark_dead err.message
808
+ end
809
+
810
+ return @sock
811
+ ensure
812
+ @mutex.unlock if @multithread
813
+ end
814
+
815
+ ##
816
+ # Close the connection to the memcached server targeted by this
817
+ # object. The server is not considered dead.
818
+
819
+ def close
820
+ @mutex.lock if @multithread
821
+ @sock.close if @sock && !@sock.closed?
822
+ @sock = nil
823
+ @retry = nil
824
+ @status = "NOT CONNECTED"
825
+ ensure
826
+ @mutex.unlock if @multithread
827
+ end
828
+
829
+ private
830
+
831
+ ##
832
+ # Mark the server as dead and close its socket.
833
+
834
+ def mark_dead(reason = "Unknown error")
835
+ @sock.close if @sock && !@sock.closed?
836
+ @sock = nil
837
+ @retry = Time.now + RETRY_DELAY
838
+
839
+ @status = sprintf "DEAD: %s, will retry at %s", reason, @retry
840
+ end
841
+ end
842
+
843
+ ##
844
+ # Base MemCache exception class.
845
+
846
+ class MemCacheError < RuntimeError; end
847
+
848
+ end
849
+