activesupport 2.1.2 → 2.2.2

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 (240) hide show
  1. data/CHANGELOG +64 -5
  2. data/lib/active_support.rb +6 -6
  3. data/lib/active_support/base64.rb +13 -2
  4. data/lib/active_support/basic_object.rb +1 -1
  5. data/lib/active_support/buffered_logger.rb +15 -14
  6. data/lib/active_support/cache.rb +116 -40
  7. data/lib/active_support/cache/compressed_mem_cache_store.rb +7 -2
  8. data/lib/active_support/cache/file_store.rb +7 -5
  9. data/lib/active_support/cache/mem_cache_store.rb +48 -21
  10. data/lib/active_support/cache/memory_store.rb +16 -2
  11. data/lib/active_support/cache/synchronized_memory_store.rb +47 -0
  12. data/lib/active_support/callbacks.rb +14 -9
  13. data/lib/active_support/core_ext/array/access.rb +27 -0
  14. data/lib/active_support/core_ext/array/conversions.rb +12 -7
  15. data/lib/active_support/core_ext/array/grouping.rb +53 -10
  16. data/lib/active_support/core_ext/base64/encoding.rb +3 -0
  17. data/lib/active_support/core_ext/bigdecimal/conversions.rb +18 -22
  18. data/lib/active_support/core_ext/blank.rb +5 -0
  19. data/lib/active_support/core_ext/class/inheritable_attributes.rb +5 -5
  20. data/lib/active_support/core_ext/date/behavior.rb +3 -0
  21. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  22. data/lib/active_support/core_ext/date_time/calculations.rb +19 -5
  23. data/lib/active_support/core_ext/duplicable.rb +6 -0
  24. data/lib/active_support/core_ext/enumerable.rb +42 -4
  25. data/lib/active_support/core_ext/file.rb +4 -20
  26. data/lib/active_support/core_ext/file/atomic.rb +46 -0
  27. data/lib/active_support/core_ext/float.rb +2 -0
  28. data/lib/active_support/core_ext/float/time.rb +27 -0
  29. data/lib/active_support/core_ext/hash.rb +2 -1
  30. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  31. data/lib/active_support/core_ext/hash/deep_merge.rb +23 -0
  32. data/lib/active_support/core_ext/hash/except.rb +1 -1
  33. data/lib/active_support/core_ext/hash/reverse_merge.rb +12 -5
  34. data/lib/active_support/core_ext/hash/slice.rb +7 -4
  35. data/lib/active_support/core_ext/integer.rb +2 -0
  36. data/lib/active_support/core_ext/integer/time.rb +45 -0
  37. data/lib/active_support/core_ext/kernel/debugger.rb +4 -4
  38. data/lib/active_support/core_ext/logger.rb +128 -1
  39. data/lib/active_support/core_ext/module.rb +11 -1
  40. data/lib/active_support/core_ext/module/aliasing.rb +71 -67
  41. data/lib/active_support/core_ext/module/delegation.rb +32 -1
  42. data/lib/active_support/core_ext/module/introspection.rb +81 -66
  43. data/lib/active_support/core_ext/module/model_naming.rb +8 -7
  44. data/lib/active_support/core_ext/module/synchronization.rb +39 -0
  45. data/lib/active_support/core_ext/numeric/time.rb +0 -10
  46. data/lib/active_support/core_ext/object.rb +1 -0
  47. data/lib/active_support/core_ext/object/extending.rb +29 -7
  48. data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
  49. data/lib/active_support/core_ext/object/metaclass.rb +13 -0
  50. data/lib/active_support/core_ext/object/misc.rb +36 -21
  51. data/lib/active_support/core_ext/rexml.rb +2 -2
  52. data/lib/active_support/core_ext/string.rb +6 -2
  53. data/lib/active_support/core_ext/string/access.rb +5 -5
  54. data/lib/active_support/core_ext/string/behavior.rb +13 -0
  55. data/lib/active_support/core_ext/string/inflections.rb +21 -2
  56. data/lib/active_support/core_ext/string/multibyte.rb +81 -0
  57. data/lib/active_support/core_ext/time/calculations.rb +39 -12
  58. data/lib/active_support/core_ext/time/conversions.rb +1 -0
  59. data/lib/active_support/core_ext/time/zones.rb +1 -1
  60. data/lib/active_support/dependencies.rb +178 -110
  61. data/lib/active_support/deprecation.rb +24 -4
  62. data/lib/active_support/inflector.rb +122 -38
  63. data/lib/active_support/json.rb +1 -1
  64. data/lib/active_support/json/encoders/date.rb +9 -2
  65. data/lib/active_support/json/encoders/date_time.rb +9 -2
  66. data/lib/active_support/json/encoders/time.rb +10 -3
  67. data/lib/active_support/locale/en.yml +32 -0
  68. data/lib/active_support/memoizable.rb +82 -0
  69. data/lib/active_support/multibyte.rb +31 -7
  70. data/lib/active_support/multibyte/chars.rb +664 -122
  71. data/lib/active_support/multibyte/exceptions.rb +8 -0
  72. data/lib/active_support/multibyte/unicode_database.rb +71 -0
  73. data/lib/active_support/option_merger.rb +2 -10
  74. data/lib/active_support/ordered_hash.rb +15 -0
  75. data/lib/active_support/rescuable.rb +108 -0
  76. data/lib/active_support/secure_random.rb +197 -0
  77. data/lib/active_support/string_inquirer.rb +11 -1
  78. data/lib/active_support/test_case.rb +16 -5
  79. data/lib/active_support/testing/core_ext/test.rb +6 -0
  80. data/lib/active_support/{core_ext → testing/core_ext}/test/unit/assertions.rb +14 -6
  81. data/lib/active_support/testing/performance.rb +452 -0
  82. data/lib/active_support/testing/setup_and_teardown.rb +34 -7
  83. data/lib/active_support/time_with_zone.rb +66 -42
  84. data/lib/active_support/values/time_zone.rb +11 -4
  85. data/lib/active_support/values/unicode_tables.dat +0 -0
  86. data/lib/active_support/vendor.rb +13 -5
  87. data/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb +1 -1
  88. data/lib/active_support/vendor/i18n-0.0.1/i18n.rb +194 -0
  89. data/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb +216 -0
  90. data/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb +53 -0
  91. data/lib/active_support/vendor/{memcache-client-1.5.0 → memcache-client-1.5.1}/memcache.rb +5 -5
  92. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo.rb +0 -0
  93. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/data_timezone.rb +0 -0
  94. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/data_timezone_info.rb +0 -0
  95. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Algiers.rb +0 -0
  96. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Cairo.rb +0 -0
  97. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Casablanca.rb +0 -0
  98. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Harare.rb +0 -0
  99. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Johannesburg.rb +0 -0
  100. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Monrovia.rb +0 -0
  101. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Nairobi.rb +0 -0
  102. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Argentina/Buenos_Aires.rb +0 -0
  103. data/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/San_Juan.rb +86 -0
  104. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Bogota.rb +0 -0
  105. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Caracas.rb +0 -0
  106. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Chicago.rb +0 -0
  107. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Chihuahua.rb +0 -0
  108. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Denver.rb +0 -0
  109. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Godthab.rb +0 -0
  110. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Guatemala.rb +0 -0
  111. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Halifax.rb +0 -0
  112. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Indiana/Indianapolis.rb +0 -0
  113. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Juneau.rb +0 -0
  114. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/La_Paz.rb +0 -0
  115. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Lima.rb +0 -0
  116. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Los_Angeles.rb +0 -0
  117. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Mazatlan.rb +0 -0
  118. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Mexico_City.rb +0 -0
  119. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Monterrey.rb +0 -0
  120. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/New_York.rb +0 -0
  121. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Phoenix.rb +0 -0
  122. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Regina.rb +0 -0
  123. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Santiago.rb +0 -0
  124. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Sao_Paulo.rb +0 -0
  125. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/St_Johns.rb +0 -0
  126. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Tijuana.rb +0 -0
  127. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Almaty.rb +0 -0
  128. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Baghdad.rb +0 -0
  129. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Baku.rb +0 -0
  130. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Bangkok.rb +0 -0
  131. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Chongqing.rb +0 -0
  132. data/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Colombo.rb +30 -0
  133. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Dhaka.rb +0 -0
  134. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Hong_Kong.rb +0 -0
  135. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Irkutsk.rb +0 -0
  136. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Jakarta.rb +0 -0
  137. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Jerusalem.rb +0 -0
  138. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kabul.rb +0 -0
  139. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kamchatka.rb +0 -0
  140. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Karachi.rb +0 -0
  141. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Katmandu.rb +0 -0
  142. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kolkata.rb +0 -0
  143. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Krasnoyarsk.rb +0 -0
  144. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kuala_Lumpur.rb +0 -0
  145. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kuwait.rb +0 -0
  146. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Magadan.rb +0 -0
  147. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Muscat.rb +0 -0
  148. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Novosibirsk.rb +0 -0
  149. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Rangoon.rb +0 -0
  150. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Riyadh.rb +0 -0
  151. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Seoul.rb +0 -0
  152. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Shanghai.rb +0 -0
  153. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Singapore.rb +0 -0
  154. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Taipei.rb +0 -0
  155. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tashkent.rb +0 -0
  156. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tbilisi.rb +0 -0
  157. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tehran.rb +0 -0
  158. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tokyo.rb +0 -0
  159. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Ulaanbaatar.rb +0 -0
  160. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Urumqi.rb +0 -0
  161. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Vladivostok.rb +0 -0
  162. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Yakutsk.rb +0 -0
  163. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Yekaterinburg.rb +0 -0
  164. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Yerevan.rb +0 -0
  165. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Atlantic/Azores.rb +0 -0
  166. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Atlantic/Cape_Verde.rb +0 -0
  167. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Atlantic/South_Georgia.rb +0 -0
  168. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Adelaide.rb +0 -0
  169. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Brisbane.rb +0 -0
  170. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Darwin.rb +0 -0
  171. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Hobart.rb +0 -0
  172. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Melbourne.rb +0 -0
  173. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Perth.rb +0 -0
  174. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Sydney.rb +0 -0
  175. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Etc/UTC.rb +0 -0
  176. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Amsterdam.rb +0 -0
  177. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Athens.rb +0 -0
  178. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Belgrade.rb +0 -0
  179. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Berlin.rb +0 -0
  180. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Bratislava.rb +0 -0
  181. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Brussels.rb +0 -0
  182. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Bucharest.rb +0 -0
  183. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Budapest.rb +0 -0
  184. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Copenhagen.rb +0 -0
  185. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Dublin.rb +0 -0
  186. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Helsinki.rb +0 -0
  187. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Istanbul.rb +0 -0
  188. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Kiev.rb +0 -0
  189. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Lisbon.rb +0 -0
  190. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Ljubljana.rb +0 -0
  191. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/London.rb +0 -0
  192. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Madrid.rb +0 -0
  193. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Minsk.rb +0 -0
  194. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Moscow.rb +0 -0
  195. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Paris.rb +0 -0
  196. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Prague.rb +0 -0
  197. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Riga.rb +0 -0
  198. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Rome.rb +0 -0
  199. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Sarajevo.rb +0 -0
  200. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Skopje.rb +0 -0
  201. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Sofia.rb +0 -0
  202. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Stockholm.rb +0 -0
  203. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Tallinn.rb +0 -0
  204. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Vienna.rb +0 -0
  205. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Vilnius.rb +0 -0
  206. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Warsaw.rb +0 -0
  207. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Zagreb.rb +0 -0
  208. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Auckland.rb +0 -0
  209. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Fiji.rb +0 -0
  210. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Guam.rb +0 -0
  211. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Honolulu.rb +0 -0
  212. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Majuro.rb +0 -0
  213. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Midway.rb +0 -0
  214. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Noumea.rb +0 -0
  215. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Pago_Pago.rb +0 -0
  216. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Port_Moresby.rb +0 -0
  217. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Tongatapu.rb +0 -0
  218. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/info_timezone.rb +0 -0
  219. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/linked_timezone.rb +0 -0
  220. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/linked_timezone_info.rb +0 -0
  221. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/offset_rationals.rb +0 -0
  222. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/ruby_core_support.rb +0 -0
  223. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/time_or_datetime.rb +0 -0
  224. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone.rb +0 -0
  225. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_definition.rb +0 -0
  226. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_info.rb +0 -0
  227. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_offset_info.rb +0 -0
  228. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_period.rb +0 -0
  229. data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_transition_info.rb +0 -0
  230. data/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb +3 -3
  231. data/lib/active_support/version.rb +1 -1
  232. metadata +185 -167
  233. data/lib/active_support/clean_logger.rb +0 -127
  234. data/lib/active_support/core_ext/string/unicode.rb +0 -66
  235. data/lib/active_support/core_ext/test.rb +0 -1
  236. data/lib/active_support/multibyte/generators/generate_tables.rb +0 -149
  237. data/lib/active_support/multibyte/handlers/passthru_handler.rb +0 -9
  238. data/lib/active_support/multibyte/handlers/utf8_handler.rb +0 -564
  239. data/lib/active_support/multibyte/handlers/utf8_handler_proc.rb +0 -43
  240. data/lib/active_support/vendor/tzinfo-0.3.11/tzinfo/definitions/America/Argentina/San_Juan.rb +0 -170
