activesupport 3.0.0.beta4 → 3.0.pre

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 (333) hide show
  1. data/CHANGELOG +1 -100
  2. data/lib/active_support.rb +4 -38
  3. data/lib/active_support/autoload.rb +28 -0
  4. data/lib/active_support/backtrace_cleaner.rb +9 -31
  5. data/lib/active_support/benchmarkable.rb +0 -1
  6. data/lib/active_support/buffered_logger.rb +1 -0
  7. data/lib/active_support/cache.rb +81 -436
  8. data/lib/active_support/cache/compressed_mem_cache_store.rb +13 -6
  9. data/lib/active_support/cache/file_store.rb +41 -139
  10. data/lib/active_support/cache/mem_cache_store.rb +75 -120
  11. data/lib/active_support/cache/memory_store.rb +27 -127
  12. data/lib/active_support/cache/strategy/local_cache.rb +58 -111
  13. data/lib/active_support/cache/synchronized_memory_store.rb +38 -2
  14. data/lib/active_support/callbacks.rb +48 -87
  15. data/lib/active_support/configurable.rb +18 -19
  16. data/lib/active_support/core_ext/array.rb +0 -1
  17. data/lib/active_support/core_ext/array/access.rb +1 -1
  18. data/lib/active_support/core_ext/array/conversions.rb +54 -29
  19. data/lib/active_support/core_ext/array/extract_options.rb +1 -16
  20. data/lib/active_support/core_ext/array/random_access.rb +5 -19
  21. data/lib/active_support/core_ext/array/wrap.rb +9 -13
  22. data/lib/active_support/core_ext/benchmark.rb +12 -0
  23. data/lib/active_support/core_ext/boolean.rb +1 -0
  24. data/lib/active_support/core_ext/boolean/conversions.rb +11 -0
  25. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -2
  26. data/lib/active_support/core_ext/class.rb +1 -1
  27. data/lib/active_support/core_ext/class/attribute_accessors.rb +27 -33
  28. data/lib/active_support/core_ext/class/delegating_attributes.rb +41 -35
  29. data/lib/active_support/core_ext/class/inheritable_attributes.rb +13 -22
  30. data/lib/active_support/core_ext/class/removal.rb +53 -0
  31. data/lib/active_support/core_ext/date.rb +7 -0
  32. data/lib/active_support/core_ext/date/calculations.rb +8 -30
  33. data/lib/active_support/core_ext/date/conversions.rb +2 -2
  34. data/lib/active_support/core_ext/date_time.rb +5 -0
  35. data/lib/active_support/core_ext/date_time/calculations.rb +1 -2
  36. data/lib/active_support/core_ext/date_time/conversions.rb +5 -23
  37. data/lib/active_support/core_ext/enumerable.rb +9 -5
  38. data/lib/active_support/core_ext/exception.rb +47 -0
  39. data/lib/active_support/core_ext/file.rb +0 -1
  40. data/lib/active_support/core_ext/file/atomic.rb +2 -3
  41. data/lib/active_support/core_ext/float/rounding.rb +2 -3
  42. data/lib/active_support/core_ext/hash/conversions.rb +145 -65
  43. data/lib/active_support/core_ext/hash/deep_merge.rb +7 -6
  44. data/lib/active_support/core_ext/hash/except.rb +0 -8
  45. data/lib/active_support/core_ext/hash/indifferent_access.rb +0 -5
  46. data/lib/active_support/core_ext/hash/keys.rb +11 -10
  47. data/lib/active_support/core_ext/hash/slice.rb +0 -6
  48. data/lib/active_support/core_ext/integer.rb +1 -1
  49. data/lib/active_support/core_ext/integer/even_odd.rb +16 -0
  50. data/lib/active_support/core_ext/kernel.rb +1 -1
  51. data/lib/active_support/core_ext/kernel/daemonizing.rb +7 -0
  52. data/lib/active_support/core_ext/kernel/debugger.rb +2 -3
  53. data/lib/active_support/core_ext/kernel/reporting.rb +1 -2
  54. data/lib/active_support/core_ext/load_error.rb +30 -17
  55. data/lib/active_support/core_ext/logger.rb +1 -1
  56. data/lib/active_support/core_ext/module.rb +3 -5
  57. data/lib/active_support/core_ext/module/aliasing.rb +1 -1
  58. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +1 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +21 -25
  61. data/lib/active_support/core_ext/module/delegation.rb +10 -21
  62. data/lib/active_support/core_ext/module/inclusion.rb +30 -0
  63. data/lib/active_support/core_ext/module/introspection.rb +8 -8
  64. data/lib/active_support/core_ext/module/loading.rb +23 -0
  65. data/lib/active_support/core_ext/module/synchronization.rb +1 -2
  66. data/lib/active_support/core_ext/name_error.rb +1 -3
  67. data/lib/active_support/core_ext/nil.rb +1 -0
  68. data/lib/active_support/core_ext/nil/conversions.rb +5 -0
  69. data/lib/active_support/core_ext/object.rb +2 -6
  70. data/lib/active_support/core_ext/object/blank.rb +2 -20
  71. data/lib/active_support/core_ext/object/conversions.rb +16 -2
  72. data/lib/active_support/core_ext/object/duplicable.rb +1 -23
  73. data/lib/active_support/core_ext/object/extending.rb +77 -8
  74. data/lib/active_support/core_ext/object/instance_variables.rb +7 -0
  75. data/lib/active_support/core_ext/object/metaclass.rb +13 -0
  76. data/lib/active_support/core_ext/object/misc.rb +1 -0
  77. data/lib/active_support/core_ext/object/tap.rb +16 -0
  78. data/lib/active_support/core_ext/object/with_options.rb +0 -2
  79. data/lib/active_support/core_ext/proc.rb +4 -4
  80. data/lib/active_support/core_ext/regexp.rb +22 -0
  81. data/lib/active_support/core_ext/rexml.rb +1 -4
  82. data/lib/active_support/core_ext/string.rb +2 -3
  83. data/lib/active_support/core_ext/string/access.rb +2 -4
  84. data/lib/active_support/core_ext/string/bytesize.rb +5 -0
  85. data/lib/active_support/core_ext/string/conversions.rb +1 -36
  86. data/lib/active_support/core_ext/string/filters.rb +0 -29
  87. data/lib/active_support/core_ext/string/inflections.rb +12 -1
  88. data/lib/active_support/core_ext/string/interpolation.rb +92 -2
  89. data/lib/active_support/core_ext/string/iterators.rb +13 -0
  90. data/lib/active_support/core_ext/string/multibyte.rb +19 -16
  91. data/lib/active_support/core_ext/string/output_safety.rb +35 -101
  92. data/lib/active_support/core_ext/string/starts_ends_with.rb +14 -0
  93. data/lib/active_support/core_ext/string/xchar.rb +1 -1
  94. data/lib/active_support/core_ext/symbol.rb +1 -0
  95. data/lib/active_support/core_ext/symbol/to_proc.rb +14 -0
  96. data/lib/active_support/core_ext/time.rb +10 -0
  97. data/lib/active_support/core_ext/time/calculations.rb +7 -9
  98. data/lib/active_support/core_ext/time/conversions.rb +0 -1
  99. data/lib/active_support/core_ext/time/marshal_with_utc_flag.rb +22 -0
  100. data/lib/active_support/core_ext/uri.rb +4 -10
  101. data/lib/active_support/dependencies.rb +192 -179
  102. data/lib/active_support/deprecated_callbacks.rb +283 -0
  103. data/lib/active_support/deprecation/behaviors.rb +1 -1
  104. data/lib/active_support/deprecation/method_wrappers.rb +9 -10
  105. data/lib/active_support/deprecation/reporting.rb +1 -2
  106. data/lib/active_support/duration.rb +2 -6
  107. data/lib/active_support/hash_with_indifferent_access.rb +1 -9
  108. data/lib/active_support/inflections.rb +1 -1
  109. data/lib/active_support/inflector.rb +407 -4
  110. data/lib/active_support/json/backends/jsongem.rb +9 -12
  111. data/lib/active_support/json/decoding.rb +1 -16
  112. data/lib/active_support/json/encoding.rb +12 -42
  113. data/lib/active_support/locale/en.yml +1 -4
  114. data/lib/active_support/memoizable.rb +1 -1
  115. data/lib/active_support/message_encryptor.rb +0 -1
  116. data/lib/active_support/message_verifier.rb +5 -6
  117. data/lib/active_support/multibyte.rb +22 -7
  118. data/lib/active_support/multibyte/chars.rb +392 -164
  119. data/lib/active_support/multibyte/unicode_database.rb +71 -0
  120. data/lib/active_support/multibyte/utils.rb +7 -6
  121. data/lib/active_support/notifications.rb +113 -23
  122. data/lib/active_support/ordered_hash.rb +11 -35
  123. data/lib/active_support/ordered_options.rb +0 -6
  124. data/lib/active_support/rescuable.rb +4 -7
  125. data/lib/active_support/ruby/shim.rb +6 -4
  126. data/lib/active_support/test_case.rb +7 -2
  127. data/lib/active_support/testing/assertions.rb +0 -15
  128. data/lib/active_support/testing/declarative.rb +1 -1
  129. data/lib/active_support/testing/isolation.rb +19 -63
  130. data/lib/active_support/testing/performance.rb +337 -342
  131. data/lib/active_support/testing/setup_and_teardown.rb +29 -51
  132. data/lib/active_support/time.rb +3 -23
  133. data/lib/active_support/time_with_zone.rb +10 -5
  134. data/lib/active_support/values/time_zone.rb +84 -40
  135. data/lib/active_support/values/unicode_tables.dat +0 -0
  136. data/lib/active_support/vendor.rb +16 -0
  137. data/lib/active_support/vendor/builder-2.1.2/lib/blankslate.rb +113 -0
  138. data/lib/active_support/vendor/builder-2.1.2/lib/builder.rb +13 -0
  139. data/lib/active_support/vendor/builder-2.1.2/lib/builder/blankslate.rb +20 -0
  140. data/lib/active_support/vendor/builder-2.1.2/lib/builder/css.rb +250 -0
  141. data/lib/active_support/vendor/builder-2.1.2/lib/builder/xchar.rb +115 -0
  142. data/lib/active_support/vendor/builder-2.1.2/lib/builder/xmlbase.rb +139 -0
  143. data/lib/active_support/vendor/builder-2.1.2/lib/builder/xmlevents.rb +63 -0
  144. data/lib/active_support/vendor/builder-2.1.2/lib/builder/xmlmarkup.rb +328 -0
  145. data/lib/active_support/vendor/i18n-0.1.3/MIT-LICENSE +20 -0
  146. data/lib/active_support/vendor/i18n-0.1.3/README.textile +20 -0
  147. data/lib/active_support/vendor/i18n-0.1.3/Rakefile +5 -0
  148. data/lib/active_support/vendor/i18n-0.1.3/i18n.gemspec +27 -0
  149. data/lib/active_support/vendor/i18n-0.1.3/lib/i18n.rb +204 -0
  150. data/lib/active_support/vendor/i18n-0.1.3/lib/i18n/backend/simple.rb +215 -0
  151. data/lib/active_support/vendor/i18n-0.1.3/lib/i18n/exceptions.rb +53 -0
  152. data/lib/active_support/vendor/i18n-0.1.3/test/all.rb +5 -0
  153. data/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb +99 -0
  154. data/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb +124 -0
  155. data/lib/active_support/vendor/i18n-0.1.3/test/locale/en.rb +1 -0
  156. data/lib/active_support/vendor/i18n-0.1.3/test/locale/en.yml +3 -0
  157. data/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb +567 -0
  158. data/lib/active_support/vendor/memcache-client-1.7.5/lib/memcache.rb +1133 -0
  159. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo.rb +33 -0
  160. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/data_timezone.rb +47 -0
  161. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/data_timezone_info.rb +228 -0
  162. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Algiers.rb +55 -0
  163. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Cairo.rb +219 -0
  164. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Casablanca.rb +42 -0
  165. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Harare.rb +18 -0
  166. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Johannesburg.rb +25 -0
  167. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Monrovia.rb +22 -0
  168. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Africa/Nairobi.rb +23 -0
  169. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Argentina/Buenos_Aires.rb +166 -0
  170. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Argentina/San_Juan.rb +86 -0
  171. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Bogota.rb +23 -0
  172. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Caracas.rb +23 -0
  173. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Chicago.rb +283 -0
  174. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Chihuahua.rb +136 -0
  175. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Denver.rb +204 -0
  176. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Godthab.rb +161 -0
  177. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Guatemala.rb +27 -0
  178. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Halifax.rb +274 -0
  179. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Indiana/Indianapolis.rb +149 -0
  180. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Juneau.rb +194 -0
  181. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/La_Paz.rb +22 -0
  182. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Lima.rb +35 -0
  183. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Los_Angeles.rb +232 -0
  184. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Mazatlan.rb +139 -0
  185. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Mexico_City.rb +144 -0
  186. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Monterrey.rb +131 -0
  187. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/New_York.rb +282 -0
  188. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Phoenix.rb +30 -0
  189. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Regina.rb +74 -0
  190. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Santiago.rb +205 -0
  191. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Sao_Paulo.rb +171 -0
  192. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/St_Johns.rb +288 -0
  193. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/America/Tijuana.rb +196 -0
  194. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Almaty.rb +67 -0
  195. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Baghdad.rb +73 -0
  196. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Baku.rb +161 -0
  197. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Bangkok.rb +20 -0
  198. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Chongqing.rb +33 -0
  199. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Colombo.rb +30 -0
  200. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Dhaka.rb +27 -0
  201. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Hong_Kong.rb +87 -0
  202. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Irkutsk.rb +165 -0
  203. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Jakarta.rb +30 -0
  204. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Jerusalem.rb +163 -0
  205. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kabul.rb +20 -0
  206. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kamchatka.rb +163 -0
  207. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Karachi.rb +32 -0
  208. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kathmandu.rb +20 -0
  209. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kolkata.rb +25 -0
  210. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Krasnoyarsk.rb +163 -0
  211. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kuala_Lumpur.rb +31 -0
  212. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Kuwait.rb +18 -0
  213. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Magadan.rb +163 -0
  214. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Muscat.rb +18 -0
  215. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Novosibirsk.rb +164 -0
  216. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Rangoon.rb +24 -0
  217. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Riyadh.rb +18 -0
  218. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Seoul.rb +34 -0
  219. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Shanghai.rb +35 -0
  220. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Singapore.rb +33 -0
  221. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Taipei.rb +59 -0
  222. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tashkent.rb +47 -0
  223. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tbilisi.rb +78 -0
  224. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tehran.rb +121 -0
  225. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Tokyo.rb +30 -0
  226. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Ulaanbaatar.rb +65 -0
  227. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Urumqi.rb +33 -0
  228. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Vladivostok.rb +164 -0
  229. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Yakutsk.rb +163 -0
  230. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Yekaterinburg.rb +165 -0
  231. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Asia/Yerevan.rb +165 -0
  232. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Atlantic/Azores.rb +270 -0
  233. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Atlantic/Cape_Verde.rb +23 -0
  234. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Atlantic/South_Georgia.rb +18 -0
  235. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Adelaide.rb +187 -0
  236. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Brisbane.rb +35 -0
  237. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Darwin.rb +29 -0
  238. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Hobart.rb +193 -0
  239. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Melbourne.rb +185 -0
  240. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Perth.rb +37 -0
  241. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Australia/Sydney.rb +185 -0
  242. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Etc/UTC.rb +16 -0
  243. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Amsterdam.rb +228 -0
  244. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Athens.rb +185 -0
  245. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Belgrade.rb +163 -0
  246. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Berlin.rb +188 -0
  247. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Bratislava.rb +13 -0
  248. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Brussels.rb +232 -0
  249. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Bucharest.rb +181 -0
  250. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Budapest.rb +197 -0
  251. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Copenhagen.rb +179 -0
  252. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Dublin.rb +276 -0
  253. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Helsinki.rb +163 -0
  254. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Istanbul.rb +218 -0
  255. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Kiev.rb +168 -0
  256. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Lisbon.rb +268 -0
  257. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Ljubljana.rb +13 -0
  258. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/London.rb +288 -0
  259. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Madrid.rb +211 -0
  260. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Minsk.rb +170 -0
  261. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Moscow.rb +181 -0
  262. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Paris.rb +232 -0
  263. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Prague.rb +187 -0
  264. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Riga.rb +176 -0
  265. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Rome.rb +215 -0
  266. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Sarajevo.rb +13 -0
  267. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Skopje.rb +13 -0
  268. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Sofia.rb +173 -0
  269. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Stockholm.rb +165 -0
  270. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Tallinn.rb +172 -0
  271. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Vienna.rb +183 -0
  272. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Vilnius.rb +170 -0
  273. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Warsaw.rb +212 -0
  274. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Europe/Zagreb.rb +13 -0
  275. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Auckland.rb +202 -0
  276. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Fiji.rb +23 -0
  277. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Guam.rb +22 -0
  278. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Honolulu.rb +28 -0
  279. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Majuro.rb +20 -0
  280. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Midway.rb +25 -0
  281. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Noumea.rb +25 -0
  282. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Pago_Pago.rb +26 -0
  283. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Port_Moresby.rb +20 -0
  284. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/definitions/Pacific/Tongatapu.rb +27 -0
  285. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/info_timezone.rb +52 -0
  286. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/linked_timezone.rb +51 -0
  287. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/linked_timezone_info.rb +44 -0
  288. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/offset_rationals.rb +98 -0
  289. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/ruby_core_support.rb +56 -0
  290. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/time_or_datetime.rb +292 -0
  291. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone.rb +508 -0
  292. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_definition.rb +56 -0
  293. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_info.rb +40 -0
  294. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_offset_info.rb +94 -0
  295. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_period.rb +198 -0
  296. data/lib/active_support/vendor/tzinfo-0.3.13/lib/tzinfo/timezone_transition_info.rb +129 -0
  297. data/lib/active_support/version.rb +2 -3
  298. data/lib/active_support/whiny_nil.rb +7 -9
  299. data/lib/active_support/xml_mini.rb +1 -126
  300. data/lib/active_support/xml_mini/jdom.rb +0 -2
  301. data/lib/active_support/xml_mini/libxml.rb +86 -24
  302. data/lib/active_support/xml_mini/nokogiri.rb +24 -27
  303. data/lib/active_support/xml_mini/rexml.rb +1 -7
  304. metadata +191 -49
  305. data/lib/active_support/builder.rb +0 -6
  306. data/lib/active_support/core_ext/array/uniq_by.rb +0 -17
  307. data/lib/active_support/core_ext/class/attribute.rb +0 -67
  308. data/lib/active_support/core_ext/class/subclasses.rb +0 -55
  309. data/lib/active_support/core_ext/file/path.rb +0 -5
  310. data/lib/active_support/core_ext/integer/multiple.rb +0 -6
  311. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -13
  312. data/lib/active_support/core_ext/module/anonymous.rb +0 -24
  313. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  314. data/lib/active_support/core_ext/module/reachable.rb +0 -10
  315. data/lib/active_support/core_ext/module/remove_method.rb +0 -6
  316. data/lib/active_support/core_ext/object/to_param.rb +0 -49
  317. data/lib/active_support/core_ext/object/to_query.rb +0 -27
  318. data/lib/active_support/core_ext/string/encoding.rb +0 -11
  319. data/lib/active_support/core_ext/string/exclude.rb +0 -6
  320. data/lib/active_support/core_ext/time/marshal.rb +0 -56
  321. data/lib/active_support/dependencies/autoload.rb +0 -50
  322. data/lib/active_support/i18n.rb +0 -8
  323. data/lib/active_support/inflector/inflections.rb +0 -211
  324. data/lib/active_support/inflector/methods.rb +0 -141
  325. data/lib/active_support/inflector/transliterate.rb +0 -97
  326. data/lib/active_support/json/backends/yajl.rb +0 -40
  327. data/lib/active_support/lazy_load_hooks.rb +0 -27
  328. data/lib/active_support/multibyte/unicode.rb +0 -393
  329. data/lib/active_support/notifications/fanout.rb +0 -93
  330. data/lib/active_support/notifications/instrumenter.rb +0 -56
  331. data/lib/active_support/railtie.rb +0 -100
  332. data/lib/active_support/xml_mini/libxmlsax.rb +0 -85
  333. data/lib/active_support/xml_mini/nokogirisax.rb +0 -83
