rambling-trie 2.4.0 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +28 -0
  3. data/Gemfile +20 -8
  4. data/Guardfile +16 -5
  5. data/README.md +38 -32
  6. data/Rakefile +6 -0
  7. data/Steepfile +35 -0
  8. data/lib/rambling/trie/comparable.rb +2 -2
  9. data/lib/rambling/trie/compressible.rb +1 -1
  10. data/lib/rambling/trie/compressor.rb +22 -19
  11. data/lib/rambling/trie/configuration/properties.rb +10 -6
  12. data/lib/rambling/trie/configuration/provider_collection.rb +14 -9
  13. data/lib/rambling/trie/configuration.rb +2 -3
  14. data/lib/rambling/trie/container.rb +32 -24
  15. data/lib/rambling/trie/enumerable.rb +5 -6
  16. data/lib/rambling/trie/nodes/compressed.rb +26 -16
  17. data/lib/rambling/trie/nodes/node.rb +35 -12
  18. data/lib/rambling/trie/nodes/raw.rb +18 -20
  19. data/lib/rambling/trie/nodes.rb +2 -3
  20. data/lib/rambling/trie/readers/plain_text.rb +3 -3
  21. data/lib/rambling/trie/readers.rb +2 -3
  22. data/lib/rambling/trie/serializers/file.rb +1 -3
  23. data/lib/rambling/trie/serializers/marshal.rb +4 -4
  24. data/lib/rambling/trie/serializers/yaml.rb +3 -3
  25. data/lib/rambling/trie/serializers/zip.rb +13 -5
  26. data/lib/rambling/trie/serializers.rb +2 -3
  27. data/lib/rambling/trie/stringifyable.rb +1 -1
  28. data/lib/rambling/trie/version.rb +1 -1
  29. data/lib/rambling/trie.rb +12 -15
  30. data/rambling-trie.gemspec +4 -10
  31. data/sig/lib/rambling/trie/comparable.rbs +17 -0
  32. data/sig/lib/rambling/trie/compressible.rbs +17 -0
  33. data/sig/lib/rambling/trie/compressor.rbs +17 -0
  34. data/sig/lib/rambling/trie/configuration/properties.rbs +28 -0
  35. data/sig/lib/rambling/trie/configuration/provider_collection.rbs +47 -0
  36. data/sig/lib/rambling/trie/container.rbs +69 -0
  37. data/sig/lib/rambling/trie/enumerable.rbs +23 -0
  38. data/sig/lib/rambling/trie/inspectable.rbs +27 -0
  39. data/sig/lib/rambling/trie/invalid_operation.rbs +7 -0
  40. data/sig/lib/rambling/trie/nodes/compressed.rbs +25 -0
  41. data/sig/lib/rambling/trie/nodes/missing.rbs +9 -0
  42. data/sig/lib/rambling/trie/nodes/node.rbs +69 -0
  43. data/sig/lib/rambling/trie/nodes/raw.rbs +27 -0
  44. data/sig/lib/rambling/trie/readers/plain_text.rbs +9 -0
  45. data/sig/lib/rambling/trie/readers/reader.rbs +9 -0
  46. data/sig/lib/rambling/trie/serializers/file.rbs +8 -0
  47. data/sig/lib/rambling/trie/serializers/marshal.rbs +13 -0
  48. data/sig/lib/rambling/trie/serializers/serializer.rbs +10 -0
  49. data/sig/lib/rambling/trie/serializers/yaml.rbs +13 -0
  50. data/sig/lib/rambling/trie/serializers/zip.rbs +21 -0
  51. data/sig/lib/rambling/trie/stringifyable.rbs +21 -0
  52. data/sig/lib/rambling/trie.rbs +27 -0
  53. data/sig/lib/zip/entry.rbs +11 -0
  54. data/sig/lib/zip/file.rbs +11 -0
  55. metadata +34 -123
  56. data/spec/assets/test_words.en_US.txt +0 -23
  57. data/spec/assets/test_words.es_DO.txt +0 -24
  58. data/spec/integration/rambling/trie_spec.rb +0 -116
  59. data/spec/lib/rambling/trie/comparable_spec.rb +0 -87
  60. data/spec/lib/rambling/trie/compressor_spec.rb +0 -111
  61. data/spec/lib/rambling/trie/configuration/properties_spec.rb +0 -75
  62. data/spec/lib/rambling/trie/configuration/provider_collection_spec.rb +0 -177
  63. data/spec/lib/rambling/trie/container_spec.rb +0 -466
  64. data/spec/lib/rambling/trie/enumerable_spec.rb +0 -50
  65. data/spec/lib/rambling/trie/inspectable_spec.rb +0 -62
  66. data/spec/lib/rambling/trie/nodes/compressed_spec.rb +0 -43
  67. data/spec/lib/rambling/trie/nodes/node_spec.rb +0 -9
  68. data/spec/lib/rambling/trie/nodes/raw_spec.rb +0 -184
  69. data/spec/lib/rambling/trie/readers/plain_text_spec.rb +0 -26
  70. data/spec/lib/rambling/trie/readers/reader_spec.rb +0 -14
  71. data/spec/lib/rambling/trie/serializers/file_spec.rb +0 -11
  72. data/spec/lib/rambling/trie/serializers/marshal_spec.rb +0 -10
  73. data/spec/lib/rambling/trie/serializers/serializer_spec.rb +0 -21
  74. data/spec/lib/rambling/trie/serializers/yaml_spec.rb +0 -10
  75. data/spec/lib/rambling/trie/serializers/zip_spec.rb +0 -36
  76. data/spec/lib/rambling/trie/stringifyable_spec.rb +0 -89
  77. data/spec/lib/rambling/trie_spec.rb +0 -244
  78. data/spec/spec_helper.rb +0 -42
  79. data/spec/support/config.rb +0 -15
  80. data/spec/support/helpers/add_word.rb +0 -20
  81. data/spec/support/helpers/one_line_heredoc.rb +0 -11
  82. data/spec/support/shared_examples/a_compressible_trie.rb +0 -46
  83. data/spec/support/shared_examples/a_container_partial_word.rb +0 -17
  84. data/spec/support/shared_examples/a_container_scan.rb +0 -14
  85. data/spec/support/shared_examples/a_container_word.rb +0 -43
  86. data/spec/support/shared_examples/a_container_words_within.rb +0 -44
  87. data/spec/support/shared_examples/a_serializable_trie.rb +0 -26
  88. data/spec/support/shared_examples/a_serializer.rb +0 -60
  89. data/spec/support/shared_examples/a_trie_data_structure.rb +0 -45
  90. data/spec/support/shared_examples/a_trie_node.rb +0 -135
  91. data/spec/support/shared_examples/a_trie_node_implementation.rb +0 -149
  92. data/spec/tmp/.gitkeep +0 -0
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Inspectable do
6
- let(:node) { Rambling::Trie::Nodes::Raw.new }
7
-
8
- before do
9
- add_words node, %w(only three words)
10
- end
11
-
12
- describe '#inspect' do
13
- let(:child) { node[:o] }
14
- let(:terminal_node) { node[:o][:n][:l][:y] }
15
-
16
- it 'returns a pretty printed version of the parent node' do
17
- expect(node.inspect).to eq one_line <<~RAW
18
- #<Rambling::Trie::Nodes::Raw letter: nil,
19
- terminal: nil,
20
- children: [:o, :t, :w]>
21
- RAW
22
- end
23
-
24
- it 'returns a pretty printed version of the child node' do
25
- expect(child.inspect).to eq one_line <<~CHILD
26
- #<Rambling::Trie::Nodes::Raw letter: :o,
27
- terminal: nil,
28
- children: [:n]>
29
- CHILD
30
- end
31
-
32
- it 'returns a pretty printed version of the terminal node' do
33
- expect(terminal_node.inspect).to eq one_line <<~TERMINAL
34
- #<Rambling::Trie::Nodes::Raw letter: :y,
35
- terminal: true,
36
- children: []>
37
- TERMINAL
38
- end
39
-
40
- context 'with a compressed node' do
41
- let(:compressor) { Rambling::Trie::Compressor.new }
42
- let(:compressed) { compressor.compress node }
43
- let(:compressed_child) { compressed[:o] }
44
-
45
- it 'returns a pretty printed version of the compressed parent node' do
46
- expect(compressed.inspect).to eq one_line <<~COMPRESSED
47
- #<Rambling::Trie::Nodes::Compressed letter: nil,
48
- terminal: nil,
49
- children: [:o, :t, :w]>
50
- COMPRESSED
51
- end
52
-
53
- it 'returns a pretty printed version of the compressed child node' do
54
- expect(compressed_child.inspect).to eq one_line <<~CHILD
55
- #<Rambling::Trie::Nodes::Compressed letter: :only,
56
- terminal: true,
57
- children: []>
58
- CHILD
59
- end
60
- end
61
- end
62
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Nodes::Compressed do
6
- let(:raw_node) { Rambling::Trie::Nodes::Raw.new }
7
- let(:compressor) { Rambling::Trie::Compressor.new }
8
- let(:node) { compressor.compress raw_node }
9
-
10
- describe '#new' do
11
- it 'is not a word' do
12
- expect(node).not_to be_word
13
- end
14
- end
15
-
16
- it_behaves_like 'a trie node implementation' do
17
- def add_word_to_tree word
18
- add_word raw_node, word
19
- end
20
-
21
- def add_words_to_tree words
22
- add_words raw_node, words
23
- end
24
-
25
- def assign_letter letter
26
- raw_node.letter = letter
27
- end
28
- end
29
-
30
- describe '#compressed?' do
31
- it 'returns true' do
32
- expect(node).to be_compressed
33
- end
34
- end
35
-
36
- describe '#add' do
37
- it 'raises an error' do
38
- expect do
39
- add_word node, 'restaurant'
40
- end.to raise_error Rambling::Trie::InvalidOperation
41
- end
42
- end
43
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Nodes::Node do
6
- it_behaves_like 'a trie node' do
7
- let(:node) { described_class.new }
8
- end
9
- end
@@ -1,184 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Nodes::Raw do
6
- let(:node) { described_class.new }
7
-
8
- describe '#new' do
9
- it 'is not a word' do
10
- expect(node).not_to be_word
11
- end
12
- end
13
-
14
- it_behaves_like 'a trie node implementation' do
15
- def add_word_to_tree word
16
- add_word node, word
17
- end
18
-
19
- def add_words_to_tree words
20
- add_words node, words
21
- end
22
-
23
- def assign_letter letter
24
- node.letter = letter
25
- end
26
- end
27
-
28
- describe '#compressed?' do
29
- it 'returns false' do
30
- expect(node).not_to be_compressed
31
- end
32
- end
33
-
34
- describe '#add' do
35
- context 'when the node has no branches' do
36
- before { add_word node, 'abc' }
37
-
38
- it 'adds only one child' do
39
- expect(node.children.size).to eq 1
40
- end
41
-
42
- # rubocop:disable RSpec/MultipleExpectations
43
- it 'adds the full subtree' do
44
- expect(node[:a]).not_to be_nil
45
- expect(node[:a][:b]).not_to be_nil
46
- expect(node[:a][:b][:c]).not_to be_nil
47
- end
48
- # rubocop:enable RSpec/MultipleExpectations
49
-
50
- # rubocop:disable RSpec/MultipleExpectations
51
- it 'marks only the last child as terminal' do
52
- expect(node).not_to be_terminal
53
- expect(node[:a]).not_to be_terminal
54
- expect(node[:a][:b]).not_to be_terminal
55
- expect(node[:a][:b][:c]).to be_terminal
56
- end
57
- # rubocop:enable RSpec/MultipleExpectations
58
- end
59
-
60
- context 'when a word is added more than once' do
61
- before do
62
- add_word node, 'ack'
63
- add_word node, 'ack'
64
- end
65
-
66
- # rubocop:disable RSpec/MultipleExpectations
67
- it 'only counts it once' do
68
- expect(node.children.size).to eq 1
69
- expect(node[:a].children.size).to eq 1
70
- expect(node[:a][:c].children.size).to eq 1
71
- expect(node[:a][:c][:k].children.size).to eq 0
72
- end
73
- # rubocop:enable RSpec/MultipleExpectations
74
-
75
- # rubocop:disable RSpec/MultipleExpectations
76
- it 'does not change the terminal nodes in the tree' do
77
- expect(node).not_to be_terminal
78
- expect(node[:a]).not_to be_terminal
79
- expect(node[:a][:c]).not_to be_terminal
80
- expect(node[:a][:c][:k]).to be_terminal
81
- end
82
- # rubocop:enable RSpec/MultipleExpectations
83
-
84
- it 'still returns the "added" node' do
85
- child = add_word node, 'ack'
86
- expect(child.letter).to eq :a
87
- end
88
- end
89
-
90
- context 'when the word does not exist in the tree but the letters do' do
91
- before { add_words node, %w(ack a) }
92
-
93
- it 'does not add another branch' do
94
- expect(node.children.size).to eq 1
95
- end
96
-
97
- # rubocop:disable RSpec/MultipleExpectations
98
- it 'marks the corresponding node as terminal' do
99
- expect(node).not_to be_terminal
100
- expect(node[:a]).to be_terminal
101
- expect(node[:a][:c]).not_to be_terminal
102
- expect(node[:a][:c][:k]).to be_terminal
103
- end
104
- # rubocop:enable RSpec/MultipleExpectations
105
-
106
- it 'returns the added node' do
107
- child = add_word node, 'a'
108
- expect(child.letter).to eq :a
109
- end
110
- end
111
-
112
- context 'when the node has a letter and a parent' do
113
- let(:parent) { described_class.new }
114
- let(:node) { described_class.new :a, parent }
115
-
116
- context 'when adding an empty string' do
117
- before { add_word node, '' }
118
-
119
- it 'does not alter the node letter' do
120
- expect(node.letter).to eq :a
121
- end
122
-
123
- it 'does not change the node children' do
124
- expect(node.children.size).to eq 0
125
- end
126
-
127
- it 'changes the node to terminal' do
128
- expect(node).to be_terminal
129
- end
130
- end
131
-
132
- context 'when adding a one letter word' do
133
- before { add_word node, 'b' }
134
-
135
- it 'does not alter the node letter' do
136
- expect(node.letter).to eq :a
137
- end
138
-
139
- # rubocop:disable RSpec/MultipleExpectations
140
- it 'adds a child with the expected letter' do
141
- expect(node.children.size).to eq 1
142
- expect(node.children.first.letter).to eq :b
143
- end
144
- # rubocop:enable RSpec/MultipleExpectations
145
-
146
- it 'reports it has the expected letter a key' do
147
- expect(node).to have_key(:b)
148
- end
149
-
150
- it 'returns the child corresponding to the key' do
151
- expect(node[:b]).to eq node.children_tree[:b]
152
- end
153
-
154
- it 'does not mark itself as terminal' do
155
- expect(node).not_to be_terminal
156
- end
157
-
158
- it 'marks the first child as terminal' do
159
- expect(node[:b]).to be_terminal
160
- end
161
- end
162
-
163
- context 'when adding a large word' do
164
- before { add_word node, 'mplified' }
165
-
166
- it 'marks the last letter as terminal' do
167
- expect(node[:m][:p][:l][:i][:f][:i][:e][:d]).to be_terminal
168
- end
169
-
170
- # rubocop:disable RSpec/ExampleLength, RSpec/MultipleExpectations
171
- it 'does not mark any other letter as terminal' do
172
- expect(node[:m][:p][:l][:i][:f][:i][:e]).not_to be_terminal
173
- expect(node[:m][:p][:l][:i][:f][:i]).not_to be_terminal
174
- expect(node[:m][:p][:l][:i][:f]).not_to be_terminal
175
- expect(node[:m][:p][:l][:i]).not_to be_terminal
176
- expect(node[:m][:p][:l]).not_to be_terminal
177
- expect(node[:m][:p]).not_to be_terminal
178
- expect(node[:m]).not_to be_terminal
179
- end
180
- # rubocop:enable RSpec/ExampleLength, RSpec/MultipleExpectations
181
- end
182
- end
183
- end
184
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Readers::PlainText do
6
- subject(:reader) { described_class.new }
7
-
8
- describe '#each_word' do
9
- let(:filepath) { File.join(::SPEC_ROOT, 'assets', 'test_words.en_US.txt') }
10
- let(:words) { File.readlines(filepath).map(&:chomp) }
11
-
12
- it 'yields every word yielded by the file' do
13
- yielded = []
14
- reader.each_word(filepath) { |word| yielded << word }
15
- expect(yielded).to eq words
16
- end
17
-
18
- it 'returns an enumerator when no block is given' do
19
- expect(reader.each_word filepath).to be_an Enumerator
20
- end
21
-
22
- it 'returns the enumerable when a block is given' do
23
- expect(reader.each_word(filepath) { |_| }).to eq reader
24
- end
25
- end
26
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Readers::Reader do
6
- subject(:reader) { described_class.new }
7
-
8
- describe '#load' do
9
- it 'is an abstract method that raises NotImplementedError' do
10
- expect { reader.each_word('any-file.zip') }
11
- .to raise_exception NotImplementedError
12
- end
13
- end
14
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Serializers::File do
6
- it_behaves_like 'a serializer' do
7
- let(:file_format) { :file }
8
- let(:content) { trie.to_a.join ' ' }
9
- let(:format_content) { ->(content) { content } }
10
- end
11
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Serializers::Marshal do
6
- it_behaves_like 'a serializer' do
7
- let(:file_format) { :marshal }
8
- let(:format_content) { Marshal.method(:dump) }
9
- end
10
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Serializers::Serializer do
6
- subject(:serializer) { described_class.new }
7
-
8
- describe '#load' do
9
- it 'is an abstract method that raises NotImplementedError' do
10
- expect { serializer.load('any-file.zip') }
11
- .to raise_exception NotImplementedError
12
- end
13
- end
14
-
15
- describe '#dump' do
16
- it 'is an abstract method that raises NotImplementedError' do
17
- expect { serializer.dump('any contents', 'any-file.zip') }
18
- .to raise_exception NotImplementedError
19
- end
20
- end
21
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Serializers::Yaml do
6
- it_behaves_like 'a serializer' do
7
- let(:file_format) { :yml }
8
- let(:format_content) { YAML.method(:dump) }
9
- end
10
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Serializers::Zip do
6
- {
7
- yaml: YAML.method(:dump),
8
- yml: YAML.method(:dump),
9
- marshal: Marshal.method(:dump),
10
- file: Marshal.method(:dump),
11
- }.each do |file_format, dump_method|
12
- context "with '.#{file_format}'" do
13
- it_behaves_like 'a serializer' do
14
- let(:properties) { Rambling::Trie::Configuration::Properties.new }
15
- let(:serializer) { described_class.new properties }
16
- let(:file_format) { :zip }
17
-
18
- let(:filepath) { File.join tmp_path, "trie-root.#{file_format}.zip" }
19
- let(:format_content) { ->(content) { zip dump_method.call content } }
20
- let(:filename) { File.basename(filepath).gsub %r{\.zip}, '' }
21
-
22
- before { properties.tmp_path = tmp_path }
23
- end
24
- end
25
- end
26
-
27
- def zip content
28
- cursor = Zip::OutputStream.write_buffer do |io|
29
- io.put_next_entry filename
30
- io.write content
31
- end
32
-
33
- cursor.rewind
34
- cursor.read
35
- end
36
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Rambling::Trie::Stringifyable do
6
- describe '#as_word' do
7
- let(:node) { Rambling::Trie::Nodes::Raw.new }
8
-
9
- context 'with an empty node' do
10
- before { add_word node, '' }
11
-
12
- it 'returns nil' do
13
- expect(node.as_word).to be_empty
14
- end
15
- end
16
-
17
- context 'with one letter' do
18
- before do
19
- node.letter = :a
20
- add_word node, ''
21
- end
22
-
23
- it 'returns the expected one letter word' do
24
- expect(node.as_word).to eq 'a'
25
- end
26
- end
27
-
28
- context 'with a small word' do
29
- before do
30
- node.letter = :a
31
- add_word node, 'll'
32
- end
33
-
34
- it 'returns the expected small word' do
35
- expect(node[:l][:l].as_word).to eq 'all'
36
- end
37
-
38
- it 'raises an error for a non terminal node' do
39
- expect { node[:l].as_word }
40
- .to raise_error Rambling::Trie::InvalidOperation
41
- end
42
- end
43
-
44
- context 'with a long word' do
45
- before do
46
- node.letter = :b
47
- add_word node, 'eautiful'
48
- end
49
-
50
- it 'returns the expected long word' do
51
- expect(node[:e][:a][:u][:t][:i][:f][:u][:l].as_word).to eq 'beautiful'
52
- end
53
- end
54
-
55
- context 'with a node with nil letter' do
56
- let(:node) { Rambling::Trie::Nodes::Raw.new nil }
57
-
58
- it 'returns nil' do
59
- expect(node.as_word).to be_empty
60
- end
61
- end
62
-
63
- context 'with a compressed node' do
64
- let(:compressor) { Rambling::Trie::Compressor.new }
65
- let(:compressed_node) { compressor.compress node }
66
-
67
- before do
68
- node.letter = :a
69
- add_words node, %w(m dd)
70
- end
71
-
72
- [
73
- [:m, 'am'],
74
- [:d, 'add'],
75
- ].each do |test_params|
76
- key, expected = test_params
77
-
78
- it "returns the words for terminal nodes (#{key} => #{expected})" do
79
- expect(compressed_node[key].as_word).to eq expected
80
- end
81
- end
82
-
83
- it 'raises an error for non terminal nodes' do
84
- expect { compressed_node.as_word }
85
- .to raise_error Rambling::Trie::InvalidOperation
86
- end
87
- end
88
- end
89
- end