rambling-trie 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rspec +0 -0
- data/.travis.yml +0 -0
- data/Gemfile +2 -0
- data/Guardfile +1 -1
- data/LICENSE +0 -0
- data/README.markdown +36 -13
- data/Rakefile +0 -0
- data/assets/dictionaries/words_with_friends.txt +0 -0
- data/lib/rambling-trie.rb +0 -0
- data/lib/rambling/trie.rb +14 -19
- data/lib/rambling/trie/branches.rb +14 -21
- data/lib/rambling/trie/children_hash_deferer.rb +0 -0
- data/lib/rambling/trie/compressor.rb +3 -3
- data/lib/rambling/trie/enumerable.rb +0 -0
- data/lib/rambling/trie/inspector.rb +10 -0
- data/lib/rambling/trie/invalid_operation.rb +0 -0
- data/lib/rambling/trie/node.rb +6 -5
- data/lib/rambling/trie/root.rb +19 -21
- data/lib/rambling/trie/tasks/gem.rb +0 -0
- data/lib/rambling/trie/tasks/performance.rb +11 -11
- data/lib/rambling/trie/version.rb +1 -1
- data/rambling-trie.gemspec +9 -9
- data/spec/assets/test_words.txt +0 -0
- data/spec/lib/rambling/trie/branches_spec.rb +18 -17
- data/spec/lib/rambling/trie/children_hash_deferer_spec.rb +3 -3
- data/spec/lib/rambling/trie/enumerable_spec.rb +8 -8
- data/spec/lib/rambling/trie/inspector_spec.rb +22 -0
- data/spec/lib/rambling/trie/node_spec.rb +58 -34
- data/spec/lib/rambling/trie/root_spec.rb +150 -139
- data/spec/lib/rambling/trie_spec.rb +17 -17
- data/spec/spec_helper.rb +7 -1
- metadata +26 -15
data/rambling-trie.gemspec
CHANGED
@@ -8,20 +8,20 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.description = 'The Rambling Trie is a custom implementation of the Trie data structure with Ruby, which includes compression abilities and is designed to be very fast to traverse.'
|
9
9
|
gem.summary = 'A custom implementation of the trie data structure.'
|
10
10
|
gem.homepage = 'http://github.com/ramblinglabs/rambling-trie'
|
11
|
-
gem.date = Time.now.strftime
|
11
|
+
gem.date = Time.now.strftime '%Y-%m-%d'
|
12
12
|
|
13
|
-
gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename
|
14
|
-
gem.files = `git ls-files`.split
|
15
|
-
gem.test_files = `git ls-files -- {test,spec,features}/*`.split
|
13
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename f }
|
14
|
+
gem.files = `git ls-files`.split "\n"
|
15
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
|
16
16
|
gem.require_paths = ['lib']
|
17
17
|
|
18
18
|
gem.name = 'rambling-trie'
|
19
19
|
gem.version = Rambling::Trie::VERSION
|
20
20
|
gem.platform = Gem::Platform::RUBY
|
21
21
|
|
22
|
-
gem.add_development_dependency 'rspec', '>=2.
|
23
|
-
gem.add_development_dependency 'rake', '>=0.
|
24
|
-
gem.add_development_dependency 'ruby-prof', '>=0.
|
25
|
-
gem.add_development_dependency 'yard', '>=0.
|
26
|
-
gem.add_development_dependency 'redcarpet', '>=2.
|
22
|
+
gem.add_development_dependency 'rspec', '>=2.12.0'
|
23
|
+
gem.add_development_dependency 'rake', '>=10.0.0.1'
|
24
|
+
gem.add_development_dependency 'ruby-prof', '>=0.11.2'
|
25
|
+
gem.add_development_dependency 'yard', '>=0.8.3'
|
26
|
+
gem.add_development_dependency 'redcarpet', '>=2.2.2'
|
27
27
|
end
|
data/spec/assets/test_words.txt
CHANGED
File without changes
|
@@ -3,48 +3,49 @@ require 'spec_helper'
|
|
3
3
|
module Rambling
|
4
4
|
module Trie
|
5
5
|
describe Branches do
|
6
|
-
describe '#
|
6
|
+
describe '#add' do
|
7
7
|
context 'new word for existing branch' do
|
8
8
|
let(:node) { Node.new 'back' }
|
9
9
|
|
10
|
-
before
|
11
|
-
node.
|
10
|
+
before do
|
11
|
+
node.add 'a'
|
12
12
|
end
|
13
13
|
|
14
14
|
it 'does not increment the child count' do
|
15
|
-
node.
|
15
|
+
expect(node).to have(1).children
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'marks it as terminal' do
|
19
|
-
node[:a].
|
19
|
+
expect(node[:a]).to be_terminal
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'returns the added node' do
|
23
|
+
expect(node.add('a').letter).to eq :a
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
23
27
|
context 'old word for existing branch' do
|
24
28
|
let(:node) { Node.new 'back' }
|
25
29
|
|
26
|
-
before
|
27
|
-
node.
|
30
|
+
before do
|
31
|
+
node.add 'ack'
|
28
32
|
end
|
29
33
|
|
30
34
|
it 'does not increment any child count' do
|
31
|
-
node.
|
32
|
-
node[:a].
|
33
|
-
node[:a][:c].
|
34
|
-
node[:a][:c][:k].
|
35
|
+
expect(node).to have(1).children
|
36
|
+
expect(node[:a]).to have(1).children
|
37
|
+
expect(node[:a][:c]).to have(1).children
|
38
|
+
expect(node[:a][:c][:k]).to have(0).children
|
35
39
|
end
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
39
43
|
describe '#<<' do
|
40
|
-
let(:
|
44
|
+
let(:node) { Node.new }
|
41
45
|
let(:word) { 'word' }
|
42
46
|
|
43
|
-
it 'delegates to #
|
44
|
-
|
45
|
-
root.stub(:add_branch_from).with(word).and_return value
|
46
|
-
root << word
|
47
|
-
end
|
47
|
+
it 'delegates to #add' do
|
48
|
+
expect((node << 'a').letter).to eq :a
|
48
49
|
end
|
49
50
|
end
|
50
51
|
end
|
@@ -20,7 +20,7 @@ module Rambling
|
|
20
20
|
|
21
21
|
it 'defers to the children hash' do
|
22
22
|
deferer.children.should_receive(:[]).with(key).and_return value
|
23
|
-
deferer[key].
|
23
|
+
expect(deferer[key]).to eq value
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -40,7 +40,7 @@ module Rambling
|
|
40
40
|
|
41
41
|
it 'defers to the children hash' do
|
42
42
|
deferer.children.should_receive(:delete).with(key).and_return value
|
43
|
-
deferer.delete
|
43
|
+
expect(deferer.delete key).to eq value
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -50,7 +50,7 @@ module Rambling
|
|
50
50
|
it 'defers to the children hash' do
|
51
51
|
[true, false].each do |value|
|
52
52
|
deferer.children.should_receive(:has_key?).with(key).and_return value
|
53
|
-
deferer.has_key?
|
53
|
+
expect(deferer.has_key? key).to eq value
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
@@ -6,31 +6,31 @@ module Rambling
|
|
6
6
|
let(:root) { Root.new }
|
7
7
|
let(:words) { %w(add some words and another word) }
|
8
8
|
|
9
|
-
before
|
9
|
+
before do
|
10
10
|
words.each { |word| root << word.clone }
|
11
11
|
end
|
12
12
|
|
13
13
|
describe '#each' do
|
14
14
|
it 'returns an enumerator' do
|
15
|
-
root.each.
|
15
|
+
expect(root.each).to be_a Enumerator
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'includes every word contained in the trie' do
|
19
|
-
root.each { |word| words.
|
20
|
-
root.count.
|
19
|
+
root.each { |word| expect(words).to include word }
|
20
|
+
expect(root.count).to eq words.count
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
describe '#size' do
|
25
25
|
it 'delegates to #count' do
|
26
|
-
root.size.
|
26
|
+
expect(root.size).to eq words.size
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'includes the core Enumerable module' do
|
31
|
-
root.all? { |word| words.include? word }.
|
32
|
-
root.any? { |word| word.start_with? 's' }.
|
33
|
-
root.to_a.
|
31
|
+
expect(root.all? { |word| words.include? word }).to be_true
|
32
|
+
expect(root.any? { |word| word.start_with? 's' }).to be_true
|
33
|
+
expect(root.to_a).to match_array words
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Rambling
|
4
|
+
module Trie
|
5
|
+
describe Inspector do
|
6
|
+
let(:root) do
|
7
|
+
Root.new do |trie|
|
8
|
+
%w(only three words).each { |word| trie << word }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:node) { root[:o] }
|
13
|
+
|
14
|
+
describe '#inspect' do
|
15
|
+
it 'returns a pretty printed version of the node' do
|
16
|
+
expect(root.inspect).to eq("#<Rambling::Trie::Root letter: nil, children: [:o, :t, :w]>")
|
17
|
+
expect(node.inspect).to eq("#<Rambling::Trie::Node letter: :o, children: [:n]>")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -4,27 +4,51 @@ module Rambling
|
|
4
4
|
module Trie
|
5
5
|
describe Node do
|
6
6
|
describe '.new' do
|
7
|
-
context 'with no
|
7
|
+
context 'with no word' do
|
8
|
+
let(:node) { Node.new }
|
9
|
+
|
10
|
+
it 'does not have any letter' do
|
11
|
+
expect(node.letter).to be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'includes no children' do
|
15
|
+
expect(node).to have(0).children
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'is not a terminal node' do
|
19
|
+
expect(node).to_not be_terminal
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'returns empty string as its word' do
|
23
|
+
expect(node.as_word).to be_empty
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'is not compressed' do
|
27
|
+
expect(node).to_not be_compressed
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with an empty word' do
|
8
32
|
let(:node) { Node.new '' }
|
9
33
|
|
10
34
|
it 'does not have any letter' do
|
11
|
-
node.letter.
|
35
|
+
expect(node.letter).to be_nil
|
12
36
|
end
|
13
37
|
|
14
38
|
it 'includes no children' do
|
15
|
-
node.
|
39
|
+
expect(node).to have(0).children
|
16
40
|
end
|
17
41
|
|
18
42
|
it 'is not a terminal node' do
|
19
|
-
node.
|
43
|
+
expect(node).to_not be_terminal
|
20
44
|
end
|
21
45
|
|
22
46
|
it 'returns empty string as its word' do
|
23
|
-
node.as_word.
|
47
|
+
expect(node.as_word).to be_empty
|
24
48
|
end
|
25
49
|
|
26
50
|
it 'is not compressed' do
|
27
|
-
node.
|
51
|
+
expect(node).to_not be_compressed
|
28
52
|
end
|
29
53
|
end
|
30
54
|
|
@@ -32,15 +56,15 @@ module Rambling
|
|
32
56
|
let(:node) { Node.new 'a' }
|
33
57
|
|
34
58
|
it 'makes it the node letter' do
|
35
|
-
node.letter.
|
59
|
+
expect(node.letter).to eq :a
|
36
60
|
end
|
37
61
|
|
38
62
|
it 'includes no children' do
|
39
|
-
node.
|
63
|
+
expect(node).to have(0).children
|
40
64
|
end
|
41
65
|
|
42
66
|
it 'is a terminal node' do
|
43
|
-
node.
|
67
|
+
expect(node).to be_terminal
|
44
68
|
end
|
45
69
|
end
|
46
70
|
|
@@ -48,31 +72,31 @@ module Rambling
|
|
48
72
|
let(:node) { Node.new 'ba' }
|
49
73
|
|
50
74
|
it 'takes the first as the node letter' do
|
51
|
-
node.letter.
|
75
|
+
expect(node.letter).to eq :b
|
52
76
|
end
|
53
77
|
|
54
78
|
it 'includes one child' do
|
55
|
-
node.
|
79
|
+
expect(node).to have(1).children
|
56
80
|
end
|
57
81
|
|
58
82
|
it 'includes a child with the expected letter' do
|
59
|
-
node.children.values.first.letter.
|
83
|
+
expect(node.children.values.first.letter).to eq :a
|
60
84
|
end
|
61
85
|
|
62
86
|
it 'has the expected letter as a key' do
|
63
|
-
node.
|
87
|
+
expect(node).to have_key(:a)
|
64
88
|
end
|
65
89
|
|
66
90
|
it 'returns the child corresponding to the key' do
|
67
|
-
node[:a].
|
91
|
+
expect(node[:a]).to eq node.children[:a]
|
68
92
|
end
|
69
93
|
|
70
94
|
it 'does not mark itself as a terminal node' do
|
71
|
-
node.
|
95
|
+
expect(node).to_not be_terminal
|
72
96
|
end
|
73
97
|
|
74
98
|
it 'marks the first child as a terminal node' do
|
75
|
-
node[:a].
|
99
|
+
expect(node[:a]).to be_terminal
|
76
100
|
end
|
77
101
|
end
|
78
102
|
|
@@ -80,17 +104,17 @@ module Rambling
|
|
80
104
|
let(:node) { Node.new 'spaghetti' }
|
81
105
|
|
82
106
|
it 'marks the last letter as terminal node' do
|
83
|
-
node[:p][:a][:g][:h][:e][:t][:t][:i].
|
107
|
+
expect(node[:p][:a][:g][:h][:e][:t][:t][:i]).to be_terminal
|
84
108
|
end
|
85
109
|
|
86
110
|
it 'does not mark any other letter as terminal node' do
|
87
|
-
node[:p][:a][:g][:h][:e][:t][:t].
|
88
|
-
node[:p][:a][:g][:h][:e][:t].
|
89
|
-
node[:p][:a][:g][:h][:e].
|
90
|
-
node[:p][:a][:g][:h].
|
91
|
-
node[:p][:a][:g].
|
92
|
-
node[:p][:a].
|
93
|
-
node[:p].
|
111
|
+
expect(node[:p][:a][:g][:h][:e][:t][:t]).to_not be_terminal
|
112
|
+
expect(node[:p][:a][:g][:h][:e][:t]).to_not be_terminal
|
113
|
+
expect(node[:p][:a][:g][:h][:e]).to_not be_terminal
|
114
|
+
expect(node[:p][:a][:g][:h]).to_not be_terminal
|
115
|
+
expect(node[:p][:a][:g]).to_not be_terminal
|
116
|
+
expect(node[:p][:a]).to_not be_terminal
|
117
|
+
expect(node[:p]).to_not be_terminal
|
94
118
|
end
|
95
119
|
end
|
96
120
|
end
|
@@ -100,7 +124,7 @@ module Rambling
|
|
100
124
|
let(:node) { Node.new '' }
|
101
125
|
|
102
126
|
it 'returns nil' do
|
103
|
-
node.as_word.
|
127
|
+
expect(node.as_word).to be_empty
|
104
128
|
end
|
105
129
|
end
|
106
130
|
|
@@ -108,7 +132,7 @@ module Rambling
|
|
108
132
|
let(:node) { Node.new 'a' }
|
109
133
|
|
110
134
|
it 'returns the expected one letter word' do
|
111
|
-
node.as_word.
|
135
|
+
expect(node.as_word).to eq 'a'
|
112
136
|
end
|
113
137
|
end
|
114
138
|
|
@@ -116,11 +140,11 @@ module Rambling
|
|
116
140
|
let(:node) { Node.new 'all' }
|
117
141
|
|
118
142
|
it 'returns the expected small word' do
|
119
|
-
node[:l][:l].as_word.
|
143
|
+
expect(node[:l][:l].as_word).to eq 'all'
|
120
144
|
end
|
121
145
|
|
122
146
|
it 'raises an error for a non terminal node' do
|
123
|
-
|
147
|
+
expect { node[:l].as_word }.to raise_error InvalidOperation
|
124
148
|
end
|
125
149
|
end
|
126
150
|
|
@@ -128,14 +152,14 @@ module Rambling
|
|
128
152
|
let(:node) { Node.new 'beautiful' }
|
129
153
|
|
130
154
|
it 'returns the expected long word' do
|
131
|
-
node[:e][:a][:u][:t][:i][:f][:u][:l].as_word.
|
155
|
+
expect(node[:e][:a][:u][:t][:i][:f][:u][:l].as_word).to eq 'beautiful'
|
132
156
|
end
|
133
157
|
end
|
134
158
|
|
135
159
|
context 'for a node with nil letter' do
|
136
160
|
let(:node) { Node.new nil }
|
137
161
|
it 'returns nil' do
|
138
|
-
node.as_word.
|
162
|
+
expect(node.as_word).to be_empty
|
139
163
|
end
|
140
164
|
end
|
141
165
|
end
|
@@ -145,22 +169,22 @@ module Rambling
|
|
145
169
|
let(:node) { Node.new '', root }
|
146
170
|
|
147
171
|
context 'parent is compressed' do
|
148
|
-
before
|
172
|
+
before do
|
149
173
|
root.stub(:compressed?).and_return true
|
150
174
|
end
|
151
175
|
|
152
176
|
it 'returns true' do
|
153
|
-
node.
|
177
|
+
expect(node).to be_compressed
|
154
178
|
end
|
155
179
|
end
|
156
180
|
|
157
181
|
context 'parent is not compressed' do
|
158
|
-
before
|
182
|
+
before do
|
159
183
|
root.stub(:compressed?).and_return false
|
160
184
|
end
|
161
185
|
|
162
186
|
it 'returns false' do
|
163
|
-
node.
|
187
|
+
expect(node).to_not be_compressed
|
164
188
|
end
|
165
189
|
end
|
166
190
|
end
|
@@ -6,264 +6,275 @@ module Rambling
|
|
6
6
|
let(:root) { Root.new }
|
7
7
|
|
8
8
|
describe '.new' do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
9
|
+
it 'has no children' do
|
10
|
+
expect(root).to have(0).children
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
it 'has no letter' do
|
14
|
+
expect(root.letter).to be_nil
|
15
|
+
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
it 'is not a terminal node' do
|
18
|
+
expect(root).to_not be_terminal
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
21
|
+
it 'is not a word' do
|
22
|
+
expect(root).to_not be_word
|
25
23
|
end
|
26
24
|
|
27
|
-
context 'with a
|
28
|
-
let(:
|
29
|
-
let(:root) { Root.new filename }
|
25
|
+
context 'with a block' do
|
26
|
+
let(:root) { Root.new { |root| root << 'test' } }
|
30
27
|
|
31
|
-
it 'has
|
32
|
-
|
33
|
-
expected_hash = Hash[file.readlines.map { |x| [x.slice(0).to_sym, nil] }]
|
34
|
-
root.should have(expected_hash.length).children
|
35
|
-
root.children.map { |k, v| k }.should =~ expected_hash.map { |k, v| k }
|
36
|
-
end
|
28
|
+
it 'has no letter' do
|
29
|
+
expect(root.letter).to be_nil
|
37
30
|
end
|
38
31
|
|
39
|
-
it '
|
40
|
-
|
41
|
-
file.readlines.each { |word| root.is_word?(word.chomp).should be_true }
|
42
|
-
end
|
32
|
+
it 'is not a terminal node' do
|
33
|
+
expect(root).to_not be_terminal
|
43
34
|
end
|
44
35
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'is marked as compressed' do
|
51
|
-
root.should be_compressed
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'compresses the root nodes' do
|
55
|
-
root[:t].letter.should == :t
|
56
|
-
root[:t].children.size.should == 2
|
57
|
-
|
58
|
-
root[:t][:ru].letter.should == :ru
|
59
|
-
root[:t][:ru].children.size.should == 2
|
36
|
+
it 'is not a word' do
|
37
|
+
expect(root).to_not be_word
|
38
|
+
end
|
60
39
|
|
61
|
-
|
62
|
-
|
63
|
-
|
40
|
+
it 'executes the block' do
|
41
|
+
expect(root).to have(1).children
|
42
|
+
expect(root.word? 'test').to be_true
|
64
43
|
end
|
65
44
|
end
|
66
45
|
end
|
67
46
|
|
68
47
|
describe '#compress!' do
|
69
|
-
|
70
|
-
compressed_root = root.compress!
|
48
|
+
let(:compressed_root) { root.compress! }
|
71
49
|
|
72
|
-
|
73
|
-
compressed_root.
|
50
|
+
it 'returns itself marked as compressed' do
|
51
|
+
expect(compressed_root).to eq root
|
52
|
+
expect(compressed_root).to be_compressed
|
74
53
|
end
|
75
54
|
|
76
55
|
context 'after calling #compress! once' do
|
77
|
-
|
78
|
-
compressed_root = root.compress!.compress!
|
56
|
+
let(:recompressed_root) { compressed_root.compress! }
|
79
57
|
|
80
|
-
|
81
|
-
|
58
|
+
it 'keeps returning itself' do
|
59
|
+
expect(recompressed_root).to eq root
|
60
|
+
expect(recompressed_root).to be_compressed
|
82
61
|
end
|
83
62
|
end
|
84
63
|
|
85
64
|
context 'with at least one word' do
|
86
65
|
it 'keeps the root letter nil' do
|
87
|
-
root
|
66
|
+
root << 'all'
|
88
67
|
root.compress!
|
89
68
|
|
90
|
-
root.letter.
|
69
|
+
expect(root.letter).to be_nil
|
91
70
|
end
|
92
71
|
end
|
93
72
|
|
94
73
|
context 'with a single word' do
|
95
|
-
before
|
96
|
-
root
|
74
|
+
before do
|
75
|
+
root << 'all'
|
97
76
|
root.compress!
|
98
77
|
end
|
99
78
|
|
100
79
|
it 'compresses into a single node without children' do
|
101
|
-
root[:all].letter.
|
102
|
-
root[:all].
|
103
|
-
root[:all].
|
104
|
-
root[:all].
|
80
|
+
expect(root[:all].letter).to eq :all
|
81
|
+
expect(root[:all]).to have(0).children
|
82
|
+
expect(root[:all]).to be_terminal
|
83
|
+
expect(root[:all]).to be_compressed
|
105
84
|
end
|
106
85
|
end
|
107
86
|
|
108
87
|
context 'with two words' do
|
109
|
-
before
|
110
|
-
root
|
111
|
-
root
|
88
|
+
before do
|
89
|
+
root << 'all'
|
90
|
+
root << 'ask'
|
112
91
|
root.compress!
|
113
92
|
end
|
114
93
|
|
115
94
|
it 'compresses into corresponding three nodes' do
|
116
|
-
root[:a].letter.
|
117
|
-
root[:a].children.size.
|
95
|
+
expect(root[:a].letter).to eq :a
|
96
|
+
expect(root[:a].children.size).to eq 2
|
118
97
|
|
119
|
-
root[:a][:ll].letter.
|
120
|
-
root[:a][:sk].letter.
|
98
|
+
expect(root[:a][:ll].letter).to eq :ll
|
99
|
+
expect(root[:a][:sk].letter).to eq :sk
|
121
100
|
|
122
|
-
root[:a][:ll].
|
123
|
-
root[:a][:sk].
|
101
|
+
expect(root[:a][:ll]).to have(0).children
|
102
|
+
expect(root[:a][:sk]).to have(0).children
|
124
103
|
|
125
|
-
root[:a][:ll].
|
126
|
-
root[:a][:sk].
|
104
|
+
expect(root[:a][:ll]).to be_terminal
|
105
|
+
expect(root[:a][:sk]).to be_terminal
|
127
106
|
|
128
|
-
root[:a][:ll].
|
129
|
-
root[:a][:sk].
|
107
|
+
expect(root[:a][:ll]).to be_compressed
|
108
|
+
expect(root[:a][:sk]).to be_compressed
|
130
109
|
end
|
131
110
|
end
|
132
111
|
|
133
112
|
it 'reassigns the parent nodes correctly' do
|
134
|
-
root
|
135
|
-
root
|
136
|
-
root
|
113
|
+
root << 'repay'
|
114
|
+
root << 'rest'
|
115
|
+
root << 'repaint'
|
137
116
|
root.compress!
|
138
117
|
|
139
|
-
root[:re].letter.
|
140
|
-
root[:re].children.size.
|
118
|
+
expect(root[:re].letter).to eq :re
|
119
|
+
expect(root[:re].children.size).to eq 2
|
141
120
|
|
142
|
-
root[:re][:pa].letter.
|
143
|
-
root[:re][:st].letter.
|
121
|
+
expect(root[:re][:pa].letter).to eq :pa
|
122
|
+
expect(root[:re][:st].letter).to eq :st
|
144
123
|
|
145
|
-
root[:re][:pa].children.size.
|
146
|
-
root[:re][:st].
|
124
|
+
expect(root[:re][:pa].children.size).to eq 2
|
125
|
+
expect(root[:re][:st]).to have(0).children
|
147
126
|
|
148
|
-
root[:re][:pa][:y].letter.
|
149
|
-
root[:re][:pa][:int].letter.
|
127
|
+
expect(root[:re][:pa][:y].letter).to eq :y
|
128
|
+
expect(root[:re][:pa][:int].letter).to eq :int
|
150
129
|
|
151
|
-
root[:re][:pa][:y].
|
152
|
-
root[:re][:pa][:int].
|
130
|
+
expect(root[:re][:pa][:y]).to have(0).children
|
131
|
+
expect(root[:re][:pa][:int]).to have(0).children
|
153
132
|
|
154
|
-
root[:re][:pa][:y].parent.
|
155
|
-
root[:re][:pa][:int].parent.
|
133
|
+
expect(root[:re][:pa][:y].parent).to eq root[:re][:pa]
|
134
|
+
expect(root[:re][:pa][:int].parent).to eq root[:re][:pa]
|
156
135
|
end
|
157
136
|
|
158
137
|
it 'does not compress terminal nodes' do
|
159
|
-
root
|
160
|
-
root
|
161
|
-
root
|
138
|
+
root << 'you'
|
139
|
+
root << 'your'
|
140
|
+
root << 'yours'
|
162
141
|
|
163
142
|
root.compress!
|
164
143
|
|
165
|
-
root[:you].letter.
|
144
|
+
expect(root[:you].letter).to eq :you
|
166
145
|
|
167
|
-
root[:you][:r].letter.
|
168
|
-
root[:you][:r].
|
146
|
+
expect(root[:you][:r].letter).to eq :r
|
147
|
+
expect(root[:you][:r]).to be_compressed
|
169
148
|
|
170
|
-
root[:you][:r][:s].letter.
|
171
|
-
root[:you][:r][:s].
|
149
|
+
expect(root[:you][:r][:s].letter).to eq :s
|
150
|
+
expect(root[:you][:r][:s]).to be_compressed
|
172
151
|
end
|
173
152
|
|
174
153
|
describe 'and trying to add a branch' do
|
175
154
|
it 'raises an error' do
|
176
|
-
root
|
177
|
-
root
|
178
|
-
root
|
155
|
+
root << 'repay'
|
156
|
+
root << 'rest'
|
157
|
+
root << 'repaint'
|
179
158
|
root.compress!
|
180
159
|
|
181
|
-
|
160
|
+
expect { root << 'restaurant' }.to raise_error InvalidOperation
|
182
161
|
end
|
183
162
|
end
|
184
163
|
end
|
185
164
|
|
186
|
-
describe '#
|
165
|
+
describe '#word?' do
|
187
166
|
context 'word is contained' do
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
root.should have_branch_for 'hig'
|
192
|
-
end
|
193
|
-
|
194
|
-
it 'matches the whole word' do
|
195
|
-
root.is_word?('hello').should be_true
|
196
|
-
root.is_word?('high').should be_true
|
197
|
-
end
|
167
|
+
before do
|
168
|
+
root << 'hello'
|
169
|
+
root << 'high'
|
198
170
|
end
|
199
171
|
|
200
|
-
|
201
|
-
root.
|
202
|
-
root.
|
172
|
+
it 'matches the whole word' do
|
173
|
+
expect(root.word? 'hello').to be_true
|
174
|
+
expect(root.word? 'high').to be_true
|
203
175
|
end
|
204
176
|
|
205
|
-
|
177
|
+
it 'is aliased as #include?' do
|
178
|
+
expect(root).to include 'hello'
|
179
|
+
expect(root).to include 'high'
|
180
|
+
end
|
206
181
|
|
207
|
-
context 'and the root been compressed' do
|
208
|
-
before
|
182
|
+
context 'and the root has been compressed' do
|
183
|
+
before do
|
209
184
|
root.compress!
|
210
185
|
end
|
211
186
|
|
212
|
-
|
187
|
+
it 'matches the whole word' do
|
188
|
+
expect(root.word? 'hello').to be_true
|
189
|
+
expect(root.word? 'high').to be_true
|
190
|
+
end
|
213
191
|
end
|
214
192
|
end
|
215
193
|
|
216
194
|
context 'word is not contained' do
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
195
|
+
before do
|
196
|
+
root << 'hello'
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'does not match the whole word' do
|
200
|
+
expect(root.word? 'halt').to be_false
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'is aliased as #include?' do
|
204
|
+
expect(root).to_not include 'high'
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'and the root has been compressed' do
|
208
|
+
before do
|
209
|
+
root.compress!
|
221
210
|
end
|
222
211
|
|
223
212
|
it 'does not match the whole word' do
|
224
|
-
root.
|
213
|
+
expect(root.word? 'halt').to be_false
|
225
214
|
end
|
226
215
|
end
|
216
|
+
end
|
217
|
+
end
|
227
218
|
|
228
|
-
|
229
|
-
|
219
|
+
describe '#branch?' do
|
220
|
+
context 'word is contained' do
|
221
|
+
before do
|
222
|
+
root << 'hello'
|
223
|
+
root << 'high'
|
230
224
|
end
|
231
225
|
|
232
|
-
|
226
|
+
it 'matches part of the word' do
|
227
|
+
expect(root.branch? 'hell').to be_true
|
228
|
+
expect(root.branch? 'hig').to be_true
|
229
|
+
end
|
233
230
|
|
234
231
|
context 'and the root has been compressed' do
|
235
|
-
before
|
232
|
+
before do
|
236
233
|
root.compress!
|
237
234
|
end
|
238
235
|
|
239
|
-
|
236
|
+
it 'matches part of the word' do
|
237
|
+
expect(root.branch? 'hell').to be_true
|
238
|
+
expect(root.branch? 'hig').to be_true
|
239
|
+
end
|
240
240
|
end
|
241
241
|
end
|
242
|
-
end
|
243
242
|
|
244
|
-
|
245
|
-
|
243
|
+
context 'word is not contained' do
|
244
|
+
before do
|
245
|
+
root << 'hello'
|
246
|
+
end
|
246
247
|
|
247
|
-
|
248
|
-
|
249
|
-
root.
|
250
|
-
|
248
|
+
it 'does not match any part of the word' do
|
249
|
+
expect(root.branch? 'ha').to be_false
|
250
|
+
expect(root.branch? 'hal').to be_false
|
251
|
+
end
|
252
|
+
|
253
|
+
context 'and the root has been compressed' do
|
254
|
+
before do
|
255
|
+
root.compress!
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'does not match any part of the word' do
|
259
|
+
expect(root.branch? 'ha').to be_false
|
260
|
+
expect(root.branch? 'hal').to be_false
|
261
|
+
end
|
251
262
|
end
|
252
263
|
end
|
253
264
|
end
|
254
265
|
|
255
|
-
describe '#
|
266
|
+
describe '#add' do
|
256
267
|
let(:original_word) { 'word' }
|
257
268
|
let(:word) { original_word.clone }
|
258
269
|
|
259
270
|
it 'does not change the original word' do
|
260
|
-
root.
|
261
|
-
word.
|
271
|
+
root.add word
|
272
|
+
expect(word).to eq original_word
|
262
273
|
end
|
263
274
|
|
264
275
|
it 'is still aliased as #<<' do
|
265
276
|
root << word
|
266
|
-
word.
|
277
|
+
expect(word).to eq original_word
|
267
278
|
end
|
268
279
|
end
|
269
280
|
end
|