twitter_cldr 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +32 -0
- data/History.txt +78 -0
- data/README.md +72 -62
- data/Rakefile +22 -0
- data/js/lib/compiler.rb +40 -0
- data/js/lib/mustache/bundle.coffee +14 -0
- data/js/lib/mustache/calendars/datetime.coffee +240 -0
- data/js/lib/mustache/calendars/timespan.coffee +52 -0
- data/js/lib/mustache/plurals/rules.coffee +14 -0
- data/js/lib/renderers/base.rb +18 -0
- data/js/lib/renderers/bundle.rb +18 -0
- data/js/lib/renderers/calendars/datetime_renderer.rb +34 -0
- data/js/lib/renderers/calendars/timespan_renderer.rb +39 -0
- data/js/lib/renderers/plurals/rules/plural_rules_compiler.rb +89 -0
- data/js/lib/renderers/plurals/rules/plural_rules_renderer.rb +26 -0
- data/js/lib/twitter_cldr_js.rb +85 -0
- data/js/spec/js/calendars/datetime_spec.js +418 -0
- data/js/spec/js/calendars/timespan_spec.js +91 -0
- data/js/spec/js/plurals/plural_rules_spec.js +28 -0
- data/js/spec/js/support/jasmine.yml +8 -0
- data/js/spec/rb/renderers/plurals/plural_rules_compiler_spec.rb +52 -0
- data/js/spec/rb/spec_helper.rb +13 -0
- data/lib/twitter_cldr.rb +2 -1
- data/lib/twitter_cldr/collation.rb +2 -1
- data/lib/twitter_cldr/collation/collator.rb +49 -31
- data/lib/twitter_cldr/collation/{sort_key.rb → sort_key_builder.rb} +31 -8
- data/lib/twitter_cldr/collation/trie.rb +116 -24
- data/lib/twitter_cldr/collation/trie_builder.rb +54 -28
- data/lib/twitter_cldr/collation/trie_with_fallback.rb +55 -0
- data/lib/twitter_cldr/core_ext/array.rb +14 -1
- data/lib/twitter_cldr/core_ext/calendars/datetime.rb +8 -2
- data/lib/twitter_cldr/core_ext/calendars/timespan.rb +5 -5
- data/lib/twitter_cldr/formatters/calendars/timespan_formatter.rb +10 -10
- data/lib/twitter_cldr/formatters/plurals/rules.rb +3 -5
- data/lib/twitter_cldr/resources.rb +11 -0
- data/lib/twitter_cldr/resources/import.rb +12 -0
- data/lib/twitter_cldr/resources/import/tailoring.rb +193 -0
- data/lib/twitter_cldr/{shared/resources.rb → resources/loader.rb} +17 -4
- data/lib/twitter_cldr/shared.rb +0 -1
- data/lib/twitter_cldr/tokenizers/base.rb +9 -9
- data/lib/twitter_cldr/tokenizers/calendars/datetime_tokenizer.rb +0 -4
- data/lib/twitter_cldr/tokenizers/calendars/timespan_tokenizer.rb +21 -7
- data/lib/twitter_cldr/utils.rb +11 -0
- data/lib/twitter_cldr/version.rb +1 -1
- data/resources/collation/tailoring/af.yml +3 -0
- data/resources/collation/tailoring/ar.yml +21 -0
- data/resources/collation/tailoring/ca.yml +9 -0
- data/resources/collation/tailoring/cs.yml +25 -0
- data/resources/collation/tailoring/da.yml +59 -0
- data/resources/collation/tailoring/de.yml +3 -0
- data/resources/collation/tailoring/el.yml +3 -0
- data/resources/collation/tailoring/en.yml +3 -0
- data/resources/collation/tailoring/es.yml +5 -0
- data/resources/collation/tailoring/eu.yml +3 -0
- data/resources/collation/tailoring/fa.yml +73 -0
- data/resources/collation/tailoring/fi.yml +61 -0
- data/resources/collation/tailoring/fil.yml +11 -0
- data/resources/collation/tailoring/fr.yml +3 -0
- data/resources/collation/tailoring/he.yml +3 -0
- data/resources/collation/tailoring/hi.yml +7 -0
- data/resources/collation/tailoring/hu.yml +125 -0
- data/resources/collation/tailoring/id.yml +3 -0
- data/resources/collation/tailoring/it.yml +3 -0
- data/resources/collation/tailoring/ja.yml +14647 -0
- data/resources/collation/tailoring/ko.yml +14953 -0
- data/resources/collation/tailoring/ms.yml +3 -0
- data/resources/collation/tailoring/nb.yml +59 -0
- data/resources/collation/tailoring/nl.yml +3 -0
- data/resources/collation/tailoring/pl.yml +37 -0
- data/resources/collation/tailoring/pt.yml +3 -0
- data/resources/collation/tailoring/ru.yml +3 -0
- data/resources/collation/tailoring/sv.yml +63 -0
- data/resources/collation/tailoring/th.yml +19 -0
- data/resources/collation/tailoring/tr.yml +27 -0
- data/resources/collation/tailoring/uk.yml +5 -0
- data/resources/collation/tailoring/ur.yml +163 -0
- data/resources/collation/tailoring/zh-Hant.yml +3 -0
- data/resources/collation/tailoring/zh.yml +149 -0
- data/resources/custom/locales/af/units.yml +19 -0
- data/resources/custom/locales/ar/units.yml +35 -0
- data/resources/custom/locales/ca/units.yml +19 -0
- data/resources/custom/locales/cs/units.yml +23 -0
- data/resources/custom/locales/da/units.yml +19 -0
- data/resources/custom/locales/de/units.yml +19 -0
- data/resources/custom/locales/el/units.yml +19 -0
- data/resources/custom/locales/en/units.yml +18 -0
- data/resources/custom/locales/es/units.yml +19 -0
- data/resources/custom/locales/eu/units.yml +19 -0
- data/resources/custom/locales/fa/units.yml +15 -0
- data/resources/custom/locales/fi/units.yml +19 -0
- data/resources/custom/locales/fil/units.yml +19 -0
- data/resources/custom/locales/fr/units.yml +19 -0
- data/resources/custom/locales/he/units.yml +19 -0
- data/resources/custom/locales/hi/units.yml +19 -0
- data/resources/custom/locales/hu/units.yml +15 -0
- data/resources/custom/locales/id/units.yml +15 -0
- data/resources/custom/locales/it/units.yml +19 -0
- data/resources/custom/locales/ja/units.yml +15 -0
- data/resources/custom/locales/ko/units.yml +15 -0
- data/resources/custom/locales/ms/units.yml +15 -0
- data/resources/custom/locales/nb/units.yml +19 -0
- data/resources/custom/locales/nl/units.yml +19 -0
- data/resources/custom/locales/pl/units.yml +23 -0
- data/resources/custom/locales/pt/units.yml +19 -0
- data/resources/custom/locales/ru/units.yml +27 -0
- data/resources/custom/locales/sv/units.yml +19 -0
- data/resources/custom/locales/th/units.yml +15 -0
- data/resources/custom/locales/tr/units.yml +15 -0
- data/resources/custom/locales/uk/units.yml +27 -0
- data/resources/custom/locales/ur/units.yml +19 -0
- data/resources/custom/locales/zh-Hant/units.yml +15 -0
- data/resources/custom/locales/zh/units.yml +15 -0
- data/resources/locales/af/units.yml +112 -65
- data/resources/locales/ar/units.yml +196 -126
- data/resources/locales/ca/units.yml +112 -70
- data/resources/locales/cs/units.yml +140 -91
- data/resources/locales/da/units.yml +98 -56
- data/resources/locales/de/units.yml +112 -70
- data/resources/locales/el/units.yml +119 -84
- data/resources/locales/en/units.yml +84 -42
- data/resources/locales/es/units.yml +112 -70
- data/resources/locales/eu/units.yml +105 -68
- data/resources/locales/fa/units.yml +98 -63
- data/resources/locales/fi/units.yml +112 -70
- data/resources/locales/fil/units.yml +98 -56
- data/resources/locales/fr/units.yml +112 -70
- data/resources/locales/he/units.yml +98 -56
- data/resources/locales/hi/units.yml +98 -56
- data/resources/locales/hu/units.yml +84 -49
- data/resources/locales/id/units.yml +84 -49
- data/resources/locales/it/units.yml +98 -56
- data/resources/locales/ja/units.yml +84 -49
- data/resources/locales/ko/units.yml +84 -49
- data/resources/locales/ms/units.yml +112 -63
- data/resources/locales/nb/units.yml +106 -64
- data/resources/locales/nl/units.yml +98 -56
- data/resources/locales/pl/units.yml +181 -112
- data/resources/locales/pt/units.yml +112 -70
- data/resources/locales/ru/units.yml +168 -112
- data/resources/locales/sv/units.yml +112 -70
- data/resources/locales/th/units.yml +84 -49
- data/resources/locales/tr/units.yml +84 -49
- data/resources/locales/uk/units.yml +168 -112
- data/resources/locales/ur/units.yml +112 -63
- data/resources/locales/zh-Hant/units.yml +84 -49
- data/resources/locales/zh/units.yml +84 -49
- data/spec/collation/collation_spec.rb +1 -1
- data/spec/collation/collator_spec.rb +120 -48
- data/spec/collation/sort_key_builder_spec.rb +80 -0
- data/spec/collation/tailoring_spec.rb +137 -0
- data/spec/collation/tailoring_tests/af.txt +321 -0
- data/spec/collation/tailoring_tests/ar.txt +188 -0
- data/spec/collation/tailoring_tests/ca.txt +446 -0
- data/spec/collation/tailoring_tests/cs.txt +273 -0
- data/spec/collation/tailoring_tests/da.txt +293 -0
- data/spec/collation/tailoring_tests/de.txt +414 -0
- data/spec/collation/tailoring_tests/el.txt +228 -0
- data/spec/collation/tailoring_tests/en.txt +399 -0
- data/spec/collation/tailoring_tests/es.txt +402 -0
- data/spec/collation/tailoring_tests/eu.txt +183 -0
- data/spec/collation/tailoring_tests/fa.txt +263 -0
- data/spec/collation/tailoring_tests/fi.txt +389 -0
- data/spec/collation/tailoring_tests/fil.txt +279 -0
- data/spec/collation/tailoring_tests/fr.txt +363 -0
- data/spec/collation/tailoring_tests/he.txt +167 -0
- data/spec/collation/tailoring_tests/hi.txt +230 -0
- data/spec/collation/tailoring_tests/hu.txt +773 -0
- data/spec/collation/tailoring_tests/id.txt +171 -0
- data/spec/collation/tailoring_tests/it.txt +231 -0
- data/spec/collation/tailoring_tests/ja.txt +4287 -0
- data/spec/collation/tailoring_tests/ko.txt +1761 -0
- data/spec/collation/tailoring_tests/ms.txt +531 -0
- data/spec/collation/tailoring_tests/nb.txt +375 -0
- data/spec/collation/tailoring_tests/nl.txt +273 -0
- data/spec/collation/tailoring_tests/pl.txt +225 -0
- data/spec/collation/tailoring_tests/pt.txt +405 -0
- data/spec/collation/tailoring_tests/ru.txt +213 -0
- data/spec/collation/tailoring_tests/sv.txt +353 -0
- data/spec/collation/tailoring_tests/th.txt +239 -0
- data/spec/collation/tailoring_tests/tr.txt +414 -0
- data/spec/collation/tailoring_tests/uk.txt +218 -0
- data/spec/collation/tailoring_tests/ur.txt +284 -0
- data/spec/collation/tailoring_tests/zh-Hant.txt +626 -0
- data/spec/collation/tailoring_tests/zh.txt +717 -0
- data/spec/collation/trie_builder_spec.rb +131 -51
- data/spec/collation/trie_spec.rb +301 -26
- data/spec/collation/trie_with_fallback_spec.rb +41 -0
- data/spec/core_ext/array_spec.rb +46 -3
- data/spec/core_ext/calendars/date_spec.rb +24 -24
- data/spec/core_ext/calendars/datetime_spec.rb +7 -0
- data/spec/core_ext/calendars/time_spec.rb +2 -2
- data/spec/formatters/calendars/timespan_formatter_spec.rb +47 -18
- data/spec/formatters/plurals/rules_spec.rb +3 -11
- data/spec/readme_spec.rb +15 -15
- data/spec/resources/loader_spec.rb +94 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/tokenizers/calendars/timespan_tokenizer_spec.rb +1 -1
- data/spec/twitter_cldr_spec.rb +3 -3
- data/spec/utils_spec.rb +38 -0
- data/twitter_cldr.gemspec +25 -0
- metadata +156 -110
- data/spec/collation/sort_key_spec.rb +0 -56
- data/spec/shared/resources_spec.rb +0 -75
@@ -9,41 +9,28 @@ include TwitterCldr::Collation
|
|
9
9
|
|
10
10
|
describe TrieBuilder do
|
11
11
|
|
12
|
-
describe '#
|
13
|
-
|
14
|
-
|
15
|
-
builder = TrieBuilder.new('resource')
|
16
|
-
stub(builder).load_collation_elements_table { FRACTIONAL_UCA_SHORT_STUB }
|
17
|
-
builder
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'returns a trie' do
|
21
|
-
trie_builder.is_a?(Trie)
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'adds every collation element from the FractionalUCA_SHORT.txt file to the trie' do
|
25
|
-
mock(Trie).new { TrieStub.new }
|
26
|
-
|
27
|
-
trie_builder.build.storage.should == COLLATION_ELEMENTS_TABLE
|
28
|
-
end
|
12
|
+
describe '#parse_trie' do
|
13
|
+
it 'returns a trie' do
|
14
|
+
TrieBuilder.parse_trie(fractional_uca_short_stub).should be_instance_of(Trie)
|
29
15
|
end
|
30
|
-
end
|
31
16
|
|
32
|
-
|
17
|
+
it 'adds every collation element from the FCE table to the trie' do
|
18
|
+
trie = Object.new
|
19
|
+
mock(Trie).new { trie }
|
20
|
+
collation_elements_table.each { |code_points, collation_elements| mock(trie).set(code_points, collation_elements) }
|
33
21
|
|
34
|
-
|
35
|
-
|
22
|
+
TrieBuilder.parse_trie(fractional_uca_short_stub).should == trie
|
23
|
+
end
|
36
24
|
|
37
|
-
|
38
|
-
|
39
|
-
|
25
|
+
it 'populates the trie that is passed as an argument' do
|
26
|
+
trie = Object.new
|
27
|
+
collation_elements_table.each { |code_points, collation_elements| mock(trie).set(code_points, collation_elements) }
|
40
28
|
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
29
|
+
TrieBuilder.parse_trie(fractional_uca_short_stub, trie).should == trie
|
30
|
+
end
|
45
31
|
|
46
|
-
|
32
|
+
let(:fractional_uca_short_stub) do
|
33
|
+
<<END
|
47
34
|
# Fractional UCA Table, generated from standard UCA
|
48
35
|
# 2012-01-03, 21:52:55 GMT [MD]
|
49
36
|
# VERSION: UCA=6.1.0, UCD=6.1.0
|
@@ -80,35 +67,128 @@ FFFF; [EF FE, 05, 05] # Special HIGHEST primary, for ranges
|
|
80
67
|
[first tertiary in secondary non-ignorable [X, X, 05]] # U+0332 COMBINING LOW LINE
|
81
68
|
[last tertiary in secondary non-ignorable [X, X, 3D]] # U+2A74 DOUBLE COLON EQUAL
|
82
69
|
END
|
70
|
+
end
|
83
71
|
|
84
|
-
|
85
|
-
|
86
|
-
|
72
|
+
let(:collation_elements_table) do
|
73
|
+
[
|
74
|
+
# 0000; [,,]
|
75
|
+
[[0], [[0, 0, 0]]],
|
87
76
|
|
88
|
-
|
89
|
-
|
77
|
+
# 030C; [, 97, 05]
|
78
|
+
[[780], [[0, 151, 5]]],
|
90
79
|
|
91
|
-
|
92
|
-
|
80
|
+
# 215E; [20, 05, 3B][0D 75 2C, 05, 3B][22, 05, 3D]
|
81
|
+
[[8542], [[32, 5, 59], [881964, 5, 59], [34, 5, 61]]],
|
93
82
|
|
94
|
-
|
95
|
-
|
83
|
+
# FC63; [, D3 A9, 33][, D5 11, 33]
|
84
|
+
[[64611], [[0, 54185, 51], [0, 54545, 51]]],
|
96
85
|
|
97
|
-
|
98
|
-
|
86
|
+
# 0E40 0E01; [72 0A, 05, 05][72 7E, 05, 3D]
|
87
|
+
[[3648, 3585], [[29194, 5, 5], [29310, 5, 61]]],
|
99
88
|
|
100
|
-
|
101
|
-
|
89
|
+
# 0E40 0E02; [72 0C, 05, 05][72 7E, 05, 3D]
|
90
|
+
[[3648, 3586], [[29196, 5, 5], [29310, 5, 61]]],
|
102
91
|
|
103
|
-
|
104
|
-
|
92
|
+
# FDD0 0063; [, 97, 3D]
|
93
|
+
[[64976, 99], [[0, 151, 61]]],
|
105
94
|
|
106
|
-
|
107
|
-
|
95
|
+
# FDD0 0064; [, A7, 09]
|
96
|
+
[[64976, 100], [[0, 167, 9]]],
|
97
|
+
|
98
|
+
# FFFE; [02, 02, 02]
|
99
|
+
[[65534], [[2, 2, 2]]],
|
100
|
+
|
101
|
+
# FFFF; [EF FE, 05, 05]
|
102
|
+
[[65535], [[61438, 5, 5]]]
|
103
|
+
]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#load_trie' do
|
108
|
+
it 'load FCE table from the resource into a trie' do
|
109
|
+
mock(TrieBuilder).parse_trie('fce-table') { 'trie' }
|
110
|
+
mock(TrieBuilder).load_resource('resource') { 'fce-table' }
|
111
|
+
|
112
|
+
TrieBuilder.load_trie('resource').should == 'trie'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#load_tailored_trie' do
|
117
|
+
let(:locale) { :xxx }
|
118
|
+
let(:fallback) { TrieBuilder.parse_trie(fractional_uca_short_stub) }
|
119
|
+
let(:tailored_trie) { TrieBuilder.load_tailored_trie(locale, fallback) }
|
120
|
+
|
121
|
+
before(:each) { mock(TwitterCldr).get_resource(:collation, :tailoring, locale) { YAML.load(tailoring_resource_stub) } }
|
122
|
+
|
123
|
+
it 'returns a TrieWithFallback' do
|
124
|
+
tailored_trie.should be_instance_of(TrieWithFallback)
|
125
|
+
end
|
108
126
|
|
109
|
-
|
110
|
-
|
127
|
+
it 'tailors elements in the trie' do
|
128
|
+
fallback.get([0x0491]).should == [[0x5C1A, 5, 9], [0, 0xDBB9, 9]]
|
129
|
+
fallback.get([0x0490]).should == [[0x5C1A, 5, 0x93], [0, 0xDBB9, 9]]
|
130
|
+
|
131
|
+
tailored_trie.get([0x0491]).should == [[0x5C1B, 5, 5]]
|
132
|
+
tailored_trie.get([0x0490]).should == [[0x5C1B, 5, 0x86]]
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'makes contractions available in the tailored trie' do
|
136
|
+
tailored_trie.get([0x491, 0x306]).should == [[0x5C, 0xDB, 9]]
|
137
|
+
tailored_trie.get([0x415, 0x306]).should == [[0x5C36, 5, 0x8F]]
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'suppresses required contractions' do
|
141
|
+
fallback.find_prefix([0x41A, 0x301]).first(2).should == [[[0x5CCC, 5, 0x8F]], 2]
|
142
|
+
fallback.find_prefix([0x413, 0x301]).first(2).should == [[[0x5C30, 5, 0x8F]], 2]
|
143
|
+
|
144
|
+
tailored_trie.find_prefix([0x41A, 0x301]).first(2).should == [[[0x5C6C, 5, 0x8F]], 1]
|
145
|
+
tailored_trie.find_prefix([0x413, 0x301]).first(2).should == [[[0x5C1A, 5, 0x8F]], 1]
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'do not copy other collation elements from the fallback' do
|
149
|
+
%w[0301 0306 041A 0413 0415].each do |code_point|
|
150
|
+
code_points = [code_point.to_i(16)]
|
151
|
+
|
152
|
+
tailored_trie.get(code_points).should_not be_nil
|
153
|
+
tailored_trie.get(code_points).object_id.should == fallback.get(code_points).object_id
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
let(:fractional_uca_short_stub) do
|
158
|
+
<<END
|
159
|
+
# collation elements from default FCE table
|
160
|
+
0301; [, 8D, 05]
|
161
|
+
0306; [, 91, 05]
|
162
|
+
041A; [5C 6C, 05, 8F] # К
|
163
|
+
0413; [5C 1A, 05, 8F] # Г
|
164
|
+
0415; [5C 34, 05, 8F] # Е
|
165
|
+
|
166
|
+
# tailored (in UK locale) with "Г < ґ <<< Ґ"
|
167
|
+
0491; [5C 1A, 05, 09][, DB B9, 09] # ґ
|
168
|
+
0490; [5C 1A, 05, 93][, DB B9, 09] # Ґ
|
169
|
+
|
170
|
+
# contraction for a tailored collation element
|
171
|
+
0491 0306; [5C, DB, 09] # ґ̆
|
172
|
+
|
173
|
+
# contractions suppressed in tailoring (for RU locale)
|
174
|
+
041A 0301; [5C CC, 05, 8F] # Ќ
|
175
|
+
0413 0301; [5C 30, 05, 8F] # Ѓ
|
176
|
+
|
177
|
+
# contractions non-suppressed in tailoring
|
178
|
+
0415 0306; [5C 36, 05, 8F] # Ӗ
|
179
|
+
END
|
180
|
+
end
|
181
|
+
|
182
|
+
let(:tailoring_resource_stub) do
|
183
|
+
<<END
|
184
|
+
---
|
185
|
+
:tailored_table: ! '0491; [5C1B, 5, 5]
|
186
|
+
|
187
|
+
0490; [5C1B, 5, 86]'
|
188
|
+
:suppressed_contractions: ГК
|
189
|
+
...
|
190
|
+
END
|
191
|
+
end
|
192
|
+
end
|
111
193
|
|
112
|
-
|
113
|
-
[[65535], [[61438, 5, 5]]]
|
114
|
-
]
|
194
|
+
end
|
data/spec/collation/trie_spec.rb
CHANGED
@@ -15,16 +15,67 @@ describe Trie do
|
|
15
15
|
[
|
16
16
|
[[1], '1' ],
|
17
17
|
[[1, 4], '14' ],
|
18
|
-
[[1, 5], '15' ],
|
19
18
|
[[1, 4, 8], '148'],
|
19
|
+
[[1, 5], '15' ],
|
20
20
|
[[2], '2' ],
|
21
21
|
[[2, 7, 5], '275'],
|
22
|
-
[[3, 9],
|
22
|
+
[[3, 9, 2], '392'],
|
23
|
+
[[4], '4' ]
|
23
24
|
]
|
24
25
|
end
|
25
26
|
|
26
|
-
before(:each)
|
27
|
-
|
27
|
+
before(:each) { values.each { |key, value| trie.add(key, value) } }
|
28
|
+
|
29
|
+
describe '#initialize' do
|
30
|
+
it 'initializes an empty trie by default' do
|
31
|
+
Trie.new.should be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'initializes with a root node' do
|
35
|
+
trie = Trie.new(Trie::Node.new(nil, 1 => Trie::Node.new(nil, { 2 => Trie::Node.new('12')}), 2 => Trie::Node.new('2')))
|
36
|
+
|
37
|
+
trie.to_hash.should == {
|
38
|
+
1 => [nil, { 2 => ['12', {}] }],
|
39
|
+
2 => ['2', {}]
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#lock and #locked?' do
|
45
|
+
it 'trie is unlocked by default' do
|
46
|
+
trie.should_not be_locked
|
47
|
+
end
|
48
|
+
|
49
|
+
it '#lock locks the trie' do
|
50
|
+
trie.lock
|
51
|
+
trie.should be_locked
|
52
|
+
end
|
53
|
+
|
54
|
+
it '#lock returns the trie' do
|
55
|
+
trie.lock.should == trie
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#starters' do
|
60
|
+
it 'returns all unique first elements of the keys in the trie' do
|
61
|
+
trie.starters.should =~ [1, 2, 3, 4]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#each_starting_with' do
|
66
|
+
it 'iterates over all key-value pairs for which key starts with a given value' do
|
67
|
+
res = {}
|
68
|
+
trie.each_starting_with(1) { |k, v| res[k] = v }
|
69
|
+
|
70
|
+
res.should == { [1] => '1', [1, 4] => '14', [1, 5] => '15', [1, 4, 8] => '148' }
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'works when argument is not a starter' do
|
74
|
+
res = {}
|
75
|
+
trie.each_starting_with(42) { |k, v| res[k] = v }
|
76
|
+
|
77
|
+
res.should == {}
|
78
|
+
end
|
28
79
|
end
|
29
80
|
|
30
81
|
describe '#get' do
|
@@ -32,29 +83,69 @@ describe Trie do
|
|
32
83
|
[[6], [3], [1, 4, 3], [2, 7, 5, 6, 9]].each { |key| trie.get(key).should be_nil }
|
33
84
|
end
|
34
85
|
|
35
|
-
it 'returns value
|
86
|
+
it 'returns value for each existing key' do
|
36
87
|
values.each { |key, value| trie.get(key).should == value }
|
37
88
|
end
|
38
89
|
end
|
39
90
|
|
40
91
|
describe '#add' do
|
41
|
-
it '
|
92
|
+
it 'does not override values' do
|
42
93
|
trie.get([1, 4]).should == '14'
|
43
94
|
|
44
95
|
trie.add([1, 4], '14-new')
|
96
|
+
trie.get([1, 4]).should == '14'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'adds new values' do
|
100
|
+
trie.get([1, 9]).should be_nil
|
101
|
+
|
102
|
+
trie.add([1, 9], '19')
|
103
|
+
trie.get([1, 9]).should == '19'
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'raises RuntimeError if called on a locked trie' do
|
107
|
+
lambda { trie.lock.add([1, 3], 'value') }.should raise_error(RuntimeError)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#set' do
|
112
|
+
it 'overrides values' do
|
113
|
+
trie.get([1, 4]).should == '14'
|
114
|
+
|
115
|
+
trie.set([1, 4], '14-new')
|
45
116
|
trie.get([1, 4]).should == '14-new'
|
46
117
|
end
|
118
|
+
|
119
|
+
it 'adds new values' do
|
120
|
+
trie.get([1, 9]).should be_nil
|
121
|
+
|
122
|
+
trie.set([1, 9], '19')
|
123
|
+
trie.get([1, 9]).should == '19'
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'raises RuntimeError if called on a locked trie' do
|
127
|
+
lambda { trie.lock.set([1, 3], 'value') }.should raise_error(RuntimeError)
|
128
|
+
end
|
47
129
|
end
|
48
130
|
|
49
131
|
describe '#find_prefix' do
|
50
|
-
|
51
|
-
|
52
|
-
|
132
|
+
let(:root_subtrie) {
|
133
|
+
{
|
134
|
+
1 => ['1', { 4 => ['14', { 8 => ['148', {}] }], 5 => ['15', {}] }],
|
135
|
+
2 => ['2', { 7 => [nil, { 5 => ['275', {}] }] }],
|
136
|
+
3 => [nil, { 9 => [nil, { 2 => ['392', {}] }] }],
|
137
|
+
4 => ['4', {}]
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
describe 'first two elements of the returned array (value and prefix size)' do
|
142
|
+
it 'are nil and 0 if the prefix was not found' do
|
143
|
+
trie.find_prefix([42]).first(2).should == [nil, 0]
|
53
144
|
end
|
54
145
|
|
55
|
-
it 'stored value and key size
|
146
|
+
it 'are the stored value and the key size if the whole key was found' do
|
56
147
|
values.each do |key, value|
|
57
|
-
|
148
|
+
trie.find_prefix(key).first(2).should == [value, key.size]
|
58
149
|
end
|
59
150
|
end
|
60
151
|
|
@@ -66,32 +157,216 @@ describe Trie do
|
|
66
157
|
[2, 7, 5, 5] => ['275', 3]
|
67
158
|
}
|
68
159
|
|
69
|
-
tests.each
|
160
|
+
tests.each do |key, result|
|
161
|
+
trie.find_prefix(key).first(2).should == result
|
162
|
+
end
|
70
163
|
end
|
71
164
|
|
72
165
|
def test_find_prefix(trie, key, value, size = key.size)
|
73
|
-
|
74
|
-
|
75
|
-
result[0].should == value
|
76
|
-
result[2].should == size
|
166
|
+
trie.find_prefix(key).first(2).should == [value, size]
|
77
167
|
end
|
78
168
|
end
|
79
169
|
|
80
|
-
describe '
|
81
|
-
|
82
|
-
|
83
|
-
|
170
|
+
describe 'last element of the returned array (suffixes subtrie)' do
|
171
|
+
let(:non_existing_key) { [5, 2, 7] }
|
172
|
+
let(:key_with_suffixes) { [2] }
|
173
|
+
let(:key_without_suffixes) { [1, 4, 8] }
|
174
|
+
|
175
|
+
it 'is always a locked trie' do
|
176
|
+
[trie, trie.lock].each do |some_trie|
|
177
|
+
[non_existing_key, key_with_suffixes, key_without_suffixes].each do |key|
|
178
|
+
some_trie.find_prefix(key).last.should be_locked
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'is a locked empty subtrie if the prefix that was found does not have any suffixes' do
|
184
|
+
trie.find_prefix(key_without_suffixes).last.to_hash.should be_empty
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'is a subtrie of possible suffixes for the prefix that was found' do
|
188
|
+
trie.find_prefix(key_with_suffixes).last.to_hash.should == { 7 => [nil, { 5 => ["275", {}] }] }
|
84
189
|
end
|
85
190
|
|
86
191
|
it 'is a hash representing the whole trie if the prefix was not found' do
|
87
|
-
trie.
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
192
|
+
trie.get(non_existing_key).should be_nil
|
193
|
+
|
194
|
+
trie.find_prefix(non_existing_key).last.to_hash.should == root_subtrie
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
context 'argument does not match any value, but is a prefix of a longer key' do
|
200
|
+
context 'argument has a shorter key as a prefix' do
|
201
|
+
it 'returns value for the key, its size and suffixes subtrie' do
|
202
|
+
trie.get([2]).should_not be_nil
|
203
|
+
trie.get([2, 7, 5]).should_not be_nil
|
204
|
+
|
205
|
+
result = trie.find_prefix([2, 7])
|
206
|
+
|
207
|
+
result.first(2).should == ['2', 1]
|
208
|
+
result.last.to_hash.should == { 7 => [nil, { 5 => ["275", {}] }] }
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'argument does not have a shorter key as a prefix' do
|
213
|
+
it 'returns nil, 0 and suffixes subtrie for the root node' do
|
214
|
+
trie.get([3]).should be_nil
|
215
|
+
trie.get([3, 9]).should be_nil
|
216
|
+
trie.get([3, 9, 2]).should_not be_nil
|
217
|
+
|
218
|
+
result = trie.find_prefix([3, 9])
|
219
|
+
|
220
|
+
result.first(2).should == [nil, 0]
|
221
|
+
result.last.to_hash.should == root_subtrie
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
describe Trie::Node do
|
229
|
+
|
230
|
+
let(:node) { Trie::Node.new }
|
231
|
+
let(:child) { Trie::Node.new('child') }
|
232
|
+
let(:another_child) { Trie::Node.new('another-child') }
|
233
|
+
|
234
|
+
let(:root_node) do
|
235
|
+
Trie::Node.new(
|
236
|
+
'node-0',
|
237
|
+
1 => Trie::Node.new(
|
238
|
+
'node-1',
|
239
|
+
1 => Trie::Node.new('node-11'),
|
240
|
+
2 => Trie::Node.new('node-12')
|
241
|
+
),
|
242
|
+
2 => Trie::Node.new(
|
243
|
+
'node-2',
|
244
|
+
1 => Trie::Node.new(
|
245
|
+
'node-21',
|
246
|
+
1 => Trie::Node.new('node-211')
|
247
|
+
)
|
248
|
+
)
|
249
|
+
)
|
250
|
+
end
|
251
|
+
|
252
|
+
let(:subtrie_hash) do
|
253
|
+
{
|
254
|
+
1 => [
|
255
|
+
'node-1',
|
256
|
+
{
|
257
|
+
1 => ['node-11', {}],
|
258
|
+
2 => ['node-12', {}]
|
259
|
+
}
|
260
|
+
],
|
261
|
+
2 => [
|
262
|
+
'node-2',
|
263
|
+
{
|
264
|
+
1 => [
|
265
|
+
'node-21',
|
266
|
+
{
|
267
|
+
1 => ['node-211', {}]
|
268
|
+
}
|
269
|
+
]
|
270
|
+
}
|
271
|
+
]
|
272
|
+
}
|
273
|
+
end
|
274
|
+
|
275
|
+
describe '#initialize' do
|
276
|
+
it 'initializes node with nil value and empty children hash by default' do
|
277
|
+
node.value.should be_nil
|
278
|
+
node.should_not have_children
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'initializes node with provided value and children hash' do
|
282
|
+
root_node.value.should == 'node-0'
|
283
|
+
root_node.should have_children
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe '#child and #set_child' do
|
288
|
+
it '#child returns nil if a child with a given key does not exist' do
|
289
|
+
node.child(42).should be_nil
|
290
|
+
end
|
291
|
+
|
292
|
+
it '#set_child saves a child by key and #child returns the child by key' do
|
293
|
+
node.set_child(42, child)
|
294
|
+
node.child(42).should == child
|
295
|
+
end
|
296
|
+
|
297
|
+
it '#set_child overrides a child by key' do
|
298
|
+
node.set_child(42, child)
|
299
|
+
node.set_child(42, another_child)
|
300
|
+
|
301
|
+
node.child(42).should_not == child
|
302
|
+
node.child(42).should == another_child
|
303
|
+
end
|
304
|
+
|
305
|
+
it '#set_child returns the child that was saved' do
|
306
|
+
node.set_child(42, child).should == child
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
describe '#each_key_and_child' do
|
311
|
+
it 'iterates over all (key, child) pairs' do
|
312
|
+
node.set_child(42, child)
|
313
|
+
node.set_child(13, another_child)
|
314
|
+
res = {}
|
315
|
+
node.each_key_and_child { |key, child| res[key] = child }
|
316
|
+
|
317
|
+
res.should == { 42 => child, 13 => another_child }
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe '#keys' do
|
322
|
+
it 'returns all children keys' do
|
323
|
+
node.set_child(42, child)
|
324
|
+
node.set_child(13, another_child)
|
325
|
+
|
326
|
+
node.keys.should =~ [13, 42]
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
describe '#has_children?' do
|
331
|
+
it 'returns false if the node has no children' do
|
332
|
+
node.should_not have_children
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'returns true if the node has children' do
|
336
|
+
node.set_child(42, child)
|
337
|
+
node.should have_children
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
describe '#to_trie' do
|
342
|
+
it 'returns a trie' do
|
343
|
+
node.to_trie.should be_instance_of(Trie)
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'returns a locked trie' do
|
347
|
+
node.to_trie.should be_locked
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'current node is a root of a new trie' do
|
351
|
+
root_node.to_trie.to_hash.should == subtrie_hash
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'sets new trie root value to nil' do
|
355
|
+
root_node.value.should_not be_nil
|
356
|
+
root_node.to_trie.instance_variable_get(:@root).value.should be_nil
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
describe '#subtrie_hash' do
|
361
|
+
it 'returns an empty hash if the node has no children' do
|
362
|
+
node.subtrie_hash.should == {}
|
363
|
+
end
|
364
|
+
|
365
|
+
it 'returns a nested hash of children values' do
|
366
|
+
root_node.subtrie_hash.should == subtrie_hash
|
92
367
|
end
|
93
368
|
end
|
94
369
|
|
95
370
|
end
|
96
371
|
|
97
|
-
end
|
372
|
+
end
|