rambling-trie-opal 2.1.1
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 +7 -0
- data/Gemfile +26 -0
- data/Guardfile +10 -0
- data/LICENSE +26 -0
- data/README.md +301 -0
- data/Rakefile +15 -0
- data/lib/rambling-trie.rb +3 -0
- data/lib/rambling/trie.rb +119 -0
- data/lib/rambling/trie/comparable.rb +19 -0
- data/lib/rambling/trie/compressible.rb +16 -0
- data/lib/rambling/trie/compressor.rb +64 -0
- data/lib/rambling/trie/configuration.rb +16 -0
- data/lib/rambling/trie/configuration/properties.rb +75 -0
- data/lib/rambling/trie/configuration/provider_collection.rb +122 -0
- data/lib/rambling/trie/container.rb +226 -0
- data/lib/rambling/trie/enumerable.rb +29 -0
- data/lib/rambling/trie/inspectable.rb +39 -0
- data/lib/rambling/trie/invalid_operation.rb +15 -0
- data/lib/rambling/trie/nodes.rb +18 -0
- data/lib/rambling/trie/nodes/compressed.rb +98 -0
- data/lib/rambling/trie/nodes/missing.rb +12 -0
- data/lib/rambling/trie/nodes/node.rb +183 -0
- data/lib/rambling/trie/nodes/raw.rb +82 -0
- data/lib/rambling/trie/readers.rb +15 -0
- data/lib/rambling/trie/readers/plain_text.rb +18 -0
- data/lib/rambling/trie/serializers.rb +18 -0
- data/lib/rambling/trie/serializers/file.rb +27 -0
- data/lib/rambling/trie/serializers/marshal.rb +48 -0
- data/lib/rambling/trie/serializers/yaml.rb +55 -0
- data/lib/rambling/trie/serializers/zip.rb +74 -0
- data/lib/rambling/trie/stringifyable.rb +26 -0
- data/lib/rambling/trie/version.rb +8 -0
- data/rambling-trie-opal.gemspec +36 -0
- data/spec/assets/test_words.en_US.txt +23 -0
- data/spec/assets/test_words.es_DO.txt +24 -0
- data/spec/integration/rambling/trie_spec.rb +87 -0
- data/spec/lib/rambling/trie/comparable_spec.rb +97 -0
- data/spec/lib/rambling/trie/compressor_spec.rb +108 -0
- data/spec/lib/rambling/trie/configuration/properties_spec.rb +57 -0
- data/spec/lib/rambling/trie/configuration/provider_collection_spec.rb +149 -0
- data/spec/lib/rambling/trie/container_spec.rb +591 -0
- data/spec/lib/rambling/trie/enumerable_spec.rb +42 -0
- data/spec/lib/rambling/trie/inspectable_spec.rb +56 -0
- data/spec/lib/rambling/trie/nodes/compressed_spec.rb +37 -0
- data/spec/lib/rambling/trie/nodes/node_spec.rb +9 -0
- data/spec/lib/rambling/trie/nodes/raw_spec.rb +179 -0
- data/spec/lib/rambling/trie/readers/plain_text_spec.rb +16 -0
- data/spec/lib/rambling/trie/serializers/file_spec.rb +13 -0
- data/spec/lib/rambling/trie/serializers/marshal_spec.rb +12 -0
- data/spec/lib/rambling/trie/serializers/yaml_spec.rb +12 -0
- data/spec/lib/rambling/trie/serializers/zip_spec.rb +28 -0
- data/spec/lib/rambling/trie/stringifyable_spec.rb +85 -0
- data/spec/lib/rambling/trie_spec.rb +182 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/config.rb +15 -0
- data/spec/support/helpers/add_word.rb +20 -0
- data/spec/support/helpers/one_line_heredoc.rb +11 -0
- data/spec/support/shared_examples/a_compressible_trie.rb +40 -0
- data/spec/support/shared_examples/a_serializable_trie.rb +30 -0
- data/spec/support/shared_examples/a_serializer.rb +37 -0
- data/spec/support/shared_examples/a_trie_data_structure.rb +31 -0
- data/spec/support/shared_examples/a_trie_node.rb +127 -0
- data/spec/support/shared_examples/a_trie_node_implementation.rb +152 -0
- data/spec/tmp/.gitkeep +0 -0
- metadata +179 -0
@@ -0,0 +1,182 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Rambling::Trie do
|
6
|
+
describe '.create' do
|
7
|
+
let(:root) { Rambling::Trie::Nodes::Raw.new }
|
8
|
+
let(:compressor) { Rambling::Trie::Compressor.new }
|
9
|
+
let!(:container) { Rambling::Trie::Container.new root, compressor }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(Rambling::Trie::Container).to receive(:new)
|
13
|
+
.and_yield(container)
|
14
|
+
.and_return container
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns a new instance of the trie container' do
|
18
|
+
expect(Rambling::Trie.create).to eq container
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'with a block' do
|
22
|
+
it 'yields the new container' do
|
23
|
+
yielded = nil
|
24
|
+
Rambling::Trie.create { |trie| yielded = trie }
|
25
|
+
expect(yielded).to eq container
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with a filepath' do
|
30
|
+
let(:filepath) { 'a test filepath' }
|
31
|
+
let(:reader) { double :reader }
|
32
|
+
let(:words) { %w(a couple of test words over here) }
|
33
|
+
|
34
|
+
before do
|
35
|
+
receive_and_yield = receive(:each_word)
|
36
|
+
words.inject(receive_and_yield) do |yielder, word|
|
37
|
+
yielder.and_yield word
|
38
|
+
end
|
39
|
+
|
40
|
+
allow(reader).to receive_and_yield
|
41
|
+
allow(container).to receive :<<
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'loads every word' do
|
45
|
+
Rambling::Trie.create filepath, reader
|
46
|
+
|
47
|
+
words.each do |word|
|
48
|
+
expect(container).to have_received(:<<).with word
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'without any reader' do
|
54
|
+
let(:filepath) { 'a test filepath' }
|
55
|
+
let(:reader) { double :reader, each_word: nil }
|
56
|
+
|
57
|
+
before do
|
58
|
+
Rambling::Trie.config do |c|
|
59
|
+
c.readers.add :default, reader
|
60
|
+
c.readers.default = reader
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'defaults to a plain text reader' do
|
65
|
+
Rambling::Trie.create filepath, nil
|
66
|
+
|
67
|
+
expect(reader).to have_received(:each_word).with filepath
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '.load' do
|
73
|
+
let(:root) { Rambling::Trie::Nodes::Raw.new }
|
74
|
+
let(:compressor) { Rambling::Trie::Compressor.new }
|
75
|
+
let(:container) { Rambling::Trie::Container.new root, compressor }
|
76
|
+
let(:serializer) { double :serializer, load: root }
|
77
|
+
let(:filepath) { 'a path to a file' }
|
78
|
+
|
79
|
+
it 'returns a new container with the loaded root node' do
|
80
|
+
trie = Rambling::Trie.load filepath, serializer
|
81
|
+
expect(trie).to eq container
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'uses the serializer to load the root node from the given filepath' do
|
85
|
+
Rambling::Trie.load filepath, serializer
|
86
|
+
expect(serializer).to have_received(:load).with filepath
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'without a serializer' do
|
90
|
+
let(:marshal_serializer) { double :marshal_serializer, load: nil }
|
91
|
+
let(:default_serializer) { double :default_serializer, load: nil }
|
92
|
+
let(:yaml_serializer) { double :yaml_serializer, load: nil }
|
93
|
+
|
94
|
+
before do
|
95
|
+
Rambling::Trie.config do |c|
|
96
|
+
c.serializers.add :default, default_serializer
|
97
|
+
c.serializers.add :marshal, marshal_serializer
|
98
|
+
c.serializers.add :yml, yaml_serializer
|
99
|
+
c.serializers.add :yaml, yaml_serializer
|
100
|
+
|
101
|
+
c.serializers.default = default_serializer
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'determines the serializer based on the file extension' do
|
106
|
+
Rambling::Trie.load 'test.marshal'
|
107
|
+
expect(marshal_serializer).to have_received(:load).with 'test.marshal'
|
108
|
+
|
109
|
+
Rambling::Trie.load 'test.yml'
|
110
|
+
expect(yaml_serializer).to have_received(:load).with 'test.yml'
|
111
|
+
|
112
|
+
Rambling::Trie.load 'test.yaml'
|
113
|
+
expect(yaml_serializer).to have_received(:load).with 'test.yaml'
|
114
|
+
|
115
|
+
Rambling::Trie.load 'test'
|
116
|
+
expect(default_serializer).to have_received(:load).with 'test'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'with a block' do
|
121
|
+
it 'yields the new container' do
|
122
|
+
yielded = nil
|
123
|
+
|
124
|
+
Rambling::Trie.load filepath, serializer do |trie|
|
125
|
+
yielded = trie
|
126
|
+
end
|
127
|
+
|
128
|
+
expect(yielded).to eq container
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '.dump' do
|
134
|
+
let(:filename) { 'a trie' }
|
135
|
+
let(:root) { double :root }
|
136
|
+
let(:compressor) { double :compressor }
|
137
|
+
let(:trie) { Rambling::Trie::Container.new root, compressor }
|
138
|
+
|
139
|
+
let(:marshal_serializer) { double :marshal_serializer, dump: nil }
|
140
|
+
let(:yaml_serializer) { double :yaml_serializer, dump: nil }
|
141
|
+
let(:default_serializer) { double :default_serializer, dump: nil }
|
142
|
+
|
143
|
+
before do
|
144
|
+
Rambling::Trie.config do |c|
|
145
|
+
c.serializers.add :default, default_serializer
|
146
|
+
c.serializers.add :marshal, marshal_serializer
|
147
|
+
c.serializers.add :yml, yaml_serializer
|
148
|
+
|
149
|
+
c.serializers.default = default_serializer
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'uses the configured default serializer by default' do
|
154
|
+
Rambling::Trie.dump trie, filename
|
155
|
+
expect(default_serializer).to have_received(:dump).with root, filename
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'when provided with a format' do
|
159
|
+
it 'uses the corresponding serializer' do
|
160
|
+
Rambling::Trie.dump trie, "#{filename}.marshal"
|
161
|
+
expect(marshal_serializer).to have_received(:dump)
|
162
|
+
.with root, "#{filename}.marshal"
|
163
|
+
|
164
|
+
Rambling::Trie.dump trie, "#{filename}.yml"
|
165
|
+
expect(yaml_serializer).to have_received(:dump)
|
166
|
+
.with root, "#{filename}.yml"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '.config' do
|
172
|
+
it 'returns the properties' do
|
173
|
+
expect(Rambling::Trie.config).to eq Rambling::Trie.send :properties
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'yields the properties' do
|
177
|
+
yielded = nil
|
178
|
+
Rambling::Trie.config { |c| yielded = c }
|
179
|
+
expect(yielded).to eq Rambling::Trie.send :properties
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
require 'coveralls'
|
5
|
+
|
6
|
+
Coveralls.wear!
|
7
|
+
|
8
|
+
SimpleCov.formatters = [
|
9
|
+
SimpleCov::Formatter::HTMLFormatter,
|
10
|
+
Coveralls::SimpleCov::Formatter,
|
11
|
+
]
|
12
|
+
|
13
|
+
SimpleCov.start do
|
14
|
+
add_filter '/spec/'
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'rspec'
|
18
|
+
require 'rambling-trie'
|
19
|
+
::SPEC_ROOT = File.dirname __FILE__
|
20
|
+
|
21
|
+
RSpec.configure do |config|
|
22
|
+
config.color = true
|
23
|
+
config.tty = true
|
24
|
+
config.formatter = :documentation
|
25
|
+
config.order = :random
|
26
|
+
config.run_all_when_everything_filtered = true
|
27
|
+
config.raise_errors_for_deprecations!
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'support/config'
|
31
|
+
|
32
|
+
%w(
|
33
|
+
a_compressible_trie a_serializable_trie a_serializer a_trie_data_structure
|
34
|
+
a_trie_node a_trie_node_implementation
|
35
|
+
).each do |name|
|
36
|
+
require File.join('support', 'shared_examples', name)
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helpers/add_word'
|
4
|
+
require_relative 'helpers/one_line_heredoc'
|
5
|
+
|
6
|
+
RSpec.configure do |c|
|
7
|
+
c.before do
|
8
|
+
Rambling::Trie.config.reset
|
9
|
+
end
|
10
|
+
|
11
|
+
c.include Support::Helpers::AddWord
|
12
|
+
c.include Support::Helpers::OneLineHeredoc
|
13
|
+
|
14
|
+
RSpec::Matchers.define_negated_matcher :not_change, :change
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Support
|
4
|
+
module Helpers
|
5
|
+
module AddWord
|
6
|
+
def add_words node, words
|
7
|
+
words.each { |word| add_word node, word }
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_word node, word
|
11
|
+
case node
|
12
|
+
when Rambling::Trie::Container
|
13
|
+
node.add word
|
14
|
+
else
|
15
|
+
node.add word.chars.reverse.map(&:to_sym)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
shared_examples_for 'a compressible trie' do
|
4
|
+
context 'and the trie is not compressed' do
|
5
|
+
it_behaves_like 'a trie data structure'
|
6
|
+
|
7
|
+
it 'does not alter the input' do
|
8
|
+
word = 'string'
|
9
|
+
add_word trie, word
|
10
|
+
|
11
|
+
expect(word).to eq 'string'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'is marked as not compressed' do
|
15
|
+
expect(trie).not_to be_compressed
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'and the trie is compressed' do
|
20
|
+
let!(:original_root) { trie.root }
|
21
|
+
let!(:original_keys) { original_root.children_tree.keys }
|
22
|
+
let!(:original_values) { original_root.children_tree.values }
|
23
|
+
|
24
|
+
before do
|
25
|
+
trie.compress!
|
26
|
+
end
|
27
|
+
|
28
|
+
it_behaves_like 'a trie data structure'
|
29
|
+
|
30
|
+
it 'is marked as compressed' do
|
31
|
+
expect(trie).to be_compressed
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'leaves the original root intact' do
|
35
|
+
expect(original_root.children_tree.keys).to eq original_keys
|
36
|
+
expect(trie.children_tree.keys).to eq original_keys
|
37
|
+
expect(trie.children_tree.values).not_to eq original_values
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
shared_examples_for 'a serializable trie' do
|
4
|
+
let(:tmp_path) { File.join ::SPEC_ROOT, 'tmp' }
|
5
|
+
let(:filepath) { File.join tmp_path, "trie-root.#{format}" }
|
6
|
+
|
7
|
+
context 'and the trie is not compressed' do
|
8
|
+
before do
|
9
|
+
Rambling::Trie.dump trie_to_serialize, filepath
|
10
|
+
end
|
11
|
+
|
12
|
+
it_behaves_like 'a compressible trie' do
|
13
|
+
let(:trie) { Rambling::Trie.load filepath }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'and the trie is compressed' do
|
18
|
+
let(:trie) { Rambling::Trie.load filepath }
|
19
|
+
|
20
|
+
before do
|
21
|
+
Rambling::Trie.dump trie_to_serialize.compress!, filepath
|
22
|
+
end
|
23
|
+
|
24
|
+
it_behaves_like 'a trie data structure'
|
25
|
+
|
26
|
+
it 'is marked as compressed' do
|
27
|
+
expect(trie).to be_compressed
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
shared_examples_for 'a serializer' do
|
4
|
+
let(:trie) { Rambling::Trie.create }
|
5
|
+
let(:tmp_path) { File.join ::SPEC_ROOT, 'tmp' }
|
6
|
+
let(:filepath) { File.join tmp_path, "trie-root.#{format}" }
|
7
|
+
let(:content) { trie.root }
|
8
|
+
|
9
|
+
before do
|
10
|
+
trie.concat %w(a few words to validate that load and dump are working)
|
11
|
+
FileUtils.rm_f filepath
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#dump' do
|
15
|
+
before do
|
16
|
+
serializer.dump content, filepath
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'creates the file with the provided path' do
|
20
|
+
expect(File.exist? filepath).to be true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'converts the contents to the appropriate format' do
|
24
|
+
expect(File.read(filepath).size).to be_within(10).of formatted_content.size
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#load' do
|
29
|
+
before do
|
30
|
+
serializer.dump content, filepath
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'loads the dumped object back into memory' do
|
34
|
+
expect(serializer.load filepath).to eq content
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
shared_examples_for 'a trie data structure' do
|
4
|
+
it 'contains all the words previously provided' do
|
5
|
+
words.each do |word|
|
6
|
+
expect(trie).to include word
|
7
|
+
expect(trie.word? word).to be true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'matches the start of all the words from the file' do
|
12
|
+
words.each do |word|
|
13
|
+
expect(trie.match? word).to be true
|
14
|
+
expect(trie.match? word[0..-2]).to be true
|
15
|
+
expect(trie.partial_word? word).to be true
|
16
|
+
expect(trie.partial_word? word[0..-2]).to be true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'identifies words within larger strings' do
|
21
|
+
words.each do |word|
|
22
|
+
phrase = "x#{word}y"
|
23
|
+
expect(trie.words_within phrase).to include word
|
24
|
+
expect(trie.words_within? phrase).to be true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'allows iterating over all the words' do
|
29
|
+
expect(trie.to_a.sort).to eq words.sort
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
shared_examples_for 'a trie node' do
|
4
|
+
let(:node_class) { node.class }
|
5
|
+
|
6
|
+
describe '.new' do
|
7
|
+
it 'has no letter' do
|
8
|
+
expect(node.letter).to be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'has no children' do
|
12
|
+
expect(node.children.size).to eq 0
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'is not terminal' do
|
16
|
+
expect(node).not_to be_terminal
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns empty string as its word' do
|
20
|
+
expect(node.as_word).to be_empty
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'with a letter and a parent' do
|
24
|
+
let(:parent) { node.class.new }
|
25
|
+
let(:node_with_parent) { node_class.new :a, parent }
|
26
|
+
|
27
|
+
it 'does not have any letter' do
|
28
|
+
expect(node_with_parent.letter).to eq :a
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'has no children' do
|
32
|
+
expect(node_with_parent.children.size).to eq 0
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'is not terminal' do
|
36
|
+
expect(node_with_parent).not_to be_terminal
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#root?' do
|
42
|
+
context 'when the node has a parent' do
|
43
|
+
before do
|
44
|
+
node.parent = node
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns false' do
|
48
|
+
expect(node).not_to be_root
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when the node does not have a parent' do
|
53
|
+
before do
|
54
|
+
node.parent = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'returns true' do
|
58
|
+
expect(node).to be_root
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#terminal!' do
|
64
|
+
it 'forces the node to be terminal' do
|
65
|
+
expect(node).not_to be_terminal
|
66
|
+
node.terminal!
|
67
|
+
|
68
|
+
expect(node).to be_terminal
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'returns the node' do
|
72
|
+
expect(node.terminal!).to eq node
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'delegates and aliases' do
|
77
|
+
let(:children_tree) do
|
78
|
+
double(
|
79
|
+
:children_tree,
|
80
|
+
:[] => 'value',
|
81
|
+
:[]= => nil,
|
82
|
+
key?: false,
|
83
|
+
delete: true,
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
before do
|
88
|
+
node.children_tree = children_tree
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'delegates `#[]` to its children tree' do
|
92
|
+
expect(node[:key]).to eq 'value'
|
93
|
+
expect(children_tree).to have_received(:[]).with :key
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'delegates `#[]=` to its children tree' do
|
97
|
+
node[:key] = 'value'
|
98
|
+
expect(children_tree).to have_received(:[]=).with(:key, 'value')
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'delegates `#key?` to its children tree' do
|
102
|
+
allow(children_tree).to receive(:key?)
|
103
|
+
.with(:present_key)
|
104
|
+
.and_return true
|
105
|
+
|
106
|
+
expect(node).to have_key(:present_key)
|
107
|
+
expect(node).not_to have_key(:absent_key)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'delegates `#delete` to its children tree' do
|
111
|
+
expect(node.delete :key).to be true
|
112
|
+
expect(children_tree).to have_received(:delete).with :key
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'delegates `#children` to its children tree values' do
|
116
|
+
children = [double(:child_1), double(:child_2)]
|
117
|
+
allow(children_tree).to receive(:values).and_return children
|
118
|
+
|
119
|
+
expect(node.children).to eq children
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'aliases `#has_key?` to `#key?`' do
|
123
|
+
node.has_key? :nope
|
124
|
+
expect(children_tree).to have_received(:key?).with :nope
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|