@@ -23,18 +23,15 @@ module ActiveSupport
23
23
  private
24
24
  def convert_dates_from(data)
25
25
  case data
26
- when nil
27
- nil
28
- when DATE_REGEX
29
- DateTime.parse(data)
30
- when Array
31
- data.map! { |d| convert_dates_from(d) }
32
- when Hash
33
- data.each do |key, value|
34
- data[key] = convert_dates_from(value)
35
- end
36
- else
37
- data
26
+ when DATE_REGEX
27
+ DateTime.parse(data)
28
+ when Array
29
+ data.map! { |d| convert_dates_from(d) }
30
+ when Hash
31
+ data.each do |key, value|
32
+ data[key] = convert_dates_from(value)
33
+ end
34
+ else data
38
35
  end
39
36
  end
40
37
  end
@@ -6,15 +6,12 @@ module ActiveSupport
6
6
  mattr_accessor :parse_json_times
7
7
 
8
8
  module JSON
9
- # Listed in order of preference.
10
- DECODERS = %w(Yajl Yaml)
11
-
12
9
  class << self
13
10
  attr_reader :parse_error
14
11
  delegate :decode, :to => :backend
15
12
 
16
13
  def backend
17
- set_default_backend unless defined?(@backend)
14
+ self.backend = "Yaml" unless defined?(@backend)
18
15
  @backend