@@ -1,9 +1,33 @@
1
- module ActiveSupport
2
- module Multibyte #:nodoc:
3
- DEFAULT_NORMALIZATION_FORM = :kc
4
- NORMALIZATIONS_FORMS = [:c, :kc, :d, :kd]
5
- UNICODE_VERSION = '5.0.0'
6
- end
7
- end
1
+ # encoding: utf-8
8
2
 
9
3
  require 'active_support/multibyte/chars'
4
+ require 'active_support/multibyte/exceptions'
5
+ require 'active_support/multibyte/unicode_database'
6
+
7
+ module ActiveSupport #:nodoc:
8
+ module Multibyte
9
+ # A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more
10
+ # information about normalization.
11
+ NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
12
+
13
+ # The Unicode version that is supported by the implementation
14
+ UNICODE_VERSION = '5.1.0'
15
+
16
+ # The default normalization used for operations that require normalization. It can be set to any of the
17
+ # normalizations in NORMALIZATION_FORMS.
18
+ #
19
+ # Example:
20
+ # ActiveSupport::Multibyte.default_normalization_form = :c
21
+ mattr_accessor :default_normalization_form
22
+ self.default_normalization_form = :kc
23
+
24
+ # The proxy class returned when calling mb_chars. You can use this accessor to configure your own proxy
25
+ # class so you can support other encodings. See the ActiveSupport::Multibyte::Chars implementation for
26
+ # an example how to do this.
27
+ #
28
+ # Example:
29
+ # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
30
+ mattr_accessor :proxy_class
31
+ self.proxy_class = ActiveSupport::Multibyte::Chars
32
+ end
33
+ end
@@ -1,137 +1,679 @@
1
- require 'active_support/multibyte/handlers/utf8_handler'
2
- require 'active_support/multibyte/handlers/passthru_handler'
3
-
4
- # Encapsulates all the functionality related to the Chars proxy.
5
- module ActiveSupport::Multibyte #:nodoc:
6
- # Chars enables you to work transparently with multibyte encodings in the Ruby String class without having extensive
7
- # knowledge about the encoding. A Chars object accepts a string upon initialization and proxies String methods in an
8
- # encoding safe manner. All the normal String methods are also implemented on the proxy.
9
- #
10
- # String methods are proxied through the Chars object, and can be accessed through the +chars+ method. Methods
11
- # which would normally return a String object now return a Chars object so methods can be chained.
12
- #
13
- # "The Perfect String ".chars.downcase.strip.normalize # => "the perfect string"
14
- #
15
- # Chars objects are perfectly interchangeable with String objects as long as no explicit class checks are made.
16
- # If certain methods do explicitly check the class, call +to_s+ before you pass chars objects to them.
17
- #
18
- # bad.explicit_checking_method "T".chars.downcase.to_s
19
- #
20
- # The actual operations on the string are delegated to handlers. Theoretically handlers can be implemented for
21
- # any encoding, but the default handler handles UTF-8. This handler is set during initialization, if you want to
22
- # use you own handler, you can set it on the Chars class. Look at the UTF8Handler source for an example how to
23
- # implement your own handler. If you your own handler to work on anything but UTF-8 you probably also
24
- # want to override Chars#handler.
25
- #
26
- # ActiveSupport::Multibyte::Chars.handler = MyHandler
27
- #
28
- # Note that a few methods are defined on Chars instead of the handler because they are defined on Object or Kernel
29
- # and method_missing can't catch them.
30
- class Chars
31
-
32
- attr_reader :string # The contained string
33
- alias_method :to_s, :string
34
-
35
- include Comparable
36
-
37
- # The magic method to make String and Chars comparable
38
- def to_str
39
- # Using any other ways of overriding the String itself will lead you all the way from infinite loops to
40
- # core dumps. Don't go there.
41
- @string
42
- end
1
+ # encoding: utf-8
2
+
3
+ module ActiveSupport #:nodoc:
4
+ module Multibyte #:nodoc:
5
+ # Chars enables you to work transparently with UTF-8 encoding in the Ruby String class without having extensive
6
+ # knowledge about the encoding. A Chars object accepts a string upon initialization and proxies String methods in an
7
+ # encoding safe manner. All the normal String methods are also implemented on the proxy.
8
+ #
9
+ # String methods are proxied through the Chars object, and can be accessed through the +mb_chars+ method. Methods
10
+ # which would normally return a String object now return a Chars object so methods can be chained.
11
+ #
12
+ # "The Perfect String ".mb_chars.downcase.strip.normalize #=> "the perfect string"
13
+ #
14
+ # Chars objects are perfectly interchangeable with String objects as long as no explicit class checks are made.
15
+ # If certain methods do explicitly check the class, call +to_s+ before you pass chars objects to them.
16
+ #
17
+ # bad.explicit_checking_method "T".mb_chars.downcase.to_s
18
+ #
19
+ # The default Chars implementation assumes that the encoding of the string is UTF-8, if you want to handle different
20
+ # encodings you can write your own multibyte string handler and configure it through
21
+ # ActiveSupport::Multibyte.proxy_class.
22
+ #
23
+ # class CharsForUTF32
24
+ # def size
25
+ # @wrapped_string.size / 4
26
+ # end
27
+ #
28
+ # def self.accepts?(string)
29
+ # string.length % 4 == 0
30
+ # end
31
+ # end
32
+ #
33
+ # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
34
+ class Chars
35
+ # Hangul character boundaries and properties
36
+ HANGUL_SBASE = 0xAC00
37
+ HANGUL_LBASE = 0x1100
38
+ HANGUL_VBASE = 0x1161
39
+ HANGUL_TBASE = 0x11A7
40
+ HANGUL_LCOUNT = 19
41
+ HANGUL_VCOUNT = 21
42
+ HANGUL_TCOUNT = 28
43
+ HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
44
+ HANGUL_SCOUNT = 11172
45
+ HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
46
+ HANGUL_JAMO_FIRST = 0x1100
47
+ HANGUL_JAMO_LAST = 0x11FF
48
+
49
+ # All the unicode whitespace
50
+ UNICODE_WHITESPACE = [
51
+ (0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
52
+ 0x0020, # White_Space # Zs SPACE
53
+ 0x0085, # White_Space # Cc <control-0085>
54
+ 0x00A0, # White_Space # Zs NO-BREAK SPACE
55
+ 0x1680, # White_Space # Zs OGHAM SPACE MARK
56
+ 0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
57
+ (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
58
+ 0x2028, # White_Space # Zl LINE SEPARATOR
59
+ 0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
60
+ 0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
61
+ 0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
62
+ 0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
63
+ ].flatten.freeze
64
+
65
+ # BOM (byte order mark) can also be seen as whitespace, it's a non-rendering character used to distinguish
66
+ # between little and big endian. This is not an issue in utf-8, so it must be ignored.
67
+ UNICODE_LEADERS_AND_TRAILERS = UNICODE_WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM
68
+
69
+ # Returns a regular expression pattern that matches the passed Unicode codepoints
70
+ def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
71
+ array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
72
+ end
73
+ UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/
74
+ UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/
75
+
76
+ # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
77
+ UTF8_PAT = /\A(?:
78
+ [\x00-\x7f] |
79
+ [\xc2-\xdf] [\x80-\xbf] |
80
+ \xe0 [\xa0-\xbf] [\x80-\xbf] |
81
+ [\xe1-\xef] [\x80-\xbf] [\x80-\xbf] |
82
+ \xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] |
83
+ [\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] |
84
+ \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf]
85
+ )*\z/xn
86
+
87
+ attr_reader :wrapped_string
88
+ alias to_s wrapped_string
89
+ alias to_str wrapped_string
90
+
91
+ if '1.9'.respond_to?(:force_encoding)
92
+ # Creates a new Chars instance by wrapping _string_.
93
+ def initialize(string)
94
+ @wrapped_string = string
95
+ @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
96
+ end
97
+ else
98
+ def initialize(string) #:nodoc:
99
+ @wrapped_string = string
100
+ end
101
+ end
102
+
103
+ # Forward all undefined methods to the wrapped string.
104
+ def method_missing(method, *args, &block)
105
+ if method.to_s =~ /!$/
106
+ @wrapped_string.__send__(method, *args, &block)
107
+ self
108
+ else
109
+ result = @wrapped_string.__send__(method, *args, &block)
110
+ result.kind_of?(String) ? chars(result) : result
111
+ end
112
+ end
43
113
 
