rambling-trie 0.9.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/LICENSE +1 -1
  4. data/README.md +133 -26
  5. data/Rakefile +1 -2
  6. data/lib/rambling/trie.rb +53 -9
  7. data/lib/rambling/trie/comparable.rb +16 -0
  8. data/lib/rambling/trie/compressable.rb +14 -0
  9. data/lib/rambling/trie/compressed_node.rb +38 -14
  10. data/lib/rambling/trie/compressor.rb +14 -10
  11. data/lib/rambling/trie/configuration.rb +11 -0
  12. data/lib/rambling/trie/configuration/properties.rb +66 -0
  13. data/lib/rambling/trie/configuration/provider_collection.rb +101 -0
  14. data/lib/rambling/trie/container.rb +57 -17
  15. data/lib/rambling/trie/enumerable.rb +1 -1
  16. data/lib/rambling/trie/forwardable.rb +9 -4
  17. data/lib/rambling/trie/inspectable.rb +37 -0
  18. data/lib/rambling/trie/invalid_operation.rb +3 -2
  19. data/lib/rambling/trie/missing_node.rb +2 -1
  20. data/lib/rambling/trie/node.rb +40 -30
  21. data/lib/rambling/trie/raw_node.rb +29 -13
  22. data/lib/rambling/trie/readers.rb +11 -0
  23. data/lib/rambling/trie/readers/plain_text.rb +26 -0
  24. data/lib/rambling/trie/serializers.rb +11 -0
  25. data/lib/rambling/trie/serializers/file.rb +25 -0
  26. data/lib/rambling/trie/serializers/marshal.rb +38 -0
  27. data/lib/rambling/trie/serializers/yaml.rb +39 -0
  28. data/lib/rambling/trie/serializers/zip.rb +67 -0
  29. data/lib/rambling/trie/stringifyable.rb +20 -0
  30. data/lib/rambling/trie/version.rb +1 -1
  31. data/rambling-trie.gemspec +2 -2
  32. data/spec/integration/rambling/trie_spec.rb +45 -49
  33. data/spec/lib/rambling/trie/comparable_spec.rb +104 -0
  34. data/spec/lib/rambling/trie/compressed_node_spec.rb +44 -0
  35. data/spec/lib/rambling/trie/configuration/properties_spec.rb +49 -0
  36. data/spec/lib/rambling/trie/configuration/provider_collection_spec.rb +165 -0
  37. data/spec/lib/rambling/trie/container_spec.rb +127 -38
  38. data/spec/lib/rambling/trie/{inspector_spec.rb → inspectable_spec.rb} +7 -5
  39. data/spec/lib/rambling/trie/raw_node_spec.rb +22 -41
  40. data/spec/lib/rambling/trie/readers/plain_text_spec.rb +14 -0
  41. data/spec/lib/rambling/trie/serializers/file_spec.rb +11 -0
  42. data/spec/lib/rambling/trie/serializers/marshal_spec.rb +14 -0
  43. data/spec/lib/rambling/trie/serializers/yaml_spec.rb +14 -0
  44. data/spec/lib/rambling/trie/serializers/zip_spec.rb +30 -0
  45. data/spec/lib/rambling/trie/stringifyable_spec.rb +82 -0
  46. data/spec/lib/rambling/trie_spec.rb +120 -7
  47. data/spec/spec_helper.rb +7 -1
  48. data/spec/support/config.rb +5 -0
  49. data/spec/support/shared_examples/a_compressable_trie.rb +26 -0
  50. data/spec/support/shared_examples/a_serializable_trie.rb +26 -0
  51. data/spec/support/shared_examples/a_serializer.rb +29 -0
  52. data/spec/support/shared_examples/a_trie_data_structure.rb +29 -0
  53. data/spec/tmp/.gitkeep +0 -0
  54. metadata +51 -24
  55. data/lib/rambling/trie/compression.rb +0 -13
  56. data/lib/rambling/trie/inspector.rb +0 -11
  57. data/lib/rambling/trie/plain_text_reader.rb +0 -23
  58. data/lib/rambling/trie/tasks/gem.rb +0 -17
  59. data/lib/rambling/trie/tasks/helpers/path.rb +0 -17
  60. data/lib/rambling/trie/tasks/helpers/performance_report.rb +0 -17
  61. data/lib/rambling/trie/tasks/helpers/time.rb +0 -7
  62. data/lib/rambling/trie/tasks/performance.rb +0 -15
  63. data/lib/rambling/trie/tasks/performance/all.rb +0 -17
  64. data/lib/rambling/trie/tasks/performance/benchmark.rb +0 -201
  65. data/lib/rambling/trie/tasks/performance/directory.rb +0 -11
  66. data/lib/rambling/trie/tasks/performance/flamegraph.rb +0 -119
  67. data/lib/rambling/trie/tasks/performance/profile/call_tree.rb +0 -147
  68. data/lib/rambling/trie/tasks/performance/profile/memory.rb +0 -143
  69. data/spec/lib/rambling/trie/plain_text_reader_spec.rb +0 -18