19
16
  end
20
17
 
@@ -34,18 +31,6 @@ module ActiveSupport
34
31
  ensure
35
32
  self.backend = old_backend
36
33
  end
37
-
38
- def set_default_backend
39
- DECODERS.find do |name|
40
- begin
41
- self.backend = name
42
- true
43
- rescue LoadError
44
- # Try next decoder.
45
- false
46
- end
47
- end
48
- end
49
34
  end
50
35
  end
51
36
  end
@@ -1,14 +1,15 @@
1
1
  # encoding: utf-8
2
- require 'bigdecimal'
3
2
  require 'active_support/core_ext/array/wrap'
4
- require 'active_support/core_ext/big_decimal/conversions' # for #to_s
5
3
  require 'active_support/core_ext/hash/except'
6
4
  require 'active_support/core_ext/hash/slice'
7
5
  require 'active_support/core_ext/module/delegation'
8
6
  require 'active_support/core_ext/object/instance_variables'
9
7
  require 'active_support/deprecation'
10
8
 
11
- require 'active_support/time'
9
+ require 'active_support/core_ext/date_time/conversions'
10
+ require 'active_support/core_ext/time/conversions'
11
+ require 'active_support/time_with_zone'
12
+ require 'active_support/values/time_zone'
12
13
 
13
14
  # Hack to load json gem first so we can overwrite its to_json.