44
- # Make duck-typing with String possible
45
- def respond_to?(method, include_priv = false)
46
- super || @string.respond_to?(method, include_priv) ||
47
- handler.respond_to?(method, include_priv) ||
48
- (method.to_s =~ /(.*)!/ && handler.respond_to?($1, include_priv)) ||
114
+ # Returns +true+ if _obj_ responds to the given method. Private methods are included in the search
115
+ # only if the optional second parameter evaluates to +true+.
116
+ def respond_to?(method, include_private=false)
117
+ super || @wrapped_string.respond_to?(method, include_private) || false
118
+ end
119
+
120
+ # Enable more predictable duck-typing on String-like classes. See Object#acts_like?.
121
+ def acts_like_string?
122
+ true
123
+ end
124
+
125
+ # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
126
+ # +false+ otherwise.
127
+ def self.wants?(string)
128
+ $KCODE == 'UTF8' && consumes?(string)
129
+ end
130
+
131
+ # Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
132
+ def self.consumes?(string)
133
+ # Unpack is a little bit faster than regular expressions.
134
+ string.unpack('U*')
135
+ true
136
+ rescue ArgumentError
49
137
  false
50
- end
138
+ end
51
139
 
52
- # Create a new Chars instance.
53
- def initialize(str)
54
- @string = str.respond_to?(:string) ? str.string : str
55
- end
56
-
57
- # Returns -1, 0 or +1 depending on whether the Chars object is to be sorted before, equal or after the
58
- # object on the right side of the operation. It accepts any object that implements +to_s+. See String.<=>
59
- # for more details.
60
- def <=>(other); @string <=> other.to_s; end
61
-
62
- # Works just like String#split, with the exception that the items in the resulting list are Chars
63
- # instances instead of String. This makes chaining methods easier.
64
- def split(*args)
65
- @string.split(*args).map { |i| i.chars }
66
- end
67
-
68
- # Gsub works exactly the same as gsub on a normal string.
69
- def gsub(*a, &b); @string.gsub(*a, &b).chars; end
70
-
71
- # Like String.=~ only it returns the character offset (in codepoints) instead of the byte offset.
72
- def =~(other)
73
- handler.translate_offset(@string, @string =~ other)
74
- end
75
-
76
- # Try to forward all undefined methods to the handler, when a method is not defined on the handler, send it to
77
- # the contained string. Method_missing is also responsible for making the bang! methods destructive.
78
- def method_missing(m, *a, &b)
79
- begin
80
- # Simulate methods with a ! at the end because we can't touch the enclosed string from the handlers.
81
- if m.to_s =~ /^(.*)\!$/ && handler.respond_to?($1)
82
- result = handler.send($1, @string, *a, &b)
83
- if result == @string
84
- result = nil
140
+ include Comparable
141
+
142
+ # Returns <tt>-1</tt>, <tt>0</tt> or <tt>+1</tt> depending on whether the Chars object is to be sorted before,
143
+ # equal or after the object on the right side of the operation. It accepts any object that implements +to_s+.
144
+ # See <tt>String#<=></tt> for more details.
145
+ #
146
+ # Example:
147
+ # 'é'.mb_chars <=> 'ü'.mb_chars #=> -1
148
+ def <=>(other)
149
+ @wrapped_string <=> other.to_s
150
+ end
151
+
152
+ # Returns a new Chars object containing the _other_ object concatenated to the string.
153
+ #
154
+ # Example:
155
+ # ('Café'.mb_chars + ' périferôl').to_s #=> "Café périferôl"
156
+ def +(other)
157
+ self << other
158
+ end
159
+
160
+ # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
161
+ #
162
+ # Example:
163
+ # 'Café périferôl'.mb_chars =~ /ô/ #=> 12
164
+ def =~(other)
165
+ translate_offset(@wrapped_string =~ other)
166
+ end
167
+
168
+ # Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
169
+ # instances instead of String. This makes chaining methods easier.
170
+ #
171
+ # Example:
172
+ # 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } #=> ["CAF", " P", "RIFERÔL"]
173
+ def split(*args)
174
+ @wrapped_string.split(*args).map { |i| i.mb_chars }
175
+ end
176
+
177
+ # Inserts the passed string at specified codepoint offsets.
178
+ #
179
+ # Example:
180
+ # 'Café'.mb_chars.insert(4, ' périferôl').to_s #=> "Café périferôl"
181
+ def insert(offset, fragment)
182
+ unpacked = self.class.u_unpack(@wrapped_string)
183
+ unless offset > unpacked.length
184
+ @wrapped_string.replace(
185
+ self.class.u_unpack(@wrapped_string).insert(offset, *self.class.u_unpack(fragment)).pack('U*')
186
+ )
187
+ else
188
+ raise IndexError, "index #{offset} out of string"
189
+ end
190
+ self
191
+ end
192
+
193
+ # Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
194
+ #
195
+ # Example:
196
+ # 'Café'.mb_chars.include?('é') #=> true
197
+ def include?(other)
198
+ # We have to redefine this method because Enumerable defines it.
199
+ @wrapped_string.include?(other)
200
+ end
201
+
202
+ # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
203
+ #
204
+ # Example:
205
+ # 'Café périferôl'.mb_chars.index('ô') #=> 12
206
+ # 'Café périferôl'.mb_chars.index(/\w/u) #=> 0
207
+ def index(needle, offset=0)
208
+ index = @wrapped_string.index(needle, offset)
209
+ index ? (self.class.u_unpack(@wrapped_string.slice(0...index)).size) : nil
210
+ end
211
+
212
+ # Like <tt>String#[]=</tt>, except instead of byte offsets you specify character offsets.
213
+ #
214
+ # Example:
215
+ #
216
+ # s = "Müller"
217
+ # s.mb_chars[2] = "e" # Replace character with offset 2
218
+ # s
219
+ # #=> "Müeler"
220
+ #
221
+ # s = "Müller"
222
+ # s.mb_chars[1, 2] = "ö" # Replace 2 characters at character offset 1
223
+ # s
224
+ # #=> "Möler"
225
+ def []=(*args)
226
+ replace_by = args.pop
227
+ # Indexed replace with regular expressions already works
228
+ if args.first.is_a?(Regexp)
229
+ @wrapped_string[*args] = replace_by
230
+ else
231
+ result = self.class.u_unpack(@wrapped_string)
232
+ if args[0].is_a?(Fixnum)
233
+ raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
234
+ min = args[0]
235
+ max = args[1].nil? ? min : (min + args[1] - 1)
236
+ range = Range.new(min, max)
237
+ replace_by = [replace_by].pack('U') if replace_by.is_a?(Fixnum)
238
+ elsif args.first.is_a?(Range)
239
+ raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
240
+ range = args[0]
85
241
  else
