rambling-trie 1.0.2 → 1.0.3
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.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/README.md +23 -7
- data/Rakefile +4 -0
- data/lib/rambling/trie.rb +27 -21
- data/lib/rambling/trie/comparable.rb +3 -3
- data/lib/rambling/trie/compressible.rb +14 -0
- data/lib/rambling/trie/compressor.rb +37 -24
- data/lib/rambling/trie/configuration/properties.rb +8 -6
- data/lib/rambling/trie/configuration/provider_collection.rb +34 -16
- data/lib/rambling/trie/container.rb +156 -36
- data/lib/rambling/trie/enumerable.rb +4 -4
- data/lib/rambling/trie/nodes.rb +11 -0
- data/lib/rambling/trie/nodes/compressed.rb +115 -0
- data/lib/rambling/trie/nodes/missing.rb +10 -0
- data/lib/rambling/trie/nodes/node.rb +151 -0
- data/lib/rambling/trie/nodes/raw.rb +89 -0
- data/lib/rambling/trie/readers/plain_text.rb +1 -11
- data/lib/rambling/trie/serializers/marshal.rb +4 -4
- data/lib/rambling/trie/serializers/yaml.rb +4 -4
- data/lib/rambling/trie/serializers/zip.rb +9 -8
- data/lib/rambling/trie/version.rb +1 -1
- data/spec/assets/test_words.es_DO.txt +1 -0
- data/spec/integration/rambling/trie_spec.rb +40 -35
- data/spec/lib/rambling/trie/comparable_spec.rb +6 -15
- data/spec/lib/rambling/trie/compressor_spec.rb +88 -13
- data/spec/lib/rambling/trie/configuration/properties_spec.rb +7 -7
- data/spec/lib/rambling/trie/configuration/provider_collection_spec.rb +8 -20
- data/spec/lib/rambling/trie/container_spec.rb +159 -168
- data/spec/lib/rambling/trie/enumerable_spec.rb +12 -9
- data/spec/lib/rambling/trie/inspectable_spec.rb +11 -11
- data/spec/lib/rambling/trie/nodes/compressed_spec.rb +35 -0
- data/spec/lib/rambling/trie/nodes/node_spec.rb +7 -0
- data/spec/lib/rambling/trie/nodes/raw_spec.rb +177 -0
- data/spec/lib/rambling/trie/serializers/file_spec.rb +4 -4
- data/spec/lib/rambling/trie/serializers/marshal_spec.rb +3 -7
- data/spec/lib/rambling/trie/serializers/yaml_spec.rb +3 -7
- data/spec/lib/rambling/trie/serializers/zip_spec.rb +16 -20
- data/spec/lib/rambling/trie/stringifyable_spec.rb +7 -8
- data/spec/lib/rambling/trie_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -1
- data/spec/support/config.rb +4 -0
- data/spec/support/helpers/add_word.rb +18 -0
- data/spec/support/shared_examples/{a_compressable_trie.rb → a_compressible_trie.rb} +13 -3
- data/spec/support/shared_examples/a_serializable_trie.rb +8 -6
- data/spec/support/shared_examples/a_serializer.rb +6 -0
- data/spec/{lib/rambling/trie/node_spec.rb → support/shared_examples/a_trie_node.rb} +61 -30
- data/spec/{lib/rambling/trie/compressed_node_spec.rb → support/shared_examples/a_trie_node_implementation.rb} +18 -69
- metadata +22 -15
- data/lib/rambling/trie/compressable.rb +0 -14
- data/lib/rambling/trie/compressed_node.rb +0 -120
- data/lib/rambling/trie/missing_node.rb +0 -8
- data/lib/rambling/trie/node.rb +0 -97
- data/lib/rambling/trie/raw_node.rb +0 -96
- data/spec/lib/rambling/trie/raw_node_spec.rb +0 -389
@@ -3,34 +3,37 @@ require 'spec_helper'
|
|
3
3
|
module Rambling
|
4
4
|
module Trie
|
5
5
|
describe Enumerable do
|
6
|
-
let(:
|
6
|
+
let(:node) { Rambling::Trie::Nodes::Raw.new }
|
7
7
|
let(:words) { %w(add some words and another word) }
|
8
8
|
|
9
9
|
before do
|
10
|
-
|
10
|
+
add_words node, words
|
11
11
|
end
|
12
12
|
|
13
13
|
describe '#each' do
|
14
14
|
it 'returns an enumerator' do
|
15
|
-
expect(
|
15
|
+
expect(node.each).to be_a Enumerator
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'includes every word contained in the trie' do
|
19
|
-
|
20
|
-
|
19
|
+
node.each do |word|
|
20
|
+
expect(words).to include word
|
21
|
+
end
|
22
|
+
|
23
|
+
expect(node.count).to eq words.count
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
24
27
|
describe '#size' do
|
25
28
|
it 'delegates to #count' do
|
26
|
-
expect(
|
29
|
+
expect(node.size).to eq words.size
|
27
30
|
end
|
28
31
|
end
|
29
32
|
|
30
33
|
it 'includes the core Enumerable module' do
|
31
|
-
expect(
|
32
|
-
expect(
|
33
|
-
expect(
|
34
|
+
expect(node.all? { |word| words.include? word }).to be true
|
35
|
+
expect(node.any? { |word| word.start_with? 's' }).to be true
|
36
|
+
expect(node.to_a).to match_array words
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
@@ -1,30 +1,30 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Rambling::Trie::Inspectable do
|
4
|
-
let(:
|
4
|
+
let(:node) { Rambling::Trie::Nodes::Raw.new }
|
5
5
|
|
6
6
|
before do
|
7
|
-
%w(only three words)
|
7
|
+
add_words node, %w(only three words)
|
8
8
|
end
|
9
9
|
|
10
10
|
describe '#inspect' do
|
11
|
-
let(:
|
12
|
-
let(:terminal_node) {
|
11
|
+
let(:child) { node[:o] }
|
12
|
+
let(:terminal_node) { node[:o][:n][:l][:y] }
|
13
13
|
|
14
14
|
it 'returns a pretty printed version of the node' do
|
15
|
-
expect(
|
16
|
-
expect(
|
17
|
-
expect(terminal_node.inspect).to eq "#<Rambling::Trie::
|
15
|
+
expect(node.inspect).to eq "#<Rambling::Trie::Nodes::Raw letter: nil, terminal: nil, children: [:o, :t, :w]>"
|
16
|
+
expect(child.inspect).to eq "#<Rambling::Trie::Nodes::Raw letter: :o, terminal: nil, children: [:n]>"
|
17
|
+
expect(terminal_node.inspect).to eq "#<Rambling::Trie::Nodes::Raw letter: :y, terminal: true, children: []>"
|
18
18
|
end
|
19
19
|
|
20
20
|
context 'for a compressed node' do
|
21
21
|
let(:compressor) { Rambling::Trie::Compressor.new }
|
22
|
-
let(:
|
23
|
-
let(:
|
22
|
+
let(:compressed_node) { compressor.compress node }
|
23
|
+
let(:compressed_child) { compressed_node[:only] }
|
24
24
|
|
25
25
|
it 'returns a pretty printed version of the compressed node' do
|
26
|
-
expect(
|
27
|
-
expect(
|
26
|
+
expect(compressed_node.inspect).to eq "#<Rambling::Trie::Nodes::Compressed letter: nil, terminal: nil, children: [:only, :three, :words]>"
|
27
|
+
expect(compressed_child.inspect).to eq "#<Rambling::Trie::Nodes::Compressed letter: :only, terminal: true, children: []>"
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rambling::Trie::Nodes::Compressed do
|
4
|
+
let(:raw_node) { Rambling::Trie::Nodes::Raw.new }
|
5
|
+
let(:compressor) { Rambling::Trie::Compressor.new }
|
6
|
+
let(:node) { compressor.compress raw_node }
|
7
|
+
|
8
|
+
it_behaves_like 'a trie node implementation' do
|
9
|
+
def add_word_to_tree word
|
10
|
+
add_word raw_node, word
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_words_to_tree words
|
14
|
+
add_words raw_node, words
|
15
|
+
end
|
16
|
+
|
17
|
+
def assign_letter letter
|
18
|
+
raw_node.letter = letter
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#compressed?' do
|
23
|
+
it 'returns true' do
|
24
|
+
expect(node).to be_compressed
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#add' do
|
29
|
+
it 'raises an error' do
|
30
|
+
expect do
|
31
|
+
add_word node, 'restaurant'
|
32
|
+
end.to raise_error Rambling::Trie::InvalidOperation
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rambling::Trie::Nodes::Raw do
|
4
|
+
let(:node) { Rambling::Trie::Nodes::Raw.new }
|
5
|
+
|
6
|
+
it_behaves_like 'a trie node implementation' do
|
7
|
+
def add_word_to_tree word
|
8
|
+
add_word node, word
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_words_to_tree words
|
12
|
+
add_words node, words
|
13
|
+
end
|
14
|
+
|
15
|
+
def assign_letter letter
|
16
|
+
node.letter = letter
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'is not a word' do
|
20
|
+
expect(node).not_to be_word
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#compressed?' do
|
25
|
+
it 'returns false' do
|
26
|
+
expect(node).not_to be_compressed
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#add' do
|
31
|
+
context 'when the node has no branches' do
|
32
|
+
before do
|
33
|
+
add_word node, 'abc'
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'adds only one child' do
|
37
|
+
expect(node.children.size).to eq 1
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'adds the full subtree' do
|
41
|
+
expect(node[:a]).not_to be_nil
|
42
|
+
expect(node[:a][:b]).not_to be_nil
|
43
|
+
expect(node[:a][:b][:c]).not_to be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'marks only the last child as terminal' do
|
47
|
+
expect(node).not_to be_terminal
|
48
|
+
expect(node[:a]).not_to be_terminal
|
49
|
+
expect(node[:a][:b]).not_to be_terminal
|
50
|
+
expect(node[:a][:b][:c]).to be_terminal
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when a word is added more than once' do
|
55
|
+
before do
|
56
|
+
add_word node, 'ack'
|
57
|
+
add_word node, 'ack'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'only counts it once' do
|
61
|
+
expect(node.children.size).to eq 1
|
62
|
+
expect(node[:a].children.size).to eq 1
|
63
|
+
expect(node[:a][:c].children.size).to eq 1
|
64
|
+
expect(node[:a][:c][:k].children.size).to eq 0
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not change the terminal nodes in the tree' do
|
68
|
+
expect(node).not_to be_terminal
|
69
|
+
expect(node[:a]).not_to be_terminal
|
70
|
+
expect(node[:a][:c]).not_to be_terminal
|
71
|
+
expect(node[:a][:c][:k]).to be_terminal
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'still returns the "added" node' do
|
75
|
+
child = add_word node, 'ack'
|
76
|
+
expect(child.letter).to eq :a
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when the word does not exist in the tree but the letters do' do
|
81
|
+
before do
|
82
|
+
add_words node, %w(ack a)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'does not add another branch' do
|
86
|
+
expect(node.children.size).to eq 1
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'marks the corresponding node as terminal' do
|
90
|
+
expect(node[:a]).to be_terminal
|
91
|
+
|
92
|
+
expect(node).not_to be_terminal
|
93
|
+
expect(node[:a][:c]).not_to be_terminal
|
94
|
+
expect(node[:a][:c][:k]).to be_terminal
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'returns the added node' do
|
98
|
+
child = add_word node, 'a'
|
99
|
+
expect(child.letter).to eq :a
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when the node has a letter and a parent' do
|
104
|
+
let(:parent) { Rambling::Trie::Nodes::Raw.new }
|
105
|
+
let(:node) { Rambling::Trie::Nodes::Raw.new :a, parent }
|
106
|
+
|
107
|
+
context 'adding an empty string' do
|
108
|
+
before do
|
109
|
+
add_word node, ''
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'does not alter the node letter' do
|
113
|
+
expect(node.letter).to eq :a
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'does not change the node children' do
|
117
|
+
expect(node.children.size).to eq 0
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'changes the node to terminal' do
|
121
|
+
expect(node).to be_terminal
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'adding a one letter word' do
|
126
|
+
before do
|
127
|
+
add_word node, 'b'
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'does not alter the node letter' do
|
131
|
+
expect(node.letter).to eq :a
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'adds a child with the expected letter' do
|
135
|
+
expect(node.children.size).to eq 1
|
136
|
+
expect(node.children.first.letter).to eq :b
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'reports it has the expected letter a key' do
|
140
|
+
expect(node).to have_key(:b)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'returns the child corresponding to the key' do
|
144
|
+
expect(node[:b]).to eq node.children_tree[:b]
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'does not mark itself as terminal' do
|
148
|
+
expect(node).not_to be_terminal
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'marks the first child as terminal' do
|
152
|
+
expect(node[:b]).to be_terminal
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'adding a large word' do
|
157
|
+
before do
|
158
|
+
add_word node, 'mplified'
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'marks the last letter as terminal' do
|
162
|
+
expect(node[:m][:p][:l][:i][:f][:i][:e][:d]).to be_terminal
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'does not mark any other letter as terminal' do
|
166
|
+
expect(node[:m][:p][:l][:i][:f][:i][:e]).not_to be_terminal
|
167
|
+
expect(node[:m][:p][:l][:i][:f][:i]).not_to be_terminal
|
168
|
+
expect(node[:m][:p][:l][:i][:f]).not_to be_terminal
|
169
|
+
expect(node[:m][:p][:l][:i]).not_to be_terminal
|
170
|
+
expect(node[:m][:p][:l]).not_to be_terminal
|
171
|
+
expect(node[:m][:p]).not_to be_terminal
|
172
|
+
expect(node[:m]).not_to be_terminal
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Rambling::Trie::Serializers::File do
|
4
|
-
let(:serializer) { Rambling::Trie::Serializers::File.new }
|
5
|
-
|
6
4
|
it_behaves_like 'a serializer' do
|
7
|
-
let(:
|
8
|
-
let(:
|
5
|
+
let(:serializer) { Rambling::Trie::Serializers::File.new }
|
6
|
+
let(:format) { :file }
|
7
|
+
|
8
|
+
let(:content) { trie.to_a.join ' ' }
|
9
9
|
let(:formatted_content) { content }
|
10
10
|
end
|
11
11
|
end
|
@@ -1,14 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Rambling::Trie::Serializers::Marshal do
|
4
|
-
let(:serializer) { Rambling::Trie::Serializers::Marshal.new }
|
5
|
-
|
6
|
-
let(:words) { %w(a few words to validate that load and dump are working) }
|
7
|
-
let(:trie) { Rambling::Trie.create { |t| words.each { |w| t << w } } }
|
8
|
-
|
9
4
|
it_behaves_like 'a serializer' do
|
10
|
-
let(:
|
11
|
-
let(:
|
5
|
+
let(:serializer) { Rambling::Trie::Serializers::Marshal.new }
|
6
|
+
let(:format) { :marshal }
|
7
|
+
|
12
8
|
let(:formatted_content) { Marshal.dump content }
|
13
9
|
end
|
14
10
|
end
|
@@ -1,14 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Rambling::Trie::Serializers::Yaml do
|
4
|
-
let(:serializer) { Rambling::Trie::Serializers::Yaml.new }
|
5
|
-
|
6
|
-
let(:words) { %w(a few words to validate that load and dump are working) }
|
7
|
-
let(:trie) { Rambling::Trie.create { |t| words.each { |w| t << w } } }
|
8
|
-
|
9
4
|
it_behaves_like 'a serializer' do
|
10
|
-
let(:
|
11
|
-
let(:
|
5
|
+
let(:serializer) { Rambling::Trie::Serializers::Yaml.new }
|
6
|
+
let(:format) { :yml }
|
7
|
+
|
12
8
|
let(:formatted_content) { YAML.dump content }
|
13
9
|
end
|
14
10
|
end
|
@@ -1,30 +1,26 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Rambling::Trie::Serializers::Zip do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
let(:trie) { Rambling::Trie.create { |t| words.each { |w| t << w } } }
|
9
|
-
let(:tmp_path) { File.join ::SPEC_ROOT, 'tmp' }
|
4
|
+
it_behaves_like 'a serializer' do
|
5
|
+
let(:properties) { Rambling::Trie::Configuration::Properties.new }
|
6
|
+
let(:serializer) { Rambling::Trie::Serializers::Zip.new properties }
|
7
|
+
let(:format) { 'marshal.zip' }
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
before do
|
10
|
+
properties.tmp_path = tmp_path
|
11
|
+
end
|
14
12
|
|
15
|
-
|
16
|
-
let(:filename) { 'trie-root.marshal' }
|
17
|
-
let(:filepath) { File.join tmp_path, "#{filename}.zip" }
|
18
|
-
let(:content) { trie.root }
|
13
|
+
let(:filename) { File.basename(filepath).gsub /\.zip/, ''}
|
19
14
|
let(:formatted_content) { zip Marshal.dump content }
|
20
|
-
end
|
21
15
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
def zip content
|
17
|
+
io = Zip::OutputStream.write_buffer do |io|
|
18
|
+
io.put_next_entry filename
|
19
|
+
io.write content
|
20
|
+
end
|
21
|
+
|
22
|
+
io.rewind
|
23
|
+
io.read
|
26
24
|
end
|
27
|
-
io.rewind
|
28
|
-
io.read
|
29
25
|
end
|
30
26
|
end
|
@@ -2,11 +2,11 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Rambling::Trie::Stringifyable do
|
4
4
|
describe '#as_word' do
|
5
|
-
let(:node) { Rambling::Trie::
|
5
|
+
let(:node) { Rambling::Trie::Nodes::Raw.new }
|
6
6
|
|
7
7
|
context 'for an empty node' do
|
8
8
|
before do
|
9
|
-
node
|
9
|
+
add_word node, ''
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'returns nil' do
|
@@ -17,7 +17,7 @@ describe Rambling::Trie::Stringifyable do
|
|
17
17
|
context 'for one letter' do
|
18
18
|
before do
|
19
19
|
node.letter = :a
|
20
|
-
node
|
20
|
+
add_word node, ''
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'returns the expected one letter word' do
|
@@ -28,7 +28,7 @@ describe Rambling::Trie::Stringifyable do
|
|
28
28
|
context 'for a small word' do
|
29
29
|
before do
|
30
30
|
node.letter = :a
|
31
|
-
node
|
31
|
+
add_word node, 'll'
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'returns the expected small word' do
|
@@ -43,7 +43,7 @@ describe Rambling::Trie::Stringifyable do
|
|
43
43
|
context 'for a long word' do
|
44
44
|
before do
|
45
45
|
node.letter = :b
|
46
|
-
node
|
46
|
+
add_word node, 'eautiful'
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'returns the expected long word' do
|
@@ -52,7 +52,7 @@ describe Rambling::Trie::Stringifyable do
|
|
52
52
|
end
|
53
53
|
|
54
54
|
context 'for a node with nil letter' do
|
55
|
-
let(:node) { Rambling::Trie::
|
55
|
+
let(:node) { Rambling::Trie::Nodes::Raw.new nil }
|
56
56
|
|
57
57
|
it 'returns nil' do
|
58
58
|
expect(node.as_word).to be_empty
|
@@ -65,8 +65,7 @@ describe Rambling::Trie::Stringifyable do
|
|
65
65
|
|
66
66
|
before do
|
67
67
|
node.letter = :a
|
68
|
-
node
|
69
|
-
node.add 'dd'
|
68
|
+
add_words node, %w(m dd)
|
70
69
|
end
|
71
70
|
|
72
71
|
it 'returns the words for the terminal nodes' do
|