14
15
  begin
@@ -67,15 +68,6 @@ module ActiveSupport
67
68
 
68
69
 
69
70
  ESCAPED_CHARS = {
70
- "\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002',
71
- "\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005',
72
- "\x06" => '\u0006', "\x07" => '\u0007', "\x0B" => '\u000B',
73
- "\x0E" => '\u000E', "\x0F" => '\u000F', "\x10" => '\u0010',
74
- "\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013',
75
- "\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016',
76
- "\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019',
77
- "\x1A" => '\u001A', "\x1B" => '\u001B', "\x1C" => '\u001C',
78
- "\x1D" => '\u001D', "\x1E" => '\u001E', "\x1F" => '\u001F',
79
71
  "\010" => '\b',
80
72
  "\f" => '\f',
81
73
  "\n" => '\n',
@@ -97,16 +89,14 @@ module ActiveSupport
97
89
  def escape_html_entities_in_json=(value)
98
90
  self.escape_regex = \
99
91
  if @escape_html_entities_in_json = value
100
- /[\x00-\x1F"\\><&]/
92
+ /[\010\f\n\r\t"\\><&]/
101
93
  else
102
- /[\x00-\x1F"\\]/
94
+ /[\010\f\n\r\t"\\]/
103
95
  end
104
96
  end
105
97
 
106
98
  def escape(string)
107
- if string.respond_to?(:force_encoding)
108
- string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
109
- end
99
+ string = string.dup.force_encoding(::Encoding::BINARY) if string.respond_to?(:force_encoding)
110
100
  json = string.
111
101
  gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
112
102
  gsub(/([\xC0-\xDF][\x80-\xBF]|
@@ -114,14 +104,11 @@ module ActiveSupport
114
104
  [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
115
105
  s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
116
106
  }
117
- json = %("#{json}")
118
- json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding)
119
- json
107
+ %("#{json}")
120
108
  end
121
109
  end
122
110
 
123
- self.use_standard_json_time_format = true
124
- self.escape_html_entities_in_json = false
111
+ self.escape_html_entities_in_json = true
125
112
  end
126
113
 
127
114
  CircularReferenceError = Deprecation::DeprecatedConstantProxy.new('ActiveSupport::JSON::CircularReferenceError', Encoding::CircularReferenceError)
@@ -134,13 +121,7 @@ class Object
134
121
  ActiveSupport::JSON.encode(self, options)
135
122
  end
136
123
 
137
- def as_json(options = nil) #:nodoc:
138
- if respond_to?(:to_hash)
139
- to_hash
140
- else
141
- instance_values
142
- end
143
- end
124
+ def as_json(options = nil) instance_values end #:nodoc:
144
125
  end
145
126
 
146
127
  # A string that returns itself as its JSON-encoded form.
@@ -178,20 +159,9 @@ class Numeric
178
159
  def encode_json(encoder) to_s end #:nodoc:
179
160
  end
180
161
 
181
- class BigDecimal
182
- # A BigDecimal would be naturally represented as a JSON number. Most libraries,
183
- # however, parse non-integer JSON numbers directly as floats. Clients using
184
- # those libraries would get in general a wrong number and no way to recover
185
- # other than manually inspecting the string with the JSON code itself.
186
- #
187
- # That's why a JSON string is returned. The JSON literal is not numeric, but if
188
- # the other end knows by contract that the data is supposed to be a BigDecimal,
189
- # it still has the chance to post-process the string and get the real value.
190
- def as_json(options = nil) to_s end #:nodoc:
191
- end
192
-
193
162
  class Regexp
194
- def as_json(options = nil) to_s end #:nodoc:
163
+ def as_json(options = nil) self end #:nodoc:
164
+ def encode_json(encoder) inspect end #:nodoc:
195
165
  end
196
166
 
197
167
  module Enumerable
@@ -15,10 +15,7 @@ en:
15
15
  month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
16
16
  abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
17
17
  # Used in date_select and datime_select.
18
- order:
19
- - :year
20
- - :month
21
- - :day
18
+ order: [ :year, :month, :day ]
22
19
 
23
20
  time:
24
21
  formats:
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/kernel/singleton_class'
1
+ require 'active_support/core_ext/object/metaclass'
2
2
  require 'active_support/core_ext/module/aliasing'
3
3
 
4
4
  module ActiveSupport
@@ -1,5 +1,4 @@
1
1
  require 'openssl'
2
- require 'active_support/base64'
3
2
 
4
3
  module ActiveSupport
5
4
  # MessageEncryptor is a simple way to encrypt values which get stored somewhere
@@ -1,5 +1,4 @@
1
- require 'active_support/base64'
2
- require 'active_support/core_ext/object/blank'
1
+ require 'active_support/core_ext/string/bytesize'
3
2
 
4
3
  module ActiveSupport
5
4
  # MessageVerifier makes it easy to generate and verify messages which are signed
@@ -47,11 +46,11 @@ module ActiveSupport
47
46
  def secure_compare(a, b)
48
47
  return false unless a.bytesize == b.bytesize
49
48
 
50
- l = a.unpack "C*"
49
+ l = a.unpack "C#{a.bytesize}"
51
50
 
52
- res = true
53
- b.each_byte { |byte| res = (byte == l.shift) && res }
54
- res
51
+ res = 0
52
+ b.each_byte { |byte| res |= byte ^ l.shift }
53
+ res == 0
55
54
  end
56
55
 
57
56
  def generate_digest(data)
@@ -1,12 +1,24 @@
1
1
  # encoding: utf-8
2
+
2
3
  require 'active_support/core_ext/module/attribute_accessors'
3
4
 
4
5
  module ActiveSupport #:nodoc:
5
6
  module Multibyte
6
- autoload :EncodingError, 'active_support/multibyte/exceptions'
7
- autoload :Chars, 'active_support/multibyte/chars'
8
- autoload :Unicode, 'active_support/multibyte/unicode'
9
-
7
+ # A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more
8
+ # information about normalization.
9
+ NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
10
+
11
+ # The Unicode version that is supported by the implementation
12
+ UNICODE_VERSION = '5.1.0'
13
+
14
+ # The default normalization used for operations that require normalization. It can be set to any of the
15
+ # normalizations in NORMALIZATION_FORMS.
16
+ #
17
+ # Example:
18
+ # ActiveSupport::Multibyte.default_normalization_form = :c
19
+ mattr_accessor :default_normalization_form
20
+ self.default_normalization_form = :kc
21
+
10
22
  # The proxy class returned when calling mb_chars. You can use this accessor to configure your own proxy
11
23
  # class so you can support other encodings. See the ActiveSupport::Multibyte::Chars implementation for
12
24
  # an example how to do this.
@@ -35,10 +47,13 @@ module ActiveSupport #:nodoc:
35
47
  \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf])\z /xn,
36
48
  # Quick check for valid Shift-JIS characters, disregards the odd-even pairing
37
49
  'Shift_JIS' => /\A(?:
38
- [\x00-\x7e\xa1-\xdf] |
39
- [\x81-\x9f\xe0-\xef] [\x40-\x7e\x80-\x9e\x9f-\xfc])\z /xn
50
+ [\x00-\x7e \xa1-\xdf] |
51
+ [\x81-\x9f \xe0-\xef] [\x40-\x7e \x80-\x9e \x9f-\xfc])\z /xn
40
52
  }
41
53
  end
42
54
  end
43
55
 
44
- require 'active_support/multibyte/utils'
56
+ require 'active_support/multibyte/chars'
57
+ require 'active_support/multibyte/exceptions'
58
+ require 'active_support/multibyte/unicode_database'
59
+ require 'active_support/multibyte/utils'
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
- require 'active_support/core_ext/string/access'
3
2
  require 'active_support/core_ext/string/behavior'
4
3
 
5
4
  module ActiveSupport #:nodoc:
@@ -19,7 +18,7 @@ module ActiveSupport #:nodoc:
19
18
  # bad.explicit_checking_method "T".mb_chars.downcase.to_s
20
19
  #
21
20
  # The default Chars implementation assumes that the encoding of the string is UTF-8, if you want to handle different
22
- # encodings you can write your own multibyte string handler and configure it through
21
+ # encodings you can write your own multibyte string handler and configure it through
23
22
  # ActiveSupport::Multibyte.proxy_class.
24
23
  #
25
24
  # class CharsForUTF32
@@ -34,19 +33,59 @@ module ActiveSupport #:nodoc:
34
33
  #
35
34
  # ActiveSupport::Multibyte.proxy_class = CharsForUTF32
36
35
  class Chars
36
+ # Hangul character boundaries and properties
37
+ HANGUL_SBASE = 0xAC00
38
+ HANGUL_LBASE = 0x1100
39
+ HANGUL_VBASE = 0x1161
40
+ HANGUL_TBASE = 0x11A7
41
+ HANGUL_LCOUNT = 19
42
+ HANGUL_VCOUNT = 21
43
+ HANGUL_TCOUNT = 28
44
+ HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
45
+ HANGUL_SCOUNT = 11172
46
+ HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
47
+ HANGUL_JAMO_FIRST = 0x1100
48
+ HANGUL_JAMO_LAST = 0x11FF
49
+
50
+ # All the unicode whitespace
51
+ UNICODE_WHITESPACE = [
52
+ (0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
53
+ 0x0020, # White_Space # Zs SPACE
54
+ 0x0085, # White_Space # Cc <control-0085>
55
+ 0x00A0, # White_Space # Zs NO-BREAK SPACE
56
+ 0x1680, # White_Space # Zs OGHAM SPACE MARK
57
+ 0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
58
+ (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
59
+ 0x2028, # White_Space # Zl LINE SEPARATOR
60
+ 0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
61
+ 0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
62
+ 0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
63
+ 0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
64
+ ].flatten.freeze
65
+
66
+ # BOM (byte order mark) can also be seen as whitespace, it's a non-rendering character used to distinguish
67
+ # between little and big endian. This is not an issue in utf-8, so it must be ignored.
68
+ UNICODE_LEADERS_AND_TRAILERS = UNICODE_WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM
69
+
70
+ # Returns a regular expression pattern that matches the passed Unicode codepoints
71
+ def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
72
+ array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
73
+ end
74
+ UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/
75
+ UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/
76
+
77
+ UTF8_PAT = ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8']
37
78
 
38
79
  attr_reader :wrapped_string
39
80
  alias to_s wrapped_string
40
81
  alias to_str wrapped_string
41
82
 
42
- if RUBY_VERSION >= "1.9"
83
+ if '1.9'.respond_to?(:force_encoding)
43
84
  # Creates a new Chars instance by wrapping _string_.
44
85
  def initialize(string)
45
86
  @wrapped_string = string
46
87
  @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
47
88
  end
48
-
49
- undef <=>
50
89
  else
51
90
  def initialize(string) #:nodoc:
52
91
  @wrapped_string = string
@@ -75,6 +114,12 @@ module ActiveSupport #:nodoc:
75
114
  true
76
115
  end
77
116
 
117
+ # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
118
+ # +false+ otherwise.
119
+ def self.wants?(string)
120
+ $KCODE == 'UTF8' && consumes?(string)
121
+ end
122
+
78
123
  # Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
79
124
  def self.consumes?(string)
80
125
  # Unpack is a little bit faster than regular expressions.
@@ -86,122 +131,30 @@ module ActiveSupport #:nodoc:
86
131
 
87
132
  include Comparable
88
133
 
89
- if RUBY_VERSION < "1.9"
90
- # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
91
- # +false+ otherwise.
92
- def self.wants?(string)
93
- $KCODE == 'UTF8' && consumes?(string)
94
- end
95
-
96
- # Returns <tt>-1</tt>, <tt>0</tt> or <tt>+1</tt> depending on whether the Chars object is to be sorted before,
97
- # equal or after the object on the right side of the operation. It accepts any object that implements +to_s+.
98
- # See <tt>String#<=></tt> for more details.
99
- #
100
- # Example:
101
- # 'é'.mb_chars <=> 'ü'.mb_chars #=> -1
102
- def <=>(other)
103
- @wrapped_string <=> other.to_s
104
- end
105
-
106
- # Returns a new Chars object containing the _other_ object concatenated to the string.
107
- #
108
- # Example:
109
- # ('Café'.mb_chars + ' périferôl').to_s #=> "Café périferôl"
110
- def +(other)
111
- self << other
112
- end
113
-
114
- # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
115
- #
116
- # Example:
117
- # 'Café périferôl'.mb_chars =~ /ô/ #=> 12
118
- def =~(other)
119
- translate_offset(@wrapped_string =~ other)
120
- end
121
-
122
- # Inserts the passed string at specified codepoint offsets.
123
- #
124
- # Example:
125
- # 'Café'.mb_chars.insert(4, ' périferôl').to_s #=> "Café périferôl"
126
- def insert(offset, fragment)
127
- unpacked = Unicode.u_unpack(@wrapped_string)
128
- unless offset > unpacked.length
129
- @wrapped_string.replace(
130
- Unicode.u_unpack(@wrapped_string).insert(offset, *Unicode.u_unpack(fragment)).pack('U*')
131
- )
132
- else
133
- raise IndexError, "index #{offset} out of string"
134
- end
135
- self
136
- end
137
-
138
- # Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
139
- #
140
- # Example:
141
- # 'Café'.mb_chars.include?('é') #=> true
142
- def include?(other)
143
- # We have to redefine this method because Enumerable defines it.
144
- @wrapped_string.include?(other)
145
- end
146
-
147
- # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
148
- #
149
- # Example:
150
- # 'Café périferôl'.mb_chars.index('ô') #=> 12
151
- # 'Café périferôl'.mb_chars.index(/\w/u) #=> 0
152
- def index(needle, offset=0)
153
- wrapped_offset = first(offset).wrapped_string.length
154
- index = @wrapped_string.index(needle, wrapped_offset)
155
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
156
- end
157
-
158
- # Returns the position _needle_ in the string, counting in
159
- # codepoints, searching backward from _offset_ or the end of the
160
- # string. Returns +nil+ if _needle_ isn't found.
161
- #
162
- # Example:
163
- # 'Café périferôl'.mb_chars.rindex('é') #=> 6
164
- # 'Café périferôl'.mb_chars.rindex(/\w/u) #=> 13
165
- def rindex(needle, offset=nil)
166
- offset ||= length
167
- wrapped_offset = first(offset).wrapped_string.length
168
- index = @wrapped_string.rindex(needle, wrapped_offset)
169
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
170
- end
171
-
172
- # Returns the number of codepoints in the string
173
- def size
174
- Unicode.u_unpack(@wrapped_string).size
175
- end
176
- alias_method :length, :size
177
-
178
- # Strips entire range of Unicode whitespace from the right of the string.
179
- def rstrip
180
- chars(@wrapped_string.gsub(Unicode::TRAILERS_PAT, ''))
181
- end
182
-
183
- # Strips entire range of Unicode whitespace from the left of the string.
184
- def lstrip
185
- chars(@wrapped_string.gsub(Unicode::LEADERS_PAT, ''))
186
- end
187
-
188
- # Strips entire range of Unicode whitespace from the right and left of the string.
189
- def strip
190
- rstrip.lstrip
191
- end
134
+ # Returns <tt>-1</tt>, <tt>0</tt> or <tt>+1</tt> depending on whether the Chars object is to be sorted before,
135
+ # equal or after the object on the right side of the operation. It accepts any object that implements +to_s+.
136
+ # See <tt>String#<=></tt> for more details.
137
+ #
138
+ # Example:
139
+ # 'é'.mb_chars <=> 'ü'.mb_chars #=> -1
140
+ def <=>(other)
141
+ @wrapped_string <=> other.to_s
142
+ end
192
143
 
193
- # Returns the codepoint of the first character in the string.
194
- #
195
- # Example:
196
- # 'こんにちは'.mb_chars.ord #=> 12371
197
- def ord
198
- Unicode.u_unpack(@wrapped_string)[0]
199
- end
144
+ # Returns a new Chars object containing the _other_ object concatenated to the string.
145
+ #
146
+ # Example:
147
+ # ('Café'.mb_chars + ' périferôl').to_s #=> "Café périferôl"
148
+ def +(other)
149
+ self << other
150
+ end
200
151
 
201
- else
202
- def =~(other)
203
- @wrapped_string =~ other
204
- end
152
+ # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
153
+ #
154
+ # Example:
155
+ # 'Café périferôl'.mb_chars =~ /ô/ #=> 12
156
+ def =~(other)
157
+ translate_offset(@wrapped_string =~ other)
205
158
  end
206
159
 
207
160
  # Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
@@ -213,6 +166,56 @@ module ActiveSupport #:nodoc:
213
166
  @wrapped_string.split(*args).map { |i| i.mb_chars }
214
167
  end
215
168
 
169
+ # Inserts the passed string at specified codepoint offsets.
170
+ #
171
+ # Example:
172
+ # 'Café'.mb_chars.insert(4, ' périferôl').to_s #=> "Café périferôl"
173
+ def insert(offset, fragment)
174
+ unpacked = self.class.u_unpack(@wrapped_string)
175
+ unless offset > unpacked.length
176
+ @wrapped_string.replace(
177
+ self.class.u_unpack(@wrapped_string).insert(offset, *self.class.u_unpack(fragment)).pack('U*')
178
+ )
179
+ else
180
+ raise IndexError, "index #{offset} out of string"
181
+ end
182
+ self
183
+ end
184
+
185
+ # Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
186
+ #
187
+ # Example:
188
+ # 'Café'.mb_chars.include?('é') #=> true
189
+ def include?(other)
190
+ # We have to redefine this method because Enumerable defines it.
191
+ @wrapped_string.include?(other)
192
+ end
193
+
194
+ # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
195
+ #
196
+ # Example:
197
+ # 'Café périferôl'.mb_chars.index('ô') #=> 12
198
+ # 'Café périferôl'.mb_chars.index(/\w/u) #=> 0
199
+ def index(needle, offset=0)
200
+ wrapped_offset = self.first(offset).wrapped_string.length
201
+ index = @wrapped_string.index(needle, wrapped_offset)
202
+ index ? (self.class.u_unpack(@wrapped_string.slice(0...index)).size) : nil
203
+ end
204
+
205
+ # Returns the position _needle_ in the string, counting in
206
+ # codepoints, searching backward from _offset_ or the end of the
207
+ # string. Returns +nil+ if _needle_ isn't found.
208
+ #
209
+ # Example:
210
+ # 'Café périferôl'.mb_chars.rindex('é') #=> 6
211
+ # 'Café périferôl'.mb_chars.rindex(/\w/u) #=> 13
212
+ def rindex(needle, offset=nil)
213
+ offset ||= length
214
+ wrapped_offset = self.first(offset).wrapped_string.length
215
+ index = @wrapped_string.rindex(needle, wrapped_offset)
216
+ index ? (self.class.u_unpack(@wrapped_string.slice(0...index)).size) : nil
217
+ end
218
+
216
219
  # Like <tt>String#[]=</tt>, except instead of byte offsets you specify character offsets.
217
220
  #
218
221
  # Example:
@@ -232,7 +235,7 @@ module ActiveSupport #:nodoc:
232
235
  if args.first.is_a?(Regexp)
233
236
  @wrapped_string[*args] = replace_by
234
237
  else
235
- result = Unicode.u_unpack(@wrapped_string)
238
+ result = self.class.u_unpack(@wrapped_string)
236
239
  if args[0].is_a?(Fixnum)
237
240
  raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
238
241
  min = args[0]
@@ -245,10 +248,10 @@ module ActiveSupport #:nodoc:
245
248
  else
246
249
  needle = args[0].to_s
247
250
  min = index(needle)
248
- max = min + Unicode.u_unpack(needle).length - 1
251
+ max = min + self.class.u_unpack(needle).length - 1
249
252
  range = Range.new(min, max)
250
253
  end
251
- result[range] = Unicode.u_unpack(replace_by)
254
+ result[range] = self.class.u_unpack(replace_by)
252
255
  @wrapped_string.replace(result.pack('U*'))
253
256
  end
254
257
  end
@@ -292,13 +295,33 @@ module ActiveSupport #:nodoc:
292
295
  justify(integer, :center, padstr)
293
296
  end
294
297
 
298
+ # Strips entire range of Unicode whitespace from the right of the string.
299
+ def rstrip
300
+ chars(@wrapped_string.gsub(UNICODE_TRAILERS_PAT, ''))
301
+ end
302
+
303
+ # Strips entire range of Unicode whitespace from the left of the string.
304
+ def lstrip
305
+ chars(@wrapped_string.gsub(UNICODE_LEADERS_PAT, ''))
306
+ end
307
+
308
+ # Strips entire range of Unicode whitespace from the right and left of the string.
309
+ def strip
310
+ rstrip.lstrip
311
+ end
312
+
313
+ # Returns the number of codepoints in the string
314
+ def size
315
+ self.class.u_unpack(@wrapped_string).size
316
+ end
317
+ alias_method :length, :size
295
318
 
296
319
  # Reverses all characters in the string.
297
320
  #
298
321
  # Example:
299
322
  # 'Café'.mb_chars.reverse.to_s #=> 'éfaC'
300
323
  def reverse
301
- chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*'))
324
+ chars(self.class.u_unpack(@wrapped_string).reverse.pack('U*'))
302
325
  end
303
326
 
304
327
  # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
@@ -314,15 +337,15 @@ module ActiveSupport #:nodoc:
314
337
  elsif (args.size == 2 && !args[1].is_a?(Numeric))
315
338
  raise TypeError, "cannot convert #{args[1].class} into Integer" # Do as if we were native
316
339
  elsif args[0].kind_of? Range
317
- cps = Unicode.u_unpack(@wrapped_string).slice(*args)
340
+ cps = self.class.u_unpack(@wrapped_string).slice(*args)
318
341
  result = cps.nil? ? nil : cps.pack('U*')
319
342
  elsif args[0].kind_of? Regexp
320
343
  result = @wrapped_string.slice(*args)
321
344
  elsif args.size == 1 && args[0].kind_of?(Numeric)
322
- character = Unicode.u_unpack(@wrapped_string)[args[0]]
345
+ character = self.class.u_unpack(@wrapped_string)[args[0]]
323
346
  result = character.nil? ? nil : [character].pack('U')
324
347
  else
325
- result = Unicode.u_unpack(@wrapped_string).slice(*args).pack('U*')
348
+ result = self.class.u_unpack(@wrapped_string).slice(*args).pack('U*')
326
349
  end
327
350
  result.nil? ? nil : chars(result)
328
351
  end
@@ -340,22 +363,20 @@ module ActiveSupport #:nodoc:
340
363
  slice
341
364
  end
342
365
 
343
- # Limit the byte size of the string to a number of bytes without breaking characters. Usable
344
- # when the storage for a string is limited for some reason.
366
+ # Returns the codepoint of the first character in the string.
345
367
  #
346
368
  # Example:
347
- # s = 'こんにちは'
348
- # s.mb_chars.limit(7) #=> "こに"
349
- def limit(limit)
350
- slice(0...translate_offset(limit))
369
+ # 'こんにちは'.mb_chars.ord #=> 12371
370
+ def ord
371
+ self.class.u_unpack(@wrapped_string)[0]
351
372
  end
352
373
 
353
374
  # Convert characters in the string to uppercase.
354
375
  #
355
376
  # Example:
356
- # 'Laurent, sont les tests ?'.mb_chars.upcase.to_s #=> "LAURENT, SONT LES TESTS ?"
377
+ # 'Laurent, òu sont les tests?'.mb_chars.upcase.to_s #=> "LAURENT, ÒU SONT LES TESTS?"
357
378
  def upcase
358
- chars(Unicode.apply_mapping @wrapped_string, :uppercase_mapping)
379
+ apply_mapping :uppercase_mapping
359
380
  end
360
381
 
361
382
  # Convert characters in the string to lowercase.
@@ -363,7 +384,7 @@ module ActiveSupport #:nodoc:
363
384
  # Example:
364
385
  # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s #=> "věda a výzkum"
365
386
  def downcase
366
- chars(Unicode.apply_mapping @wrapped_string, :lowercase_mapping)
387
+ apply_mapping :lowercase_mapping
367
388
  end
368
389
 
369
390
  # Converts the first character to uppercase and the remainder to lowercase.
@@ -377,11 +398,25 @@ module ActiveSupport #:nodoc:
377
398
  # Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
378
399
  # passing strings to databases and validations.
379
400
  #
401
+ # * <tt>str</tt> - The string to perform normalization on.
380
402
  # * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
381
403
  # <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
382
- # ActiveSupport::Multibyte::Unicode.default_normalization_form
383
- def normalize(form = nil)
384
- chars(Unicode.normalize(@wrapped_string, form))
404
+ # ActiveSupport::Multibyte.default_normalization_form
405
+ def normalize(form=ActiveSupport::Multibyte.default_normalization_form)
406
+ # See http://www.unicode.org/reports/tr15, Table 1
407
+ codepoints = self.class.u_unpack(@wrapped_string)
408
+ chars(case form
409
+ when :d
410
+ self.class.reorder_characters(self.class.decompose_codepoints(:canonical, codepoints))
411
+ when :c
412
+ self.class.compose_codepoints(self.class.reorder_characters(self.class.decompose_codepoints(:canonical, codepoints)))
413
+ when :kd
414
+ self.class.reorder_characters(self.class.decompose_codepoints(:compatability, codepoints))
415
+ when :kc
416
+ self.class.compose_codepoints(self.class.reorder_characters(self.class.decompose_codepoints(:compatability, codepoints)))
417
+ else
418
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
419
+ end.pack('U*'))
385
420
  end
386
421
 
387
422
  # Performs canonical decomposition on all the characters.
@@ -390,7 +425,7 @@ module ActiveSupport #:nodoc:
390
425
  # 'é'.length #=> 2
391
426
  # 'é'.mb_chars.decompose.to_s.length #=> 3
392
427
  def decompose
393
- chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*'))
428
+ chars(self.class.decompose_codepoints(:canonical, self.class.u_unpack(@wrapped_string)).pack('U*'))
394
429
  end
395
430
 
396
431
  # Performs composition on all the characters.
@@ -399,7 +434,7 @@ module ActiveSupport #:nodoc:
399
434
  # 'é'.length #=> 3
400
435
  # 'é'.mb_chars.compose.to_s.length #=> 2
401
436
  def compose
402
- chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*'))
437
+ chars(self.class.compose_codepoints(self.class.u_unpack(@wrapped_string)).pack('U*'))
403
438
  end
404
439
 
405
440
  # Returns the number of grapheme clusters in the string.
@@ -408,14 +443,12 @@ module ActiveSupport #:nodoc:
408
443
  # 'क्षि'.mb_chars.length #=> 4
409
444
  # 'क्षि'.mb_chars.g_length #=> 3
410
445
  def g_length
411
- Unicode.g_unpack(@wrapped_string).length
446
+ self.class.g_unpack(@wrapped_string).length
412
447
  end
413
448
 
414
449
  # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
415
- #
416
- # Passing +true+ will forcibly tidy all bytes, assuming that the string's encoding is entirely CP1252 or ISO-8859-1.
417
- def tidy_bytes(force = false)
418
- chars(Unicode.tidy_bytes(@wrapped_string, force))
450
+ def tidy_bytes
451
+ chars(self.class.tidy_bytes(@wrapped_string))
419
452
  end
420
453
 
421
454
  %w(lstrip rstrip strip reverse upcase downcase tidy_bytes capitalize).each do |method|
@@ -429,21 +462,213 @@ module ActiveSupport #:nodoc:
429
462
  end
430
463
  end
431
464
 
465
+ class << self
466
+
467
+ # Unpack the string at codepoints boundaries. Raises an EncodingError when the encoding of the string isn't
468
+ # valid UTF-8.
469
+ #
470
+ # Example:
471
+ # Chars.u_unpack('Café') #=> [67, 97, 102, 233]
472
+ def u_unpack(string)
473
+ begin
474
+ string.unpack 'U*'
475
+ rescue ArgumentError
476
+ raise EncodingError, 'malformed UTF-8 character'
477
+ end
478
+ end
479
+
480
+ # Detect whether the codepoint is in a certain character class. Returns +true+ when it's in the specified
481
+ # character class and +false+ otherwise. Valid character classes are: <tt>:cr</tt>, <tt>:lf</tt>, <tt>:l</tt>,
482
+ # <tt>:v</tt>, <tt>:lv</tt>, <tt>:lvt</tt> and <tt>:t</tt>.
483
+ #
484
+ # Primarily used by the grapheme cluster support.
485
+ def in_char_class?(codepoint, classes)
486
+ classes.detect { |c| UCD.boundary[c] === codepoint } ? true : false
487
+ end
488
+
489
+ # Unpack the string at grapheme boundaries. Returns a list of character lists.
490
+ #
491
+ # Example:
492
+ # Chars.g_unpack('क्षि') #=> [[2325, 2381], [2359], [2367]]
493
+ # Chars.g_unpack('Café') #=> [[67], [97], [102], [233]]
494
+ def g_unpack(string)
495
+ codepoints = u_unpack(string)
496
+ unpacked = []
497
+ pos = 0
498
+ marker = 0
499
+ eoc = codepoints.length
500
+ while(pos < eoc)
501
+ pos += 1
502
+ previous = codepoints[pos-1]
503
+ current = codepoints[pos]
504
+ if (
505
+ # CR X LF
506
+ one = ( previous == UCD.boundary[:cr] and current == UCD.boundary[:lf] ) or
507
+ # L X (L|V|LV|LVT)
508
+ two = ( UCD.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or
509
+ # (LV|V) X (V|T)
510
+ three = ( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or
511
+ # (LVT|T) X (T)
512
+ four = ( in_char_class?(previous, [:lvt,:t]) and UCD.boundary[:t] === current ) or
513
+ # X Extend
514
+ five = (UCD.boundary[:extend] === current)
515
+ )
516
+ else
517
+ unpacked << codepoints[marker..pos-1]
518
+ marker = pos
519
+ end
520
+ end
521
+ unpacked
522
+ end
523
+
524
+ # Reverse operation of g_unpack.
525
+ #
526
+ # Example:
527
+ # Chars.g_pack(Chars.g_unpack('क्षि')) #=> 'क्षि'
528
+ def g_pack(unpacked)
529
+ (unpacked.flatten).pack('U*')
530
+ end
531
+
532
+ def padding(padsize, padstr=' ') #:nodoc:
533
+ if padsize != 0
534
+ new(padstr * ((padsize / u_unpack(padstr).size) + 1)).slice(0, padsize)
535
+ else
536
+ ''
537
+ end
538
+ end
539
+
540
+ # Re-order codepoints so the string becomes canonical.
541
+ def reorder_characters(codepoints)
542
+ length = codepoints.length- 1
543
+ pos = 0
544
+ while pos < length do
545
+ cp1, cp2 = UCD.codepoints[codepoints[pos]], UCD.codepoints[codepoints[pos+1]]
546
+ if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
547
+ codepoints[pos..pos+1] = cp2.code, cp1.code
548
+ pos += (pos > 0 ? -1 : 1)
549
+ else
550
+ pos += 1
551
+ end
552
+ end
553
+ codepoints
554
+ end
555
+
556
+ # Decompose composed characters to the decomposed form.
557
+ def decompose_codepoints(type, codepoints)
558
+ codepoints.inject([]) do |decomposed, cp|
559
+ # if it's a hangul syllable starter character
560
+ if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
561
+ sindex = cp - HANGUL_SBASE
562
+ ncp = [] # new codepoints
563
+ ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
564
+ ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
565
+ tindex = sindex % HANGUL_TCOUNT
566
+ ncp << (HANGUL_TBASE + tindex) unless tindex == 0
567
+ decomposed.concat ncp
568
+ # if the codepoint is decomposable in with the current decomposition type
569
+ elsif (ncp = UCD.codepoints[cp].decomp_mapping) and (!UCD.codepoints[cp].decomp_type || type == :compatability)
570
+ decomposed.concat decompose_codepoints(type, ncp.dup)
571
+ else
572
+ decomposed << cp
573
+ end
574
+ end
575
+ end
576
+
577
+ # Compose decomposed characters to the composed form.
578
+ def compose_codepoints(codepoints)
579
+ pos = 0
580
+ eoa = codepoints.length - 1
581
+ starter_pos = 0
582
+ starter_char = codepoints[0]
583
+ previous_combining_class = -1
584
+ while pos < eoa
585
+ pos += 1
586
+ lindex = starter_char - HANGUL_LBASE
587
+ # -- Hangul
588
+ if 0 <= lindex and lindex < HANGUL_LCOUNT
589
+ vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
590
+ if 0 <= vindex and vindex < HANGUL_VCOUNT
591
+ tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
592
+ if 0 <= tindex and tindex < HANGUL_TCOUNT
593
+ j = starter_pos + 2
594
+ eoa -= 2
595
+ else
596
+ tindex = 0
597
+ j = starter_pos + 1
598
+ eoa -= 1
599
+ end
600
+ codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
601
+ end
602
+ starter_pos += 1
603
+ starter_char = codepoints[starter_pos]
604
+ # -- Other characters
605
+ else
606
+ current_char = codepoints[pos]
607
+ current = UCD.codepoints[current_char]
608
+ if current.combining_class > previous_combining_class
609
+ if ref = UCD.composition_map[starter_char]
610
+ composition = ref[current_char]
611
+ else
612
+ composition = nil
613
+ end
614
+ unless composition.nil?
615
+ codepoints[starter_pos] = composition
616
+ starter_char = composition
617
+ codepoints.delete_at pos
618
+ eoa -= 1
619
+ pos -= 1
620
+ previous_combining_class = -1
621
+ else
622
+ previous_combining_class = current.combining_class
623
+ end
624
+ else
625
+ previous_combining_class = current.combining_class
626
+ end
627
+ if current.combining_class == 0
628
+ starter_pos = pos
629
+ starter_char = codepoints[pos]
630
+ end
631
+ end
632
+ end
633
+ codepoints
634
+ end
635
+
636
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
637
+ def tidy_bytes(string)
638
+ string.split(//u).map do |c|
639
+ c.force_encoding(Encoding::ASCII) if c.respond_to?(:force_encoding)
640
+
641
+ if !ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'].match(c)
642
+ n = c.unpack('C')[0]
643
+ n < 128 ? n.chr :
644
+ n < 160 ? [UCD.cp1252[n] || n].pack('U') :
645
+ n < 192 ? "\xC2" + n.chr : "\xC3" + (n-64).chr
646
+ else
647
+ c
648
+ end
649
+ end.join
650
+ end
651
+ end
652
+
432
653
  protected
433
654
 
434
655
  def translate_offset(byte_offset) #:nodoc:
435
656
  return nil if byte_offset.nil?
436
657
  return 0 if @wrapped_string == ''
437
-
438
- if @wrapped_string.respond_to?(:force_encoding)
439
- @wrapped_string = @wrapped_string.dup.force_encoding(Encoding::ASCII_8BIT)
440
- end
441
-
658
+ chunk = @wrapped_string[0..byte_offset]
442
659
  begin
443
- @wrapped_string[0...byte_offset].unpack('U*').length
444
- rescue ArgumentError => e
445
- byte_offset -= 1
446
- retry
660
+ begin
661
+ chunk.unpack('U*').length - 1
662
+ rescue ArgumentError => e
663
+ chunk = @wrapped_string[0..(byte_offset+=1)]
664
+ # Stop retrying at the end of the string
665
+ raise e unless byte_offset < chunk.length
666
+ # We damaged a character, retry
667
+ retry
668
+ end
669
+ # Catch the ArgumentError so we can throw our own
670
+ rescue ArgumentError
671
+ raise EncodingError, 'malformed UTF-8 character'
447
672
  end
448
673
  end
449
674
 
@@ -453,23 +678,26 @@ module ActiveSupport #:nodoc:
453
678
  padsize = padsize > 0 ? padsize : 0
454
679
  case way
455
680
  when :right
456
- result = @wrapped_string.dup.insert(0, padding(padsize, padstr))
681
+ result = @wrapped_string.dup.insert(0, self.class.padding(padsize, padstr))
457
682
  when :left
458
- result = @wrapped_string.dup.insert(-1, padding(padsize, padstr))
683
+ result = @wrapped_string.dup.insert(-1, self.class.padding(padsize, padstr))
459
684
  when :center
460
- lpad = padding((padsize / 2.0).floor, padstr)
461
- rpad = padding((padsize / 2.0).ceil, padstr)
685
+ lpad = self.class.padding((padsize / 2.0).floor, padstr)
686
+ rpad = self.class.padding((padsize / 2.0).ceil, padstr)
462
687
  result = @wrapped_string.dup.insert(0, lpad).insert(-1, rpad)
463
688
  end
464
689
  chars(result)
465
690
  end
466
691
 
467
- def padding(padsize, padstr=' ') #:nodoc:
468
- if padsize != 0
469
- chars(padstr * ((padsize / Unicode.u_unpack(padstr).size) + 1)).slice(0, padsize)
470
- else
471
- ''
472
- end
692
+ def apply_mapping(mapping) #:nodoc:
693
+ chars(self.class.u_unpack(@wrapped_string).map do |codepoint|
694
+ cp = UCD.codepoints[codepoint]
695
+ if cp and (ncp = cp.send(mapping)) and ncp > 0
696
+ ncp
697
+ else
698
+ codepoint
699
+ end
700
+ end.pack('U*'))
473
701
  end
474
702
 
475
703
  def chars(string) #:nodoc: