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.
- data/CHANGELOG +64 -5
- data/lib/active_support.rb +6 -6
- data/lib/active_support/base64.rb +13 -2
- data/lib/active_support/basic_object.rb +1 -1
- data/lib/active_support/buffered_logger.rb +15 -14
- data/lib/active_support/cache.rb +116 -40
- data/lib/active_support/cache/compressed_mem_cache_store.rb +7 -2
- data/lib/active_support/cache/file_store.rb +7 -5
- data/lib/active_support/cache/mem_cache_store.rb +48 -21
- data/lib/active_support/cache/memory_store.rb +16 -2
- data/lib/active_support/cache/synchronized_memory_store.rb +47 -0
- data/lib/active_support/callbacks.rb +14 -9
- data/lib/active_support/core_ext/array/access.rb +27 -0
- data/lib/active_support/core_ext/array/conversions.rb +12 -7
- data/lib/active_support/core_ext/array/grouping.rb +53 -10
- data/lib/active_support/core_ext/base64/encoding.rb +3 -0
- data/lib/active_support/core_ext/bigdecimal/conversions.rb +18 -22
- data/lib/active_support/core_ext/blank.rb +5 -0
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +5 -5
- data/lib/active_support/core_ext/date/behavior.rb +3 -0
- data/lib/active_support/core_ext/date/calculations.rb +24 -9
- data/lib/active_support/core_ext/date_time/calculations.rb +19 -5
- data/lib/active_support/core_ext/duplicable.rb +6 -0
- data/lib/active_support/core_ext/enumerable.rb +42 -4
- data/lib/active_support/core_ext/file.rb +4 -20
- data/lib/active_support/core_ext/file/atomic.rb +46 -0
- data/lib/active_support/core_ext/float.rb +2 -0
- data/lib/active_support/core_ext/float/time.rb +27 -0
- data/lib/active_support/core_ext/hash.rb +2 -1
- data/lib/active_support/core_ext/hash/conversions.rb +1 -1
- data/lib/active_support/core_ext/hash/deep_merge.rb +23 -0
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/reverse_merge.rb +12 -5
- data/lib/active_support/core_ext/hash/slice.rb +7 -4
- data/lib/active_support/core_ext/integer.rb +2 -0
- data/lib/active_support/core_ext/integer/time.rb +45 -0
- data/lib/active_support/core_ext/kernel/debugger.rb +4 -4
- data/lib/active_support/core_ext/logger.rb +128 -1
- data/lib/active_support/core_ext/module.rb +11 -1
- data/lib/active_support/core_ext/module/aliasing.rb +71 -67
- data/lib/active_support/core_ext/module/delegation.rb +32 -1
- data/lib/active_support/core_ext/module/introspection.rb +81 -66
- data/lib/active_support/core_ext/module/model_naming.rb +8 -7
- data/lib/active_support/core_ext/module/synchronization.rb +39 -0
- data/lib/active_support/core_ext/numeric/time.rb +0 -10
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/object/extending.rb +29 -7
- data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
- data/lib/active_support/core_ext/object/metaclass.rb +13 -0
- data/lib/active_support/core_ext/object/misc.rb +36 -21
- data/lib/active_support/core_ext/rexml.rb +2 -2
- data/lib/active_support/core_ext/string.rb +6 -2
- data/lib/active_support/core_ext/string/access.rb +5 -5
- data/lib/active_support/core_ext/string/behavior.rb +13 -0
- data/lib/active_support/core_ext/string/inflections.rb +21 -2
- data/lib/active_support/core_ext/string/multibyte.rb +81 -0
- data/lib/active_support/core_ext/time/calculations.rb +39 -12
- data/lib/active_support/core_ext/time/conversions.rb +1 -0
- data/lib/active_support/core_ext/time/zones.rb +1 -1
- data/lib/active_support/dependencies.rb +178 -110
- data/lib/active_support/deprecation.rb +24 -4
- data/lib/active_support/inflector.rb +122 -38
- data/lib/active_support/json.rb +1 -1
- data/lib/active_support/json/encoders/date.rb +9 -2
- data/lib/active_support/json/encoders/date_time.rb +9 -2
- data/lib/active_support/json/encoders/time.rb +10 -3
- data/lib/active_support/locale/en.yml +32 -0
- data/lib/active_support/memoizable.rb +82 -0
- data/lib/active_support/multibyte.rb +31 -7
- data/lib/active_support/multibyte/chars.rb +664 -122
- data/lib/active_support/multibyte/exceptions.rb +8 -0
- data/lib/active_support/multibyte/unicode_database.rb +71 -0
- data/lib/active_support/option_merger.rb +2 -10
- data/lib/active_support/ordered_hash.rb +15 -0
- data/lib/active_support/rescuable.rb +108 -0
- data/lib/active_support/secure_random.rb +197 -0
- data/lib/active_support/string_inquirer.rb +11 -1
- data/lib/active_support/test_case.rb +16 -5
- data/lib/active_support/testing/core_ext/test.rb +6 -0
- data/lib/active_support/{core_ext → testing/core_ext}/test/unit/assertions.rb +14 -6
- data/lib/active_support/testing/performance.rb +452 -0
- data/lib/active_support/testing/setup_and_teardown.rb +34 -7
- data/lib/active_support/time_with_zone.rb +66 -42
- data/lib/active_support/values/time_zone.rb +11 -4
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/vendor.rb +13 -5
- data/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb +1 -1
- data/lib/active_support/vendor/i18n-0.0.1/i18n.rb +194 -0
- data/lib/active_support/vendor/i18n-0.0.1/i18n/backend/simple.rb +216 -0
- data/lib/active_support/vendor/i18n-0.0.1/i18n/exceptions.rb +53 -0
- data/lib/active_support/vendor/{memcache-client-1.5.0 → memcache-client-1.5.1}/memcache.rb +5 -5
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/data_timezone.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/data_timezone_info.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Algiers.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Cairo.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Casablanca.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Harare.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Johannesburg.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Monrovia.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Africa/Nairobi.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Argentina/Buenos_Aires.rb +0 -0
- data/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/America/Argentina/San_Juan.rb +86 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Bogota.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Caracas.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Chicago.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Chihuahua.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Denver.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Godthab.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Guatemala.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Halifax.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Indiana/Indianapolis.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Juneau.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/La_Paz.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Lima.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Los_Angeles.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Mazatlan.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Mexico_City.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Monterrey.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/New_York.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Phoenix.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Regina.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Santiago.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Sao_Paulo.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/St_Johns.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/America/Tijuana.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Almaty.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Baghdad.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Baku.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Bangkok.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Chongqing.rb +0 -0
- data/lib/active_support/vendor/tzinfo-0.3.12/tzinfo/definitions/Asia/Colombo.rb +30 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Dhaka.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Hong_Kong.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Irkutsk.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Jakarta.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Jerusalem.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kabul.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kamchatka.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Karachi.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Katmandu.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kolkata.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Krasnoyarsk.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kuala_Lumpur.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Kuwait.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Magadan.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Muscat.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Novosibirsk.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Rangoon.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Riyadh.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Seoul.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Shanghai.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Singapore.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Taipei.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tashkent.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tbilisi.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tehran.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Tokyo.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Ulaanbaatar.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Urumqi.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Vladivostok.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Yakutsk.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Yekaterinburg.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Asia/Yerevan.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Atlantic/Azores.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Atlantic/Cape_Verde.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Atlantic/South_Georgia.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Adelaide.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Brisbane.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Darwin.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Hobart.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Melbourne.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Perth.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Australia/Sydney.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Etc/UTC.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Amsterdam.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Athens.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Belgrade.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Berlin.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Bratislava.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Brussels.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Bucharest.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Budapest.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Copenhagen.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Dublin.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Helsinki.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Istanbul.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Kiev.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Lisbon.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Ljubljana.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/London.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Madrid.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Minsk.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Moscow.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Paris.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Prague.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Riga.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Rome.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Sarajevo.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Skopje.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Sofia.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Stockholm.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Tallinn.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Vienna.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Vilnius.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Warsaw.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Europe/Zagreb.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Auckland.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Fiji.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Guam.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Honolulu.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Majuro.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Midway.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Noumea.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Pago_Pago.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Port_Moresby.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/definitions/Pacific/Tongatapu.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/info_timezone.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/linked_timezone.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/linked_timezone_info.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/offset_rationals.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/ruby_core_support.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/time_or_datetime.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_definition.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_info.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_offset_info.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_period.rb +0 -0
- data/lib/active_support/vendor/{tzinfo-0.3.11 → tzinfo-0.3.12}/tzinfo/timezone_transition_info.rb +0 -0
- data/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb +3 -3
- data/lib/active_support/version.rb +1 -1
- metadata +185 -167
- data/lib/active_support/clean_logger.rb +0 -127
- data/lib/active_support/core_ext/string/unicode.rb +0 -66
- data/lib/active_support/core_ext/test.rb +0 -1
- data/lib/active_support/multibyte/generators/generate_tables.rb +0 -149
- data/lib/active_support/multibyte/handlers/passthru_handler.rb +0 -9
- data/lib/active_support/multibyte/handlers/utf8_handler.rb +0 -564
- data/lib/active_support/multibyte/handlers/utf8_handler_proc.rb +0 -43
- data/lib/active_support/vendor/tzinfo-0.3.11/tzinfo/definitions/America/Argentina/San_Juan.rb +0 -170
@@ -1,127 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
require 'active_support/core_ext/class/attribute_accessors'
|
3
|
-
|
4
|
-
# Extensions to the built in Ruby logger.
|
5
|
-
#
|
6
|
-
# If you want to use the default log formatter as defined in the Ruby core, then you
|
7
|
-
# will need to set the formatter for the logger as in:
|
8
|
-
#
|
9
|
-
# logger.formatter = Formatter.new
|
10
|
-
#
|
11
|
-
# You can then specify the datetime format, for example:
|
12
|
-
#
|
13
|
-
# logger.datetime_format = "%Y-%m-%d"
|
14
|
-
#
|
15
|
-
# Note: This logger is deprecated in favor of ActiveSupport::BufferedLogger
|
16
|
-
class Logger
|
17
|
-
# Set to false to disable the silencer
|
18
|
-
cattr_accessor :silencer
|
19
|
-
self.silencer = true
|
20
|
-
|
21
|
-
# Silences the logger for the duration of the block.
|
22
|
-
def silence(temporary_level = Logger::ERROR)
|
23
|
-
if silencer
|
24
|
-
begin
|
25
|
-
old_logger_level, self.level = level, temporary_level
|
26
|
-
yield self
|
27
|
-
ensure
|
28
|
-
self.level = old_logger_level
|
29
|
-
end
|
30
|
-
else
|
31
|
-
yield self
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
alias :old_datetime_format= :datetime_format=
|
36
|
-
# Logging date-time format (string passed to +strftime+). Ignored if the formatter
|
37
|
-
# does not respond to datetime_format=.
|
38
|
-
def datetime_format=(datetime_format)
|
39
|
-
formatter.datetime_format = datetime_format if formatter.respond_to?(:datetime_format=)
|
40
|
-
end
|
41
|
-
|
42
|
-
alias :old_datetime_format :datetime_format
|
43
|
-
# Get the logging datetime format. Returns nil if the formatter does not support
|
44
|
-
# datetime formatting.
|
45
|
-
def datetime_format
|
46
|
-
formatter.datetime_format if formatter.respond_to?(:datetime_format)
|
47
|
-
end
|
48
|
-
|
49
|
-
alias :old_formatter :formatter if method_defined?(:formatter)
|
50
|
-
# Get the current formatter. The default formatter is a SimpleFormatter which only
|
51
|
-
# displays the log message
|
52
|
-
def formatter
|
53
|
-
@formatter ||= SimpleFormatter.new
|
54
|
-
end
|
55
|
-
|
56
|
-
unless const_defined? :Formatter
|
57
|
-
class Formatter
|
58
|
-
Format = "%s, [%s#%d] %5s -- %s: %s\n"
|
59
|
-
|
60
|
-
attr_accessor :datetime_format
|
61
|
-
|
62
|
-
def initialize
|
63
|
-
@datetime_format = nil
|
64
|
-
end
|
65
|
-
|
66
|
-
def call(severity, time, progname, msg)
|
67
|
-
Format % [severity[0..0], format_datetime(time), $$, severity, progname,
|
68
|
-
msg2str(msg)]
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
def format_datetime(time)
|
73
|
-
if @datetime_format.nil?
|
74
|
-
time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
|
75
|
-
else
|
76
|
-
time.strftime(@datetime_format)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def msg2str(msg)
|
81
|
-
case msg
|
82
|
-
when ::String
|
83
|
-
msg
|
84
|
-
when ::Exception
|
85
|
-
"#{ msg.message } (#{ msg.class })\n" <<
|
86
|
-
(msg.backtrace || []).join("\n")
|
87
|
-
else
|
88
|
-
msg.inspect
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# Simple formatter which only displays the message.
|
95
|
-
class SimpleFormatter < Logger::Formatter
|
96
|
-
# This method is invoked when a log event occurs
|
97
|
-
def call(severity, timestamp, progname, msg)
|
98
|
-
"#{String === msg ? msg : msg.inspect}\n"
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
private
|
103
|
-
alias old_format_message format_message
|
104
|
-
|
105
|
-
# Ruby 1.8.3 transposed the msg and progname arguments to format_message.
|
106
|
-
# We can't test RUBY_VERSION because some distributions don't keep Ruby
|
107
|
-
# and its standard library in sync, leading to installations of Ruby 1.8.2
|
108
|
-
# with Logger from 1.8.3 and vice versa.
|
109
|
-
if method_defined?(:formatter=)
|
110
|
-
def format_message(severity, timestamp, progname, msg)
|
111
|
-
formatter.call(severity, timestamp, progname, msg)
|
112
|
-
end
|
113
|
-
else
|
114
|
-
def format_message(severity, timestamp, msg, progname)
|
115
|
-
formatter.call(severity, timestamp, progname, msg)
|
116
|
-
end
|
117
|
-
|
118
|
-
attr_writer :formatter
|
119
|
-
public :formatter=
|
120
|
-
|
121
|
-
alias old_format_datetime format_datetime
|
122
|
-
def format_datetime(datetime) datetime end
|
123
|
-
|
124
|
-
alias old_msg2str msg2str
|
125
|
-
def msg2str(msg) msg end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
module ActiveSupport #:nodoc:
|
2
|
-
module CoreExtensions #:nodoc:
|
3
|
-
module String #:nodoc:
|
4
|
-
# Define methods for handling unicode data.
|
5
|
-
module Unicode
|
6
|
-
def self.append_features(base)
|
7
|
-
if '1.8.7 and later'.respond_to?(:chars)
|
8
|
-
base.class_eval { remove_method :chars }
|
9
|
-
end
|
10
|
-
super
|
11
|
-
end
|
12
|
-
|
13
|
-
unless '1.9'.respond_to?(:force_encoding)
|
14
|
-
# +chars+ is a Unicode safe proxy for string methods. It creates and returns an instance of the
|
15
|
-
# ActiveSupport::Multibyte::Chars class which encapsulates the original string. A Unicode safe version of all
|
16
|
-
# the String methods are defined on this proxy class. Undefined methods are forwarded to String, so all of the
|
17
|
-
# string overrides can also be called through the +chars+ proxy.
|
18
|
-
#
|
19
|
-
# name = 'Claus Müller'
|
20
|
-
# name.reverse # => "rell??M sualC"
|
21
|
-
# name.length # => 13
|
22
|
-
#
|
23
|
-
# name.chars.reverse.to_s # => "rellüM sualC"
|
24
|
-
# name.chars.length # => 12
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# All the methods on the chars proxy which normally return a string will return a Chars object. This allows
|
28
|
-
# method chaining on the result of any of these methods.
|
29
|
-
#
|
30
|
-
# name.chars.reverse.length # => 12
|
31
|
-
#
|
32
|
-
# The Char object tries to be as interchangeable with String objects as possible: sorting and comparing between
|
33
|
-
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
|
34
|
-
# object. Interoperability problems can be resolved easily with a +to_s+ call.
|
35
|
-
#
|
36
|
-
# For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars and
|
37
|
-
# ActiveSupport::Multibyte::Handlers::UTF8Handler.
|
38
|
-
def chars
|
39
|
-
ActiveSupport::Multibyte::Chars.new(self)
|
40
|
-
end
|
41
|
-
|
42
|
-
# Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
|
43
|
-
# them), returns false otherwise.
|
44
|
-
def is_utf8?
|
45
|
-
ActiveSupport::Multibyte::Handlers::UTF8Handler.consumes?(self)
|
46
|
-
end
|
47
|
-
else
|
48
|
-
def chars #:nodoc:
|
49
|
-
self
|
50
|
-
end
|
51
|
-
|
52
|
-
def is_utf8? #:nodoc:
|
53
|
-
case encoding
|
54
|
-
when Encoding::UTF_8
|
55
|
-
valid_encoding?
|
56
|
-
when Encoding::ASCII_8BIT
|
57
|
-
dup.force_encoding('UTF-8').valid_encoding?
|
58
|
-
else
|
59
|
-
false
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/test/unit/assertions'
|
@@ -1,149 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
begin
|
3
|
-
require File.dirname(__FILE__) + '/../../../active_support'
|
4
|
-
rescue IOError
|
5
|
-
end
|
6
|
-
require 'open-uri'
|
7
|
-
require 'tmpdir'
|
8
|
-
|
9
|
-
module ActiveSupport::Multibyte::Handlers #:nodoc:
|
10
|
-
class UnicodeDatabase #:nodoc:
|
11
|
-
def self.load
|
12
|
-
[Hash.new(Codepoint.new),[],{},{}]
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class UnicodeTableGenerator #:nodoc:
|
17
|
-
BASE_URI = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::UNICODE_VERSION}/ucd/"
|
18
|
-
SOURCES = {
|
19
|
-
:codepoints => BASE_URI + 'UnicodeData.txt',
|
20
|
-
:composition_exclusion => BASE_URI + 'CompositionExclusions.txt',
|
21
|
-
:grapheme_break_property => BASE_URI + 'auxiliary/GraphemeBreakProperty.txt',
|
22
|
-
:cp1252 => 'http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT'
|
23
|
-
}
|
24
|
-
|
25
|
-
def initialize
|
26
|
-
@ucd = UnicodeDatabase.new
|
27
|
-
|
28
|
-
default = Codepoint.new
|
29
|
-
default.combining_class = 0
|
30
|
-
default.uppercase_mapping = 0
|
31
|
-
default.lowercase_mapping = 0
|
32
|
-
@ucd.codepoints = Hash.new(default)
|
33
|
-
|
34
|
-
@ucd.composition_exclusion = []
|
35
|
-
@ucd.composition_map = {}
|
36
|
-
@ucd.boundary = {}
|
37
|
-
@ucd.cp1252 = {}
|
38
|
-
end
|
39
|
-
|
40
|
-
def parse_codepoints(line)
|
41
|
-
codepoint = Codepoint.new
|
42
|
-
raise "Could not parse input." unless line =~ /^
|
43
|
-
([0-9A-F]+); # code
|
44
|
-
([^;]+); # name
|
45
|
-
([A-Z]+); # general category
|
46
|
-
([0-9]+); # canonical combining class
|
47
|
-
([A-Z]+); # bidi class
|
48
|
-
(<([A-Z]*)>)? # decomposition type
|
49
|
-
((\ ?[0-9A-F]+)*); # decompomposition mapping
|
50
|
-
([0-9]*); # decimal digit
|
51
|
-
([0-9]*); # digit
|
52
|
-
([^;]*); # numeric
|
53
|
-
([YN]*); # bidi mirrored
|
54
|
-
([^;]*); # unicode 1.0 name
|
55
|
-
([^;]*); # iso comment
|
56
|
-
([0-9A-F]*); # simple uppercase mapping
|
57
|
-
([0-9A-F]*); # simple lowercase mapping
|
58
|
-
([0-9A-F]*)$/ix # simple titlecase mapping
|
59
|
-
codepoint.code = $1.hex
|
60
|
-
#codepoint.name = $2
|
61
|
-
#codepoint.category = $3
|
62
|
-
codepoint.combining_class = Integer($4)
|
63
|
-
#codepoint.bidi_class = $5
|
64
|
-
codepoint.decomp_type = $7
|
65
|
-
codepoint.decomp_mapping = ($8=='') ? nil : $8.split.collect { |element| element.hex }
|
66
|
-
#codepoint.bidi_mirrored = ($13=='Y') ? true : false
|
67
|
-
codepoint.uppercase_mapping = ($16=='') ? 0 : $16.hex
|
68
|
-
codepoint.lowercase_mapping = ($17=='') ? 0 : $17.hex
|
69
|
-
#codepoint.titlecase_mapping = ($18=='') ? nil : $18.hex
|
70
|
-
@ucd.codepoints[codepoint.code] = codepoint
|
71
|
-
end
|
72
|
-
|
73
|
-
def parse_grapheme_break_property(line)
|
74
|
-
if line =~ /^([0-9A-F\.]+)\s*;\s*([\w]+)\s*#/
|
75
|
-
type = $2.downcase.intern
|
76
|
-
@ucd.boundary[type] ||= []
|
77
|
-
if $1.include? '..'
|
78
|
-
parts = $1.split '..'
|
79
|
-
@ucd.boundary[type] << (parts[0].hex..parts[1].hex)
|
80
|
-
else
|
81
|
-
@ucd.boundary[type] << $1.hex
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def parse_composition_exclusion(line)
|
87
|
-
if line =~ /^([0-9A-F]+)/i
|
88
|
-
@ucd.composition_exclusion << $1.hex
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def parse_cp1252(line)
|
93
|
-
if line =~ /^([0-9A-Fx]+)\s([0-9A-Fx]+)/i
|
94
|
-
@ucd.cp1252[$1.hex] = $2.hex
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def create_composition_map
|
99
|
-
@ucd.codepoints.each do |_, cp|
|
100
|
-
if !cp.nil? and cp.combining_class == 0 and cp.decomp_type.nil? and !cp.decomp_mapping.nil? and cp.decomp_mapping.length == 2 and @ucd[cp.decomp_mapping[0]].combining_class == 0 and !@ucd.composition_exclusion.include?(cp.code)
|
101
|
-
@ucd.composition_map[cp.decomp_mapping[0]] ||= {}
|
102
|
-
@ucd.composition_map[cp.decomp_mapping[0]][cp.decomp_mapping[1]] = cp.code
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def normalize_boundary_map
|
108
|
-
@ucd.boundary.each do |k,v|
|
109
|
-
if [:lf, :cr].include? k
|
110
|
-
@ucd.boundary[k] = v[0]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def parse
|
116
|
-
SOURCES.each do |type, url|
|
117
|
-
filename = File.join(Dir.tmpdir, "#{url.split('/').last}")
|
118
|
-
unless File.exist?(filename)
|
119
|
-
$stderr.puts "Downloading #{url.split('/').last}"
|
120
|
-
File.open(filename, 'wb') do |target|
|
121
|
-
open(url) do |source|
|
122
|
-
source.each_line { |line| target.write line }
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
File.open(filename) do |file|
|
127
|
-
file.each_line { |line| send "parse_#{type}".intern, line }
|
128
|
-
end
|
129
|
-
end
|
130
|
-
create_composition_map
|
131
|
-
normalize_boundary_map
|
132
|
-
end
|
133
|
-
|
134
|
-
def dump_to(filename)
|
135
|
-
File.open(filename, 'wb') do |f|
|
136
|
-
f.write Marshal.dump([@ucd.codepoints, @ucd.composition_exclusion, @ucd.composition_map, @ucd.boundary, @ucd.cp1252])
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
if __FILE__ == $0
|
143
|
-
filename = ActiveSupport::Multibyte::Handlers::UnicodeDatabase.filename
|
144
|
-
generator = ActiveSupport::Multibyte::Handlers::UnicodeTableGenerator.new
|
145
|
-
generator.parse
|
146
|
-
print "Writing to: #{filename}"
|
147
|
-
generator.dump_to filename
|
148
|
-
puts " (#{File.size(filename)} bytes)"
|
149
|
-
end
|
@@ -1,9 +0,0 @@
|
|
1
|
-
# Chars uses this handler when $KCODE is not set to 'UTF8'. Because this handler doesn't define any methods all call
|
2
|
-
# will be forwarded to String.
|
3
|
-
class ActiveSupport::Multibyte::Handlers::PassthruHandler #:nodoc:
|
4
|
-
|
5
|
-
# Return the original byteoffset
|
6
|
-
def self.translate_offset(string, byte_offset) #:nodoc:
|
7
|
-
byte_offset
|
8
|
-
end
|
9
|
-
end
|
@@ -1,564 +0,0 @@
|
|
1
|
-
# Contains all the handlers and helper classes
|
2
|
-
module ActiveSupport::Multibyte::Handlers #:nodoc:
|
3
|
-
class EncodingError < ArgumentError #:nodoc:
|
4
|
-
end
|
5
|
-
|
6
|
-
class Codepoint #:nodoc:
|
7
|
-
attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
|
8
|
-
end
|
9
|
-
|
10
|
-
class UnicodeDatabase #:nodoc:
|
11
|
-
attr_writer :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
|
12
|
-
|
13
|
-
# self-expiring methods that lazily load the Unicode database and then return the value.
|
14
|
-
[:codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252].each do |attr_name|
|
15
|
-
class_eval(<<-EOS, __FILE__, __LINE__)
|
16
|
-
def #{attr_name}
|
17
|
-
load
|
18
|
-
@#{attr_name}
|
19
|
-
end
|
20
|
-
EOS
|
21
|
-
end
|
22
|
-
|
23
|
-
# Shortcut to ucd.codepoints[]
|
24
|
-
def [](index); codepoints[index]; end
|
25
|
-
|
26
|
-
# Returns the directory in which the data files are stored
|
27
|
-
def self.dirname
|
28
|
-
File.dirname(__FILE__) + '/../../values/'
|
29
|
-
end
|
30
|
-
|
31
|
-
# Returns the filename for the data file for this version
|
32
|
-
def self.filename
|
33
|
-
File.expand_path File.join(dirname, "unicode_tables.dat")
|
34
|
-
end
|
35
|
-
|
36
|
-
# Loads the unicode database and returns all the internal objects of UnicodeDatabase
|
37
|
-
# Once the values have been loaded, define attr_reader methods for the instance variables.
|
38
|
-
def load
|
39
|
-
begin
|
40
|
-
@codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
|
41
|
-
rescue Exception => e
|
42
|
-
raise IOError.new("Couldn't load the unicode tables for UTF8Handler (#{e.message}), handler is unusable")
|
43
|
-
end
|
44
|
-
@codepoints ||= Hash.new(Codepoint.new)
|
45
|
-
@composition_exclusion ||= []
|
46
|
-
@composition_map ||= {}
|
47
|
-
@boundary ||= {}
|
48
|
-
@cp1252 ||= {}
|
49
|
-
|
50
|
-
# Redefine the === method so we can write shorter rules for grapheme cluster breaks
|
51
|
-
@boundary.each do |k,_|
|
52
|
-
@boundary[k].instance_eval do
|
53
|
-
def ===(other)
|
54
|
-
detect { |i| i === other } ? true : false
|
55
|
-
end
|
56
|
-
end if @boundary[k].kind_of?(Array)
|
57
|
-
end
|
58
|
-
|
59
|
-
# define attr_reader methods for the instance variables
|
60
|
-
class << self
|
61
|
-
attr_reader :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# UTF8Handler implements Unicode aware operations for strings, these operations will be used by the Chars
|
67
|
-
# proxy when $KCODE is set to 'UTF8'.
|
68
|
-
class UTF8Handler
|
69
|
-
# Hangul character boundaries and properties
|
70
|
-
HANGUL_SBASE = 0xAC00
|
71
|
-
HANGUL_LBASE = 0x1100
|
72
|
-
HANGUL_VBASE = 0x1161
|
73
|
-
HANGUL_TBASE = 0x11A7
|
74
|
-
HANGUL_LCOUNT = 19
|
75
|
-
HANGUL_VCOUNT = 21
|
76
|
-
HANGUL_TCOUNT = 28
|
77
|
-
HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT
|
78
|
-
HANGUL_SCOUNT = 11172
|
79
|
-
HANGUL_SLAST = HANGUL_SBASE + HANGUL_SCOUNT
|
80
|
-
HANGUL_JAMO_FIRST = 0x1100
|
81
|
-
HANGUL_JAMO_LAST = 0x11FF
|
82
|
-
|
83
|
-
# All the unicode whitespace
|
84
|
-
UNICODE_WHITESPACE = [
|
85
|
-
(0x0009..0x000D).to_a, # White_Space # Cc [5] <control-0009>..<control-000D>
|
86
|
-
0x0020, # White_Space # Zs SPACE
|
87
|
-
0x0085, # White_Space # Cc <control-0085>
|
88
|
-
0x00A0, # White_Space # Zs NO-BREAK SPACE
|
89
|
-
0x1680, # White_Space # Zs OGHAM SPACE MARK
|
90
|
-
0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
|
91
|
-
(0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
|
92
|
-
0x2028, # White_Space # Zl LINE SEPARATOR
|
93
|
-
0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
|
94
|
-
0x202F, # White_Space # Zs NARROW NO-BREAK SPACE
|
95
|
-
0x205F, # White_Space # Zs MEDIUM MATHEMATICAL SPACE
|
96
|
-
0x3000, # White_Space # Zs IDEOGRAPHIC SPACE
|
97
|
-
].flatten.freeze
|
98
|
-
|
99
|
-
# BOM (byte order mark) can also be seen as whitespace, it's a non-rendering character used to distinguish
|
100
|
-
# between little and big endian. This is not an issue in utf-8, so it must be ignored.
|
101
|
-
UNICODE_LEADERS_AND_TRAILERS = UNICODE_WHITESPACE + [65279] # ZERO-WIDTH NO-BREAK SPACE aka BOM
|
102
|
-
|
103
|
-
# Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site)
|
104
|
-
UTF8_PAT = /\A(?:
|
105
|
-
[\x00-\x7f] |
|
106
|
-
[\xc2-\xdf] [\x80-\xbf] |
|
107
|
-
\xe0 [\xa0-\xbf] [\x80-\xbf] |
|
108
|
-
[\xe1-\xef] [\x80-\xbf] [\x80-\xbf] |
|
109
|
-
\xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] |
|
110
|
-
[\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] |
|
111
|
-
\xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf]
|
112
|
-
)*\z/xn
|
113
|
-
|
114
|
-
# Returns a regular expression pattern that matches the passed Unicode codepoints
|
115
|
-
def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
|
116
|
-
array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
|
117
|
-
end
|
118
|
-
UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/
|
119
|
-
UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/
|
120
|
-
|
121
|
-
class << self
|
122
|
-
|
123
|
-
# ///
|
124
|
-
# /// BEGIN String method overrides
|
125
|
-
# ///
|
126
|
-
|
127
|
-
# Inserts the passed string at specified codepoint offsets
|
128
|
-
def insert(str, offset, fragment)
|
129
|
-
str.replace(
|
130
|
-
u_unpack(str).insert(
|
131
|
-
offset,
|
132
|
-
u_unpack(fragment)
|
133
|
-
).flatten.pack('U*')
|
134
|
-
)
|
135
|
-
end
|
136
|
-
|
137
|
-
# Returns the position of the passed argument in the string, counting in codepoints
|
138
|
-
def index(str, *args)
|
139
|
-
bidx = str.index(*args)
|
140
|
-
bidx ? (u_unpack(str.slice(0...bidx)).size) : nil
|
141
|
-
end
|
142
|
-
|
143
|
-
# Works just like the indexed replace method on string, except instead of byte offsets you specify
|
144
|
-
# character offsets.
|
145
|
-
#
|
146
|
-
# Example:
|
147
|
-
#
|
148
|
-
# s = "Müller"
|
149
|
-
# s.chars[2] = "e" # Replace character with offset 2
|
150
|
-
# s # => "Müeler"
|
151
|
-
#
|
152
|
-
# s = "Müller"
|
153
|
-
# s.chars[1, 2] = "ö" # Replace 2 characters at character offset 1
|
154
|
-
# s # => "Möler"
|
155
|
-
def []=(str, *args)
|
156
|
-
replace_by = args.pop
|
157
|
-
# Indexed replace with regular expressions already works
|
158
|
-
return str[*args] = replace_by if args.first.is_a?(Regexp)
|
159
|
-
result = u_unpack(str)
|
160
|
-
if args[0].is_a?(Fixnum)
|
161
|
-
raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
|
162
|
-
min = args[0]
|
163
|
-
max = args[1].nil? ? min : (min + args[1] - 1)
|
164
|
-
range = Range.new(min, max)
|
165
|
-
replace_by = [replace_by].pack('U') if replace_by.is_a?(Fixnum)
|
166
|
-
elsif args.first.is_a?(Range)
|
167
|
-
raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
|
168
|
-
range = args[0]
|
169
|
-
else
|
170
|
-
needle = args[0].to_s
|
171
|
-
min = index(str, needle)
|
172
|
-
max = min + length(needle) - 1
|
173
|
-
range = Range.new(min, max)
|
174
|
-
end
|
175
|
-
result[range] = u_unpack(replace_by)
|
176
|
-
str.replace(result.pack('U*'))
|
177
|
-
end
|
178
|
-
|
179
|
-
# Works just like String#rjust, only integer specifies characters instead of bytes.
|
180
|
-
#
|
181
|
-
# Example:
|
182
|
-
#
|
183
|
-
# "¾ cup".chars.rjust(8).to_s
|
184
|
-
# # => " ¾ cup"
|
185
|
-
#
|
186
|
-
# "¾ cup".chars.rjust(8, " ").to_s # Use non-breaking whitespace
|
187
|
-
# # => " ¾ cup"
|
188
|
-
def rjust(str, integer, padstr=' ')
|
189
|
-
justify(str, integer, :right, padstr)
|
190
|
-
end
|
191
|
-
|
192
|
-
# Works just like String#ljust, only integer specifies characters instead of bytes.
|
193
|
-
#
|
194
|
-
# Example:
|
195
|
-
#
|
196
|
-
# "¾ cup".chars.rjust(8).to_s
|
197
|
-
# # => "¾ cup "
|
198
|
-
#
|
199
|
-
# "¾ cup".chars.rjust(8, " ").to_s # Use non-breaking whitespace
|
200
|
-
# # => "¾ cup "
|
201
|
-
def ljust(str, integer, padstr=' ')
|
202
|
-
justify(str, integer, :left, padstr)
|
203
|
-
end
|
204
|
-
|
205
|
-
# Works just like String#center, only integer specifies characters instead of bytes.
|
206
|
-
#
|
207
|
-
# Example:
|
208
|
-
#
|
209
|
-
# "¾ cup".chars.center(8).to_s
|
210
|
-
# # => " ¾ cup "
|
211
|
-
#
|
212
|
-
# "¾ cup".chars.center(8, " ").to_s # Use non-breaking whitespace
|
213
|
-
# # => " ¾ cup "
|
214
|
-
def center(str, integer, padstr=' ')
|
215
|
-
justify(str, integer, :center, padstr)
|
216
|
-
end
|
217
|
-
|
218
|
-
# Does Unicode-aware rstrip
|
219
|
-
def rstrip(str)
|
220
|
-
str.gsub(UNICODE_TRAILERS_PAT, '')
|
221
|
-
end
|
222
|
-
|
223
|
-
# Does Unicode-aware lstrip
|
224
|
-
def lstrip(str)
|
225
|
-
str.gsub(UNICODE_LEADERS_PAT, '')
|
226
|
-
end
|
227
|
-
|
228
|
-
# Removed leading and trailing whitespace
|
229
|
-
def strip(str)
|
230
|
-
str.gsub(UNICODE_LEADERS_PAT, '').gsub(UNICODE_TRAILERS_PAT, '')
|
231
|
-
end
|
232
|
-
|
233
|
-
# Returns the number of codepoints in the string
|
234
|
-
def size(str)
|
235
|
-
u_unpack(str).size
|
236
|
-
end
|
237
|
-
alias_method :length, :size
|
238
|
-
|
239
|
-
# Reverses codepoints in the string.
|
240
|
-
def reverse(str)
|
241
|
-
u_unpack(str).reverse.pack('U*')
|
242
|
-
end
|
243
|
-
|
244
|
-
# Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that
|
245
|
-
# character.
|
246
|
-
def slice(str, *args)
|
247
|
-
if args.size > 2
|
248
|
-
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
|
249
|
-
elsif (args.size == 2 && !(args.first.is_a?(Numeric) || args.first.is_a?(Regexp)))
|
250
|
-
raise TypeError, "cannot convert #{args.first.class} into Integer" # Do as if we were native
|
251
|
-
elsif (args.size == 2 && !args[1].is_a?(Numeric))
|
252
|
-
raise TypeError, "cannot convert #{args[1].class} into Integer" # Do as if we were native
|
253
|
-
elsif args[0].kind_of? Range
|
254
|
-
cps = u_unpack(str).slice(*args)
|
255
|
-
cps.nil? ? nil : cps.pack('U*')
|
256
|
-
elsif args[0].kind_of? Regexp
|
257
|
-
str.slice(*args)
|
258
|
-
elsif args.size == 1 && args[0].kind_of?(Numeric)
|
259
|
-
u_unpack(str)[args[0]]
|
260
|
-
else
|
261
|
-
u_unpack(str).slice(*args).pack('U*')
|
262
|
-
end
|
263
|
-
end
|
264
|
-
alias_method :[], :slice
|
265
|
-
|
266
|
-
# Convert characters in the string to uppercase
|
267
|
-
def upcase(str); to_case :uppercase_mapping, str; end
|
268
|
-
|
269
|
-
# Convert characters in the string to lowercase
|
270
|
-
def downcase(str); to_case :lowercase_mapping, str; end
|
271
|
-
|
272
|
-
# Returns a copy of +str+ with the first character converted to uppercase and the remainder to lowercase
|
273
|
-
def capitalize(str)
|
274
|
-
upcase(slice(str, 0..0)) + downcase(slice(str, 1..-1) || '')
|
275
|
-
end
|
276
|
-
|
277
|
-
# ///
|
278
|
-
# /// Extra String methods for unicode operations
|
279
|
-
# ///
|
280
|
-
|
281
|
-
# Returns the KC normalization of the string by default. NFKC is considered the best normalization form for
|
282
|
-
# passing strings to databases and validations.
|
283
|
-
#
|
284
|
-
# * <tt>str</tt> - The string to perform normalization on.
|
285
|
-
# * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
|
286
|
-
# <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
|
287
|
-
# ActiveSupport::Multibyte::DEFAULT_NORMALIZATION_FORM.
|
288
|
-
def normalize(str, form=ActiveSupport::Multibyte::DEFAULT_NORMALIZATION_FORM)
|
289
|
-
# See http://www.unicode.org/reports/tr15, Table 1
|
290
|
-
codepoints = u_unpack(str)
|
291
|
-
case form
|
292
|
-
when :d
|
293
|
-
reorder_characters(decompose_codepoints(:canonical, codepoints))
|
294
|
-
when :c
|
295
|
-
compose_codepoints reorder_characters(decompose_codepoints(:canonical, codepoints))
|
296
|
-
when :kd
|
297
|
-
reorder_characters(decompose_codepoints(:compatability, codepoints))
|
298
|
-
when :kc
|
299
|
-
compose_codepoints reorder_characters(decompose_codepoints(:compatability, codepoints))
|
300
|
-
else
|
301
|
-
raise ArgumentError, "#{form} is not a valid normalization variant", caller
|
302
|
-
end.pack('U*')
|
303
|
-
end
|
304
|
-
|
305
|
-
# Perform decomposition on the characters in the string
|
306
|
-
def decompose(str)
|
307
|
-
decompose_codepoints(:canonical, u_unpack(str)).pack('U*')
|
308
|
-
end
|
309
|
-
|
310
|
-
# Perform composition on the characters in the string
|
311
|
-
def compose(str)
|
312
|
-
compose_codepoints u_unpack(str).pack('U*')
|
313
|
-
end
|
314
|
-
|
315
|
-
# ///
|
316
|
-
# /// BEGIN Helper methods for unicode operation
|
317
|
-
# ///
|
318
|
-
|
319
|
-
# Used to translate an offset from bytes to characters, for instance one received from a regular expression match
|
320
|
-
def translate_offset(str, byte_offset)
|
321
|
-
return nil if byte_offset.nil?
|
322
|
-
return 0 if str == ''
|
323
|
-
chunk = str[0..byte_offset]
|
324
|
-
begin
|
325
|
-
begin
|
326
|
-
chunk.unpack('U*').length - 1
|
327
|
-
rescue ArgumentError => e
|
328
|
-
chunk = str[0..(byte_offset+=1)]
|
329
|
-
# Stop retrying at the end of the string
|
330
|
-
raise e unless byte_offset < chunk.length
|
331
|
-
# We damaged a character, retry
|
332
|
-
retry
|
333
|
-
end
|
334
|
-
# Catch the ArgumentError so we can throw our own
|
335
|
-
rescue ArgumentError
|
336
|
-
raise EncodingError.new('malformed UTF-8 character')
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
# Checks if the string is valid UTF8.
|
341
|
-
def consumes?(str)
|
342
|
-
# Unpack is a little bit faster than regular expressions
|
343
|
-
begin
|
344
|
-
str.unpack('U*')
|
345
|
-
true
|
346
|
-
rescue ArgumentError
|
347
|
-
false
|
348
|
-
end
|
349
|
-
end
|
350
|
-
|
351
|
-
# Returns the number of grapheme clusters in the string. This method is very likely to be moved or renamed
|
352
|
-
# in future versions.
|
353
|
-
def g_length(str)
|
354
|
-
g_unpack(str).length
|
355
|
-
end
|
356
|
-
|
357
|
-
# Replaces all the non-utf-8 bytes by their iso-8859-1 or cp1252 equivalent resulting in a valid utf-8 string
|
358
|
-
def tidy_bytes(str)
|
359
|
-
str.split(//u).map do |c|
|
360
|
-
if !UTF8_PAT.match(c)
|
361
|
-
n = c.unpack('C')[0]
|
362
|
-
n < 128 ? n.chr :
|
363
|
-
n < 160 ? [UCD.cp1252[n] || n].pack('U') :
|
364
|
-
n < 192 ? "\xC2" + n.chr : "\xC3" + (n-64).chr
|
365
|
-
else
|
366
|
-
c
|
367
|
-
end
|
368
|
-
end.join
|
369
|
-
end
|
370
|
-
|
371
|
-
protected
|
372
|
-
|
373
|
-
# Detect whether the codepoint is in a certain character class. Primarily used by the
|
374
|
-
# grapheme cluster support.
|
375
|
-
def in_char_class?(codepoint, classes)
|
376
|
-
classes.detect { |c| UCD.boundary[c] === codepoint } ? true : false
|
377
|
-
end
|
378
|
-
|
379
|
-
# Unpack the string at codepoints boundaries
|
380
|
-
def u_unpack(str)
|
381
|
-
begin
|
382
|
-
str.unpack 'U*'
|
383
|
-
rescue ArgumentError
|
384
|
-
raise EncodingError.new('malformed UTF-8 character')
|
385
|
-
end
|
386
|
-
end
|
387
|
-
|
388
|
-
# Unpack the string at grapheme boundaries instead of codepoint boundaries
|
389
|
-
def g_unpack(str)
|
390
|
-
codepoints = u_unpack(str)
|
391
|
-
unpacked = []
|
392
|
-
pos = 0
|
393
|
-
marker = 0
|
394
|
-
eoc = codepoints.length
|
395
|
-
while(pos < eoc)
|
396
|
-
pos += 1
|
397
|
-
previous = codepoints[pos-1]
|
398
|
-
current = codepoints[pos]
|
399
|
-
if (
|
400
|
-
# CR X LF
|
401
|
-
one = ( previous == UCD.boundary[:cr] and current == UCD.boundary[:lf] ) or
|
402
|
-
# L X (L|V|LV|LVT)
|
403
|
-
two = ( UCD.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or
|
404
|
-
# (LV|V) X (V|T)
|
405
|
-
three = ( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or
|
406
|
-
# (LVT|T) X (T)
|
407
|
-
four = ( in_char_class?(previous, [:lvt,:t]) and UCD.boundary[:t] === current ) or
|
408
|
-
# X Extend
|
409
|
-
five = (UCD.boundary[:extend] === current)
|
410
|
-
)
|
411
|
-
else
|
412
|
-
unpacked << codepoints[marker..pos-1]
|
413
|
-
marker = pos
|
414
|
-
end
|
415
|
-
end
|
416
|
-
unpacked
|
417
|
-
end
|
418
|
-
|
419
|
-
# Reverse operation of g_unpack
|
420
|
-
def g_pack(unpacked)
|
421
|
-
unpacked.flatten
|
422
|
-
end
|
423
|
-
|
424
|
-
# Justifies a string in a certain way. Valid values for <tt>way</tt> are <tt>:right</tt>, <tt>:left</tt> and
|
425
|
-
# <tt>:center</tt>. Is primarily used as a helper method by <tt>rjust</tt>, <tt>ljust</tt> and <tt>center</tt>.
|
426
|
-
def justify(str, integer, way, padstr=' ')
|
427
|
-
raise ArgumentError, "zero width padding" if padstr.length == 0
|
428
|
-
padsize = integer - size(str)
|
429
|
-
padsize = padsize > 0 ? padsize : 0
|
430
|
-
case way
|
431
|
-
when :right
|
432
|
-
str.dup.insert(0, padding(padsize, padstr))
|
433
|
-
when :left
|
434
|
-
str.dup.insert(-1, padding(padsize, padstr))
|
435
|
-
when :center
|
436
|
-
lpad = padding((padsize / 2.0).floor, padstr)
|
437
|
-
rpad = padding((padsize / 2.0).ceil, padstr)
|
438
|
-
str.dup.insert(0, lpad).insert(-1, rpad)
|
439
|
-
end
|
440
|
-
end
|
441
|
-
|
442
|
-
# Generates a padding string of a certain size.
|
443
|
-
def padding(padsize, padstr=' ')
|
444
|
-
if padsize != 0
|
445
|
-
slice(padstr * ((padsize / size(padstr)) + 1), 0, padsize)
|
446
|
-
else
|
447
|
-
''
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
# Convert characters to a different case
|
452
|
-
def to_case(way, str)
|
453
|
-
u_unpack(str).map do |codepoint|
|
454
|
-
cp = UCD[codepoint]
|
455
|
-
unless cp.nil?
|
456
|
-
ncp = cp.send(way)
|
457
|
-
ncp > 0 ? ncp : codepoint
|
458
|
-
else
|
459
|
-
codepoint
|
460
|
-
end
|
461
|
-
end.pack('U*')
|
462
|
-
end
|
463
|
-
|
464
|
-
# Re-order codepoints so the string becomes canonical
|
465
|
-
def reorder_characters(codepoints)
|
466
|
-
length = codepoints.length- 1
|
467
|
-
pos = 0
|
468
|
-
while pos < length do
|
469
|
-
cp1, cp2 = UCD[codepoints[pos]], UCD[codepoints[pos+1]]
|
470
|
-
if (cp1.combining_class > cp2.combining_class) && (cp2.combining_class > 0)
|
471
|
-
codepoints[pos..pos+1] = cp2.code, cp1.code
|
472
|
-
pos += (pos > 0 ? -1 : 1)
|
473
|
-
else
|
474
|
-
pos += 1
|
475
|
-
end
|
476
|
-
end
|
477
|
-
codepoints
|
478
|
-
end
|
479
|
-
|
480
|
-
# Decompose composed characters to the decomposed form
|
481
|
-
def decompose_codepoints(type, codepoints)
|
482
|
-
codepoints.inject([]) do |decomposed, cp|
|
483
|
-
# if it's a hangul syllable starter character
|
484
|
-
if HANGUL_SBASE <= cp and cp < HANGUL_SLAST
|
485
|
-
sindex = cp - HANGUL_SBASE
|
486
|
-
ncp = [] # new codepoints
|
487
|
-
ncp << HANGUL_LBASE + sindex / HANGUL_NCOUNT
|
488
|
-
ncp << HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
|
489
|
-
tindex = sindex % HANGUL_TCOUNT
|
490
|
-
ncp << (HANGUL_TBASE + tindex) unless tindex == 0
|
491
|
-
decomposed.concat ncp
|
492
|
-
# if the codepoint is decomposable in with the current decomposition type
|
493
|
-
elsif (ncp = UCD[cp].decomp_mapping) and (!UCD[cp].decomp_type || type == :compatability)
|
494
|
-
decomposed.concat decompose_codepoints(type, ncp.dup)
|
495
|
-
else
|
496
|
-
decomposed << cp
|
497
|
-
end
|
498
|
-
end
|
499
|
-
end
|
500
|
-
|
501
|
-
# Compose decomposed characters to the composed form
|
502
|
-
def compose_codepoints(codepoints)
|
503
|
-
pos = 0
|
504
|
-
eoa = codepoints.length - 1
|
505
|
-
starter_pos = 0
|
506
|
-
starter_char = codepoints[0]
|
507
|
-
previous_combining_class = -1
|
508
|
-
while pos < eoa
|
509
|
-
pos += 1
|
510
|
-
lindex = starter_char - HANGUL_LBASE
|
511
|
-
# -- Hangul
|
512
|
-
if 0 <= lindex and lindex < HANGUL_LCOUNT
|
513
|
-
vindex = codepoints[starter_pos+1] - HANGUL_VBASE rescue vindex = -1
|
514
|
-
if 0 <= vindex and vindex < HANGUL_VCOUNT
|
515
|
-
tindex = codepoints[starter_pos+2] - HANGUL_TBASE rescue tindex = -1
|
516
|
-
if 0 <= tindex and tindex < HANGUL_TCOUNT
|
517
|
-
j = starter_pos + 2
|
518
|
-
eoa -= 2
|
519
|
-
else
|
520
|
-
tindex = 0
|
521
|
-
j = starter_pos + 1
|
522
|
-
eoa -= 1
|
523
|
-
end
|
524
|
-
codepoints[starter_pos..j] = (lindex * HANGUL_VCOUNT + vindex) * HANGUL_TCOUNT + tindex + HANGUL_SBASE
|
525
|
-
end
|
526
|
-
starter_pos += 1
|
527
|
-
starter_char = codepoints[starter_pos]
|
528
|
-
# -- Other characters
|
529
|
-
else
|
530
|
-
current_char = codepoints[pos]
|
531
|
-
current = UCD[current_char]
|
532
|
-
if current.combining_class > previous_combining_class
|
533
|
-
if ref = UCD.composition_map[starter_char]
|
534
|
-
composition = ref[current_char]
|
535
|
-
else
|
536
|
-
composition = nil
|
537
|
-
end
|
538
|
-
unless composition.nil?
|
539
|
-
codepoints[starter_pos] = composition
|
540
|
-
starter_char = composition
|
541
|
-
codepoints.delete_at pos
|
542
|
-
eoa -= 1
|
543
|
-
pos -= 1
|
544
|
-
previous_combining_class = -1
|
545
|
-
else
|
546
|
-
previous_combining_class = current.combining_class
|
547
|
-
end
|
548
|
-
else
|
549
|
-
previous_combining_class = current.combining_class
|
550
|
-
end
|
551
|
-
if current.combining_class == 0
|
552
|
-
starter_pos = pos
|
553
|
-
starter_char = codepoints[pos]
|
554
|
-
end
|
555
|
-
end
|
556
|
-
end
|
557
|
-
codepoints
|
558
|
-
end
|
559
|
-
|
560
|
-
# UniCode Database
|
561
|
-
UCD = UnicodeDatabase.new
|
562
|
-
end
|
563
|
-
end
|
564
|
-
end
|