rambling-trie 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|