86
- @string.replace result
242
+ needle = args[0].to_s
243
+ min = index(needle)
244
+ max = min + self.class.u_unpack(needle).length - 1
245
+ range = Range.new(min, max)
87
246
  end
88
- elsif handler.respond_to?(m)
89
- result = handler.send(m, @string, *a, &b)
90
- else
91
- result = @string.send(m, *a, &b)
247
+ result[range] = self.class.u_unpack(replace_by)
248
+ @wrapped_string.replace(result.pack('U*'))
92
249
  end
93
- rescue Handlers::EncodingError
94
- @string.replace handler.tidy_bytes(@string)
95
- retry
96
250
  end
97
-
98
- if result.kind_of?(String)
99
- result.chars
100
- else
101
- result
251
+
252
+ # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes.
253
+ #
254
+ # Example:
255
+ #
256
+ # "¾ cup".mb_chars.rjust(8).to_s
257
+ # #=> " ¾ cup"
258
+ #
259
+ # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
260
+ # #=> "   ¾ cup"
261
+ def rjust(integer, padstr=' ')
262
+ justify(integer, :right, padstr)
102
263
  end
103
- end
104
-
105
- # Set the handler class for the Char objects.
106
- def self.handler=(klass)
107
- @@handler = klass
108
- end
109
264
 
