1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Support extended grapheme clusters and UAX 29

http://www.unicode.org/reports/tr29/tr29-21.html is the version of UAX
29 that corresponds to Unicode 6.2.0. Unicode.unpack_graphemes now
implements all the rules listed there, including the ones for extended
grapheme clusters.

I added a new optional test,
test/multibyte_grapheme_break_conformance.rb, that is heavily based on
test/multibyte_normalization_conformance.rb, which runs the Unicode test
suite.
This commit is contained in:
Adam Roben 2013-11-13 17:16:55 -05:00
parent e63aad97bd
commit 3ca6d1fb4a
2 changed files with 91 additions and 0 deletions

View file

@ -94,6 +94,12 @@ module ActiveSupport
# GB3. CR X LF
if previous == database.boundary[:cr] and current == database.boundary[:lf]
false
# GB4. (Control|CR|LF) ÷
elsif previous and in_char_class?(previous, [:control,:cr,:lf])
true
# GB5. ÷ (Control|CR|LF)
elsif in_char_class?(current, [:control,:cr,:lf])
true
# GB6. L X (L|V|LV|LVT)
elsif database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt])
false
@ -103,9 +109,18 @@ module ActiveSupport
# GB8. (LVT|T) X (T)
elsif in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current
false
# GB8a. Regional_Indicator X Regional_Indicator
elsif database.boundary[:regional_indicator] === previous and database.boundary[:regional_indicator] === current
false
# GB9. X Extend
elsif database.boundary[:extend] === current
false
# GB9a. X SpacingMark
elsif database.boundary[:spacingmark] === current
false
# GB9b. Prepend X
elsif database.boundary[:prepend] === previous
false
# GB10. Any ÷ Any
else
true

View file

@ -0,0 +1,76 @@
# encoding: utf-8
require 'abstract_unit'
require 'fileutils'
require 'open-uri'
require 'tmpdir'
class Downloader
def self.download(from, to)
unless File.exist?(to)
$stderr.puts "Downloading #{from} to #{to}"
unless File.exist?(File.dirname(to))
system "mkdir -p #{File.dirname(to)}"
end
open(from) do |source|
File.open(to, 'w') do |target|
source.each_line do |l|
target.write l
end
end
end
end
end
end
class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase
TEST_DATA_URL = "http://www.unicode.org/Public/#{ActiveSupport::Multibyte::Unicode::UNICODE_VERSION}/ucd/auxiliary"
TEST_DATA_FILE = '/GraphemeBreakTest.txt'
CACHE_DIR = File.join(Dir.tmpdir, 'cache')
def setup
FileUtils.mkdir_p(CACHE_DIR)
Downloader.download(TEST_DATA_URL + TEST_DATA_FILE, CACHE_DIR + TEST_DATA_FILE)
end
def test_breaks
each_line_of_break_tests do |*cols|
*clusters, comment = *cols
packed = ActiveSupport::Multibyte::Unicode.pack_graphemes(clusters)
assert_equal clusters, ActiveSupport::Multibyte::Unicode.unpack_graphemes(packed), comment
end
end
protected
def each_line_of_break_tests(&block)
lines = 0
max_test_lines = 0 # Don't limit below 21, because that's the header of the testfile
File.open(File.join(CACHE_DIR, TEST_DATA_FILE), 'r') do | f |
until f.eof? || (max_test_lines > 21 and lines > max_test_lines)
lines += 1
line = f.gets.chomp!
next if (line.empty? || line =~ /^\#/)
cols, comment = line.split("#")
# Cluster breaks are represented by ÷
clusters = cols.split("÷").map{|e| e.strip}.reject{|e| e.empty? }
clusters = clusters.map do |cluster|
# Codepoints within each cluster are separated by ×
codepoints = cluster.split("×").map{|e| e.strip}.reject{|e| e.empty? }
# codepoints are in hex in the test suite, pack wants them as integers
codepoints.map{|codepoint| codepoint.to_i(16)}
end
# The tests contain a solitary U+D800 <Non Private Use High
# Surrogate, First> character, which Ruby does not allow to stand
# alone in a UTF-8 string. So we'll just skip it.
next if clusters.flatten.include?(0xd800)
clusters << comment.strip
yield(*clusters)
end
end
end
end