@@ -1,6 +1,6 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  # Current version of the rambling-trie.
4
- VERSION = '0.9.3'
4
+ VERSION = '1.0.0'.freeze
5
5
  end
6
6
  end
@@ -5,8 +5,8 @@ require 'rambling/trie/version'
5
5
  Gem::Specification.new do |gem|
6
6
  gem.authors = ['Edgar Gonzalez', 'Lilibeth De La Cruz']
7
7
  gem.email = ['edggonzalezg@gmail.com', 'lilibethdlc@gmail.com']
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
- gem.summary = 'A custom implementation of the trie data structure.'
8
+ gem.description = 'The Rambling Trie is a Ruby implementation of the trie data structure, which includes compression abilities and is designed to be very fast to traverse.'
9
+ gem.summary = 'A Ruby implementation of the trie data structure.'
10
10
  gem.homepage = 'http://github.com/gonzedge/rambling-trie'
11
11
  gem.date = Time.now.strftime '%Y-%m-%d'
12
12
 
@@ -1,55 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Rambling::Trie do
4
- shared_examples_for 'a compressable trie' do
5
- context 'and the trie is not compressed' do
6
- it_behaves_like 'a trie data structure'
7
-
8
- it 'does not alter the input' do
9
- word = 'string'
10
- trie.add word
11
-
12
- expect(word).to eq 'string'
13
- end
14
-
15
- it 'is marked as not compressed' do
16
- expect(trie).not_to be_compressed
17
- end
18
- end
19
-
20
- context 'and the trie is compressed' do
21
- before { trie.compress! }
22
-
23
- it_behaves_like 'a trie data structure'
24
-
25
- it 'is marked as compressed' do
26
- expect(trie).to be_compressed
27
- end
28
- end
29
- end
30
-
31
- shared_examples_for 'a trie data structure' do
32
- it 'contains all the words from the file' do
33
- words.each do |word|
34
- expect(trie).to include word
35
- expect(trie.word? word).to be true
36
- end
37
- end
38
-
39
- it 'matches the start of all the words from the file' do
40
- words.each do |word|
41
- expect(trie.match? word).to be true
42
- expect(trie.match? word[0..-2]).to be true
43
- expect(trie.partial_word? word).to be true
44
- expect(trie.partial_word? word[0..-2]).to be true
45
- end
46
- end
47
-
48
- it 'allows iterating over all the words' do
49
- expect(trie.to_a.sort).to eq words.sort
50
- end
51
- end
52
-
53
4
  describe 'with words provided directly' do
54
5
  it_behaves_like 'a compressable trie' do
55
6
  let(:words) { %w[a couple of words for our full trie integration test] }
@@ -79,4 +30,49 @@ describe Rambling::Trie do
79
30
  let(:trie) { Rambling::Trie.create filepath }
80
31
  end
81
32
  end
33
+
34
+ describe 'saving and loading full trie from a file' do
35
+ let(:words_filepath) { File.join ::SPEC_ROOT, 'assets', 'test_words.en_US.txt' }
36
+ let(:words) { File.readlines(words_filepath).map &:chomp! }
37
+ let(:trie_to_serialize) { Rambling::Trie.create words_filepath }
38
+ let(:trie_filename) { File.join ::SPEC_ROOT, '..', 'tmp', 'trie-root' }
39
+
40
+ context 'when serialized with Ruby marshal format (default)' do
41
+ it_behaves_like 'a serializable trie' do
42
+ let(:trie_filepath) { "#{trie_filename}.marshal" }
43
+ let(:loaded_trie) { Rambling::Trie.load trie_filepath }
44
+ let(:serializer) { nil }
45
+ end
46
+ end
47
+
48
+ context 'when serialized with YAML' do
49
+ it_behaves_like 'a serializable trie' do
50
+ let(:trie_filepath) { "#{trie_filename}.yml" }
51
+ let(:loaded_trie) { Rambling::Trie.load trie_filepath }
52
+ let(:serializer) { nil }
53
+ end
54
+ end
55
+
56
+ context 'when serialized with zipped Ruby marshal format' do
57
+ before do
58
+ require 'zip'
59
+ @original_on_exists_proc = ::Zip.on_exists_proc
60
+ @original_continue_on_exists_proc = ::Zip.continue_on_exists_proc
61
+ ::Zip.on_exists_proc = true
62
+ ::Zip.continue_on_exists_proc = true
63
+ end
64
+
65
+ after do
66
+ require 'zip'
67
+ ::Zip.on_exists_proc = @original_on_exists_proc
68
+ ::Zip.continue_on_exists_proc = @original_continue_on_exists_proc
69
+ end
70
+
71
+ it_behaves_like 'a serializable trie' do
72
+ let(:trie_filepath) { "#{trie_filename}.marshal.zip" }
73
+ let(:loaded_trie) { Rambling::Trie.load trie_filepath }
74
+ let(:serializer) { nil }
75
+ end
76
+ end
77
+ end
82
78
  end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rambling::Trie::Comparable do
4
+ describe '#==' do
5
+ let(:node_1) { Rambling::Trie::RawNode.new }
6
+ let(:node_2) { Rambling::Trie::RawNode.new }
7
+
8
+ context 'when the nodes do not have the same letter' do
9
+ before do
10
+ node_1.letter = :a
11
+ node_2.letter = :b
12
+ end
13
+
14
+ it 'returns false' do
15
+ expect(node_1).not_to eq node_2
16
+ end
17
+ end
18
+
19
+ context 'when the nodes have the same letter and no children' do
20
+ before do
21
+ node_1.letter = :a
22
+ node_2.letter = :a
23
+ end
24
+
25
+ it 'returns true' do
26
+ expect(node_1).to eq node_2
27
+ end
28
+ end
29
+
30
+ context 'when the nodes have the same letter and are terminal' do
31
+ before do
32
+ node_1.letter = :a
33
+ node_1.terminal!
34
+
35
+ node_2.letter = :a
36
+ node_2.terminal!
37
+ end
38
+
39
+ it 'returns true' do
40
+ expect(node_1).to eq node_2
41
+ end
42
+ end
43
+
44
+ context 'when the nodes have the same letter and are not terminal' do
45
+ before do
46
+ node_1.letter = :a
47
+ node_2.letter = :a
48
+ end
49
+
50
+ it 'returns true' do
51
+ expect(node_1).to eq node_2
52
+ end
53
+ end
54
+
55
+ context 'when the nodes have the same letter but are not both terminal' do
56
+ before do
57
+ node_1.letter = :a
58
+ node_1.terminal!
59
+
60
+ node_2.letter = :a
61
+ end
62
+ it 'returns false' do
63
+ expect(node_1).not_to eq node_2
64
+ end
65
+ end
66
+
67
+ context 'when the nodes have the same letter and the same children' do
68
+ before do
69
+ node_1.letter = :t
70
+ node_1.add 'hese'
71
+ node_1.add 'hree'
72
+ node_1.add 'hings'
73
+
74
+ node_2.letter = :t
75
+ node_2.add 'hese'
76
+ node_2.add 'hree'
77
+ node_2.add 'hings'
78
+ end
79
+
80
+ it 'returns true' do
81
+ expect(node_1).to eq node_2
82
+ expect(node_1[:h][:e][:s][:e]).to eq node_2[:h][:e][:s][:e]
83
+ end
84
+ end
85
+
86
+ context 'when the nodes have the same letter but different children' do
87
+ before do
88
+ node_1.letter = :t
89
+ node_1.add 'hese'
90
+ node_1.add 'wo'
91
+
92
+ node_2.letter = :t
93
+ node_2.add 'hese'
94
+ node_2.add 'hree'
95
+ node_2.add 'hings'
96
+ end
97
+
98
+ it 'returns false' do
99
+ expect(node_1).not_to eq node_2
100
+ expect(node_1[:h][:e][:s][:e]).to eq node_2[:h][:e][:s][:e]
101
+ end
102
+ end
103
+ end
104
+ end
@@ -152,4 +152,48 @@ describe Rambling::Trie::CompressedNode do
152
152
  end
153
153
  end
154
154
  end
155
+
156
+ describe '#match_prefix' do
157
+ let(:raw_node) { Rambling::Trie::RawNode.new }
158
+ let(:compressor) { Rambling::Trie::Compressor.new }
159
+ let(:node) { compressor.compress raw_node }
160
+
161
+ before do
162
+ raw_node.letter = :i
163
+ raw_node.add 'gnite'
164
+ raw_node.add 'mport'
165
+ raw_node.add 'mportant'
166
+ raw_node.add 'mportantly'
167
+ end
168
+
169
+ context 'when the node is terminal' do
170
+ before do
171
+ raw_node.terminal!
172
+ end
173
+
174
+ it 'adds itself to the words' do
175
+ expect(node.match_prefix %w(g n i t e)).to include 'i'
176
+ end
177
+ end
178
+
179
+ context 'when the node is not terminal' do
180
+ it 'does not add itself to the words' do
181
+ expect(node.match_prefix %w(g n i t e)).not_to include 'i'
182
+ end
183
+ end
184
+
185
+ context 'when the first few chars match a terminal node' do
186
+ it 'adds those terminal nodes to the words' do
187
+ words = node.match_prefix(%w(m p o r t a n t l y)).to_a
188
+ expect(words).to include 'import', 'important', 'importantly'
189
+ end
190
+ end
191
+
192
+ context 'when the first few chars do not match a terminal node' do
193
+ it 'does not add any other words found' do
194
+ words = node.match_prefix(%w(m p m p o r t a n t l y)).to_a
195
+ expect(words).not_to include 'import', 'important', 'importantly'
196
+ end
197
+ end
198
+ end
155
199
  end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rambling::Trie::Configuration::Properties do