110
- # Returns the proper handler for the contained string depending on $KCODE and the encoding of the string. This
111
- # method is used internally to always redirect messages to the proper classes depending on the context.
112
- def handler
113
- if utf8_pragma?
114
- @@handler
115
- else
116
- ActiveSupport::Multibyte::Handlers::PassthruHandler
265
+ # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes.
266
+ #
267
+ # Example:
268
+ #
269
+ # "¾ cup".mb_chars.rjust(8).to_s
270
+ # #=> "¾ cup "
271
+ #
272
+ # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
273
+ # #=> "¾ cup   "
274
+ def ljust(integer, padstr=' ')
275
+ justify(integer, :left, padstr)
276
+ end
277
+
278
+ # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes.
279
+ #
280
+ # Example:
281
+ #
282
+ # "¾ cup".mb_chars.center(8).to_s
283
+ # #=> " ¾ cup "
284
+ #
285
+ # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace
286
+ # #=> " ¾ cup  "
287
+ def center(integer, padstr=' ')
288
+ justify(integer, :center, padstr)
117
289
  end
118
- end
119
290
 
120
- private
291
+ # Strips entire range of Unicode whitespace from the right of the string.
292
+ def rstrip
293
+ chars(@wrapped_string.gsub(UNICODE_TRAILERS_PAT, ''))
294
+ end
121
295
 