4
+ let(:properties) { Rambling::Trie::Configuration::Properties.new }
5
+
6
+ describe '.new' do
7
+ it 'configures the serializers' do
8
+ serializers = properties.serializers
9
+ expect(serializers.keys).to match_array %i(marshal yaml yml zip)
10
+
11
+ expect(serializers[:marshal]).to be_instance_of Rambling::Trie::Serializers::Marshal
12
+ expect(serializers[:yaml]).to be_instance_of Rambling::Trie::Serializers::Yaml
13
+ expect(serializers[:yml]).to be_instance_of Rambling::Trie::Serializers::Yaml
14
+ expect(serializers[:zip]).to be_instance_of Rambling::Trie::Serializers::Zip
15
+ end
16
+
17
+ it 'configures the readers' do
18
+ readers = properties.readers
19
+ expect(readers.keys).to match_array %i(txt)
20
+
21
+ expect(readers[:txt]).to be_instance_of Rambling::Trie::Readers::PlainText
22
+ end
23
+
24
+ it 'configures the compressor' do
25
+ expect(properties.compressor).to be_instance_of Rambling::Trie::Compressor
26
+ end
27
+
28
+ it 'configures the root_builder' do
29
+ expect(properties.root_builder.call).to be_instance_of Rambling::Trie::RawNode
30
+ end
31
+ end
32
+
33
+ describe '#reset' do
34
+ before do
35
+ properties.serializers.add :test, 'test'
36
+ properties.readers.add :test, 'test'
37
+ end
38
+
39
+ it 'resets the serializers and readers to initial values' do
40
+ expect(properties.serializers.keys).to include :test
41
+ expect(properties.readers.keys).to include :test
42
+
43
+ properties.reset
44
+
45
+ expect(properties.serializers.keys).not_to include :test
46
+ expect(properties.readers.keys).not_to include :test
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rambling::Trie::Configuration::ProviderCollection do
4
+ let(:configured_default) { nil }
5
+ let(:configured_providers) do
6
+ { one: first_provider, two: second_provider }
7
+ end
8
+
9
+ let(:first_provider) { double :first_provider }
10
+ let(:second_provider) { double :second_provider }
11
+
12
+ let(:provider_collection) do
13
+ Rambling::Trie::Configuration::ProviderCollection.new(
14
+ 'provider',
15
+ configured_providers,
16
+ configured_default
17
+ )
18
+ end
19
+
20
+ describe '.new' do
21
+ it 'has a name' do
22
+ expect(provider_collection.name).to eq 'provider'
23
+ end
24
+
25
+ it 'has the given providers' do
26
+ expect(provider_collection.providers)
27
+ .to eq one: first_provider, two: second_provider
28
+ end
29
+
30
+ it 'has a default provider' do
31
+ expect(provider_collection.default).to eq first_provider
32
+ end
33
+
34
+ context 'when a default is provided' do
35
+ let(:configured_default) { second_provider }
36
+
37
+ it 'has that as the default provider' do
38
+ expect(provider_collection.default).to eq second_provider
39
+ end
40
+ end
41
+ end
42
+
43
+ describe 'aliases and delegates' do
44
+ let(:providers) { provider_collection.providers }
45
+
46
+ before do
47
+ allow(providers) .to receive_messages(
48
+ :[] => nil,
49
+ :[]= => nil,
50
+ keys: nil,
51
+ values: nil,
52
+ )
53
+ end
54
+
55
+ it 'delegates #[] to providers' do
56
+ provider_collection[:key]
57
+ expect(providers).to have_received(:[]).with :key
58
+ end
59
+
60
+ it 'delegates #[]= to providers' do
61
+ provider_collection[:key] = 'hello'
62
+ expect(providers).to have_received(:[]=).with :key, 'hello'
63
+ end
64
+
65
+ it 'delegates #keys to providers' do
66
+ provider_collection.keys
67
+ expect(providers).to have_received :keys
68
+ end
69
+
70
+ it 'delegates #values to providers' do
71
+ provider_collection.values
72
+ expect(providers).to have_received :values
73
+ end
74
+ end
75
+
76
+ describe '#add' do
77
+ let(:provider) { double :provider }
78
+
79
+ before do
80
+ provider_collection.add :three, provider
81
+ end
82
+
83
+ it 'adds a new provider' do
84
+ expect(provider_collection.providers[:three]).to eq provider
85
+ end
86
+ end
87
+
88
+ describe '#default=' do
89
+ let(:other_provider) { double :other_provider }
90
+
91
+ context 'when the given value is in the providers list' do
92
+ it 'changes the default provider' do
93
+ provider_collection.default = second_provider
94
+ expect(provider_collection.default).to eq second_provider
95
+ end
96
+ end
97
+
98
+ context 'when the given value is not in the providers list' do
99
+ it 'does not change the default provider' do
100
+ expect do
101
+ begin
102
+ provider_collection.default = other_provider
103
+ rescue
104
+ end
105
+ end.not_to change { provider_collection.default }
106
+ end
107
+
108
+ it 'raises an ArgumentError' do
109
+ expect do
110
+ provider_collection.default = other_provider
111
+ end.to raise_error ArgumentError
112
+ end
113
+ end
114
+
115
+ context 'when the providers list is empty' do
116
+ let(:configured_providers) do
117
+ {}
118
+ end
119
+
120
+ it 'accepts nil' do
121
+ provider_collection.default = nil
122
+ expect(provider_collection.default).to be_nil
123
+ end
124
+
125
+ it 'raises an ArgumentError for any other provider' do
126
+ expect do
127
+ provider_collection.default = other_provider
128
+ end.to raise_error ArgumentError
129
+ expect(provider_collection.default).to be_nil
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '#resolve' do
135
+ context 'when the file extension is one of the providers' do
136
+ it 'returns the corresponding provider' do
137
+ expect(provider_collection.resolve 'hola.one').to eq first_provider
138
+ expect(provider_collection.resolve 'hola.two').to eq second_provider
139
+ end
140
+ end
141
+
142
+ context 'when the file extension is not one of the providers' do
143
+ it 'returns the default provider' do
144
+ expect(provider_collection.resolve 'hola.unknown').to eq first_provider
145
+ expect(provider_collection.resolve 'hola').to eq first_provider
146
+ end
147
+ end
148
+ end
149
+
150
+ describe '#reset' do
151
+ let(:configured_default) { second_provider }
152
+ let(:provider) { double :provider }
153
+
154
+ before do
155
+ provider_collection.add :three, provider
156
+ provider_collection.default = provider
157
+ end
158
+
159
+ it 'resets to back to the initially configured values' do
160
+ provider_collection.reset
161
+ expect(provider_collection[:three]).to be_nil
162
+ expect(provider_collection.default).to eq second_provider
163
+ end
164
+ end
165
+ end