122
- # +utf8_pragma+ checks if it can send this string to the handlers. It makes sure @string isn't nil and $KCODE is
123
- # set to 'UTF8'.
124
- def utf8_pragma?
125
- !@string.nil? && ($KCODE == 'UTF8')
296
+ # Strips entire range of Unicode whitespace from the left of the string.
297
+ def lstrip
298
+ chars(@wrapped_string.gsub(UNICODE_LEADERS_PAT, ''))
126
299
  end
127
- end
128
- end
300
+
301
+ # Strips entire range of Unicode whitespace from the right and left of the string.
302
+ def strip
303
+ rstrip.lstrip
304
+ end
305
+
306
+ # Returns the number of codepoints in the string
307
+ def size
308
+ self.class.u_unpack(@wrapped_string).size
309
+ end
310
+ alias_method :length, :size
311
+
312
+ # Reverses all characters in the string.
313
+ #
314
+ # Example:
315
+ # 'Café'.mb_chars.reverse.to_s #=> 'éfaC'
316
+ def reverse
317
+ chars(self.class.u_unpack(@wrapped_string).reverse.pack('U*'))
318
+ end
319
+
320
+ # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
321
+ # character.
322
+ #
323
+ # Example:
324
+ # 'こんにちは'.mb_chars.slice(2..3).to_s #=> "にち"
325
+ def slice(*args)
326
+ if args.size > 2
327
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
328
+ elsif (args.size == 2 && !(args.first.is_a?(Numeric) || args.first.is_a?(Regexp)))
329
+ raise TypeError, "cannot convert #{args.first.class} into Integer" # Do as if we were native
330
+ elsif (args.size == 2 && !args[1].is_a?(Numeric))
331
+ raise TypeError, "cannot convert #{args[1].class} into Integer" # Do as if we were native
332
+ elsif args[0].kind_of? Range
333
+ cps = self.class.u_unpack(@wrapped_string).slice(*args)
334
+ result = cps.nil? ? nil : cps.pack('U*')
335
+ elsif args[0].kind_of? Regexp
336
+ result = @wrapped_string.slice(*args)
337
+ elsif args.size == 1 && args[0].kind_of?(Numeric)
338
+ character = self.class.u_unpack(@wrapped_string)[args[0]]
339
+ result = character.nil? ? nil : [character].pack('U')
340
+ else
341
+ result = self.class.u_unpack(@wrapped_string).slice(*args).pack('U*')
342
+ end
343
+ result.nil? ? nil : chars(result)
344
+ end
345
+ alias_method :[], :slice
346
+
347
+ # Convert characters in the string to uppercase.
348
+ #
349
+ # Example:
350
+ # 'Laurent, òu sont les tests?'.mb_chars.upcase.to_s #=> "LAURENT, ÒU SONT LES TESTS?"
351
+ def upcase
352
+ apply_mapping :uppercase_mapping
353
+ end
354
+
355
+ # Convert characters in the string to lowercase.
356
+ #
357
+ # Example:
358
+ # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s #=> "věda a výzkum"
359
+ def downcase
360
+ apply_mapping :lowercase_mapping
361
+ end
362
+
363
+ # Converts the first character to uppercase and the remainder to lowercase.
364
+ #
365
+ # Example:
366
+ # 'über'.mb_chars.capitalize.to_s #=> "Über"
367
+ def capitalize
368
+ (slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
369
+ end
370
+
371
+ # Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
372
+ # passing strings to databases and validations.
373
+ #
374
+ # * <tt>str</tt> - The string to perform normalization on.
375
+ # * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
376
+ # <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
377
+ # ActiveSupport::Multibyte.default_normalization_form
378
+ def normalize(form=ActiveSupport::Multibyte.default_normalization_form)
379
+ # See http://www.unicode.org/reports/tr15, Table 1
380
+ codepoints = self.class.u_unpack(@wrapped_string)
381
+ chars(case form
382
+ when :d
383
+ self.class.reorder_characters(self.class.decompose_codepoints(:canonical, codepoints))
384
+ when :c
385
+ self.class.compose_codepoints(self.class.reorder_characters(self.class.decompose_codepoints(:canonical, codepoints)))
386
+ when :kd
387
+ self.class.reorder_characters(self.class.decompose_codepoints(:compatability, codepoints))
388
+ when :kc
389
+ self.class.compose_codepoints(self.class.reorder_characters(self.class.decompose_codepoints(:compatability, codepoints)))
390
+ else
391
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
392
+ end.pack('U*'))
393
+ end
394
+
395
+ # Performs canonical decomposition on all the characters.
396
+ #
397
+ # Example:
398
+ # 'é'.length #=> 2
399
+ # 'é'.mb_chars.decompose.to_s.length #=> 3
400
+ def decompose
401
+ chars(self.class.decompose_codepoints(:canonical, self.class.u_unpack(@wrapped_string)).pack('U*'))
402
+ end
403
+
404
+ # Performs composition on all the characters.
405
+ #
406
+ # Example:
407
+ # 'é'.length #=> 3
408
+ # 'é'.mb_chars.compose.to_s.length #=> 2
409
+ def compose
410
+ chars(self.class.compose_codepoints(self.class.u_unpack(@wrapped_string)).pack('U*'))
411
+ end
412
+
413
+ # Returns the number of grapheme clusters in the string.
414
+ #
415
+ # Example:
416
+ # 'क्षि'.mb_chars.length #=> 4
417
+ # 'क्षि'.mb_chars.g_length #=> 3
418
+ def g_length
419
+ self.class.g_unpack(@wrapped_string).length
420
+ end
421
+
422
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
423
+ def tidy_bytes
424
+ chars(self.class.tidy_bytes(@wrapped_string))
425
+ end
426
+
427
+ %w(lstrip rstrip strip reverse upcase downcase slice tidy_bytes capitalize).each do |method|
428
+ define_method("#{method}!") do |*args|
429
+ unless args.nil?
430
+ @wrapped_string = send(method, *args).to_s
431
+ else
432
+ @wrapped_string = send(method).to_s
433
+ end
434
+ self
435
+ end
436
+ end
437
+
438
+ class << self
439
+
440
+ # Unpack the string at codepoints boundaries. Raises an EncodingError when the encoding of the string isn't
441
+ # valid UTF-8.
442
+ #
443
+ # Example:
444
+ # Chars.u_unpack('Café') #=> [67, 97, 102, 233]
445
+ def u_unpack(string)
446
+ begin
447
+ string.unpack 'U*'
448
+ rescue ArgumentError
449
+ raise EncodingError, 'malformed UTF-8 character'
450
+ end
451
+ end
452
+
453
+ # Detect whether the codepoint is in a certain character class. Returns +true+ when it's in the specified
454
+ # character class and +false+ otherwise. Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
455
+ # <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
456
+ #
457
+ # Primarily used by the grapheme cluster support.
458
+ def in_char_class?(codepoint, classes)
459
+ classes.detect { |c| UCD.boundary[c] === codepoint } ? true : false
460
+ end
129
461
 
130
- # When we can load the utf8proc library, override normalization with the faster methods
131
- begin
132
- require 'utf8proc_native'
133
- require 'active_support/multibyte/handlers/utf8_handler_proc'
134
- ActiveSupport::Multibyte::Chars.handler = ActiveSupport::Multibyte::Handlers::UTF8HandlerProc
135
- rescue LoadError
136
- ActiveSupport::Multibyte::Chars.handler = ActiveSupport::Multibyte::Handlers::UTF8Handler
462
+ # Unpack the string at grapheme boundaries. Returns a list of character lists.
463
+ #
464
+ # Example:
465
+ # Chars.g_unpack('क्षि') #=> [[2325, 2381], [2359], [2367]]
466
+ # Chars.g_unpack('Café') #=> [[67], [97], [102], [233]]
467
+ def g_unpack(string)
468
+ codepoints = u_unpack(string)
469
+ unpacked = []
470
+ pos = 0
471
+ marker = 0
472
+ eoc = codepoints.length
473
+ while(pos < eoc)
474
+ pos += 1
475
+ previous = codepoints[pos-1]
476
+ current = codepoints[pos]
477
+ if (
478
+ # CR X LF
479
+ one = ( previous == UCD.boundary[:cr] and current == UCD.boundary[:lf] ) or
480
+ # L X (L|V|LV|LVT)
481
+ two = ( UCD.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or
482
+ # (LV|V) X (V|T)
483
+ three = ( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or
484
+ # (LVT|T) X (T)
485
+ four = ( in_char_class?(previous, [:lvt,:t]) and UCD.boundary[:t] === current ) or
486
+ # X Extend
487
+ five = (UCD.boundary[:extend] === current)
488
+ )
489
+ else
490
+ unpacked << codepoints[marker..pos-1]
491
+ marker = pos
492
+ end
493
+ end
494
+ unpacked
495
+ end
496
+
497
+ # Reverse operation of g_unpack.
498
+ #
499
+ # Example:
500
+ # Chars.g_pack(Chars.g_unpack('क्षि')) #=> 'क्षि'
501
+ def g_pack(unpacked)
502
+ (unpacked.flatten).pack('U*')
503
+ end
504
+
505
+ def padding(padsize, padstr=' ') #:nodoc:
506
+ if padsize != 0
507
+ new(padstr * ((padsize / u_unpack(padstr).size) + 1)).slice(0, padsize)
508
+ else
509
+ ''
510
+ end
511
+ end
512
+
513
+ # Re-order codepoints so the string becomes canonical.
514
+ def reorder_characters(codepoints)
515
+ length = codepoints.length- 1
516
+ pos = 0
517
+ while pos < length do
518
+ cp1, cp2 = UCD.codepoints[codepoints[pos]], UCD.codepoints[codepoints[pos+1]]
519
+ if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
520
+ codepoints[pos..pos+1] = cp2.code, cp1.code
521
+ pos += (pos > 0 ? -1 : 1)
522
+ else
523
+ pos += 1
524
+ end
525
+ end
526
+ codepoints
527
+ end
528
+
529
+ # Decompose composed characters to the decomposed form.
530
+ def decompose_codepoints(type, codepoints)
531
+ codepoints.inject([]) do |decomposed, cp|
532
+ # if it's a hangul syllable starter character
533
+ if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
534
+ sindex = cp - HANGUL_SBASE
535
+ ncp = [] # new codepoints
536
+ ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
537
+ ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
538
+ tindex = sindex % HANGUL_TCOUNT
539
+ ncp << (HANGUL_TBASE + tindex) unless tindex == 0
540
+ decomposed.concat ncp
541
+ # if the codepoint is decomposable in with the current decomposition type
542
+ elsif (ncp = UCD.codepoints[cp].decomp_mapping) and (!UCD.codepoints[cp].decomp_type || type == :compatability)
543
+ decomposed.concat decompose_codepoints(type, ncp.dup)
544
+ else
545
+ decomposed << cp
546
+ end
547
+ end
548
+ end
549
+
550
+ # Compose decomposed characters to the composed form.
551
+ def compose_codepoints(codepoints)
552
+ pos = 0
553
+ eoa = codepoints.length - 1
554
+ starter_pos = 0
555
+ starter_char = codepoints[0]
556
+ previous_combining_class = -1
557
+ while pos < eoa
558
+ pos += 1
559
+ lindex = starter_char - HANGUL_LBASE
560
+ # -- Hangul
561
+ if 0 <= lindex and lindex < HANGUL_LCOUNT
562
+ vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
563
+ if 0 <= vindex and vindex < HANGUL_VCOUNT
564
+ tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
565
+ if 0 <= tindex and tindex < HANGUL_TCOUNT
566
+ j = starter_pos + 2
567
+ eoa -= 2
568
+ else
569
+ tindex = 0
570
+ j = starter_pos + 1
571
+ eoa -= 1
572
+ end
573
+ codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
574
+ end
575
+ starter_pos += 1
576
+ starter_char = codepoints[starter_pos]
577
+ # -- Other characters
578
+ else
579
+ current_char = codepoints[pos]
580
+ current = UCD.codepoints[current_char]
581
+ if current.combining_class > previous_combining_class
582
+ if ref = UCD.composition_map[starter_char]
583
+ composition = ref[current_char]
584
+ else
585
+ composition = nil
586
+ end
587
+ unless composition.nil?
588
+ codepoints[starter_pos] = composition
589
+ starter_char = composition
590
+ codepoints.delete_at pos
591
+ eoa -= 1
592
+ pos -= 1
593
+ previous_combining_class = -1
594
+ else
595
+ previous_combining_class = current.combining_class
596
+ end
597
+ else
598
+ previous_combining_class = current.combining_class
599
+ end
600
+ if current.combining_class == 0
601
+ starter_pos = pos
602
+ starter_char = codepoints[pos]
603
+ end
604
+ end
605
+ end
606
+ codepoints
607
+ end
608
+
609
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
610
+ def tidy_bytes(string)
611
+ string.split(//u).map do |c|
612
+ if !UTF8_PAT.match(c)
613
+ n = c.unpack('C')[0]
614
+ n < 128 ? n.chr :
615
+ n < 160 ? [UCD.cp1252[n] || n].pack('U') :
616
+ n < 192 ? "\xC2" + n.chr : "\xC3" + (n-64).chr
617
+ else
618
+ c
619
+ end
620
+ end.join
621
+ end
622
+ end
623
+
624
+ protected
625
+
626
+ def translate_offset(byte_offset) #:nodoc:
627
+ return nil if byte_offset.nil?
628
+ return 0 if @wrapped_string == ''
629
+ chunk = @wrapped_string[0..byte_offset]
630
+ begin
631
+ begin
632
+ chunk.unpack('U*').length - 1
633
+ rescue ArgumentError => e
634
+ chunk = @wrapped_string[0..(byte_offset+=1)]
635
+ # Stop retrying at the end of the string
636
+ raise e unless byte_offset < chunk.length
637
+ # We damaged a character, retry
638
+ retry
639
+ end
640
+ # Catch the ArgumentError so we can throw our own
641
+ rescue ArgumentError
642
+ raise EncodingError, 'malformed UTF-8 character'
643
+ end
644
+ end
645
+
646
+ def justify(integer, way, padstr=' ') #:nodoc:
647
+ raise ArgumentError, "zero width padding" if padstr.length == 0
648
+ padsize = integer - size
649
+ padsize = padsize > 0 ? padsize : 0
650
+ case way
651
+ when :right
652
+ result = @wrapped_string.dup.insert(0, self.class.padding(padsize, padstr))
653
+ when :left
654
+ result = @wrapped_string.dup.insert(-1, self.class.padding(padsize, padstr))
655
+ when :center
656
+ lpad = self.class.padding((padsize / 2.0).floor, padstr)
657
+ rpad = self.class.padding((padsize / 2.0).ceil, padstr)
658
+ result = @wrapped_string.dup.insert(0, lpad).insert(-1, rpad)
659
+ end
660
+ chars(result)
661
+ end
662
+
663
+ def apply_mapping(mapping) #:nodoc:
664
+ chars(self.class.u_unpack(@wrapped_string).map do |codepoint|
665
+ cp = UCD.codepoints[codepoint]
666
+ if cp and (ncp = cp.send(mapping)) and ncp > 0
667
+ ncp
668
+ else
669
+ codepoint
670
+ end
671
+ end.pack('U*'))
672
+ end
673
+
674
+ def chars(string) #:nodoc:
675
+ self.class.new(string)
676
+ end
677
+ end
678
+ end
137
679
  end