rambling-trie 2.4.0 → 2.5.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 +4 -4
- data/Dockerfile +28 -0
- data/Gemfile +20 -8
- data/Guardfile +16 -5
- data/README.md +38 -32
- data/Rakefile +6 -0
- data/Steepfile +35 -0
- data/lib/rambling/trie/comparable.rb +2 -2
- data/lib/rambling/trie/compressible.rb +1 -1
- data/lib/rambling/trie/compressor.rb +22 -19
- data/lib/rambling/trie/configuration/properties.rb +10 -6
- data/lib/rambling/trie/configuration/provider_collection.rb +14 -9
- data/lib/rambling/trie/configuration.rb +2 -3
- data/lib/rambling/trie/container.rb +32 -24
- data/lib/rambling/trie/enumerable.rb +5 -6
- data/lib/rambling/trie/nodes/compressed.rb +26 -16
- data/lib/rambling/trie/nodes/node.rb +35 -12
- data/lib/rambling/trie/nodes/raw.rb +18 -20
- data/lib/rambling/trie/nodes.rb +2 -3
- data/lib/rambling/trie/readers/plain_text.rb +3 -3
- data/lib/rambling/trie/readers.rb +2 -3
- data/lib/rambling/trie/serializers/file.rb +1 -3
- data/lib/rambling/trie/serializers/marshal.rb +4 -4
- data/lib/rambling/trie/serializers/yaml.rb +3 -3
- data/lib/rambling/trie/serializers/zip.rb +13 -5
- data/lib/rambling/trie/serializers.rb +2 -3
- data/lib/rambling/trie/stringifyable.rb +1 -1
- data/lib/rambling/trie/version.rb +1 -1
- data/lib/rambling/trie.rb +12 -15
- data/rambling-trie.gemspec +4 -10
- data/sig/lib/rambling/trie/comparable.rbs +17 -0
- data/sig/lib/rambling/trie/compressible.rbs +17 -0
- data/sig/lib/rambling/trie/compressor.rbs +17 -0
- data/sig/lib/rambling/trie/configuration/properties.rbs +28 -0
- data/sig/lib/rambling/trie/configuration/provider_collection.rbs +47 -0
- data/sig/lib/rambling/trie/container.rbs +69 -0
- data/sig/lib/rambling/trie/enumerable.rbs +23 -0
- data/sig/lib/rambling/trie/inspectable.rbs +27 -0
- data/sig/lib/rambling/trie/invalid_operation.rbs +7 -0
- data/sig/lib/rambling/trie/nodes/compressed.rbs +25 -0
- data/sig/lib/rambling/trie/nodes/missing.rbs +9 -0
- data/sig/lib/rambling/trie/nodes/node.rbs +69 -0
- data/sig/lib/rambling/trie/nodes/raw.rbs +27 -0
- data/sig/lib/rambling/trie/readers/plain_text.rbs +9 -0
- data/sig/lib/rambling/trie/readers/reader.rbs +9 -0
- data/sig/lib/rambling/trie/serializers/file.rbs +8 -0
- data/sig/lib/rambling/trie/serializers/marshal.rbs +13 -0
- data/sig/lib/rambling/trie/serializers/serializer.rbs +10 -0
- data/sig/lib/rambling/trie/serializers/yaml.rbs +13 -0
- data/sig/lib/rambling/trie/serializers/zip.rbs +21 -0
- data/sig/lib/rambling/trie/stringifyable.rbs +21 -0
- data/sig/lib/rambling/trie.rbs +27 -0
- data/sig/lib/zip/entry.rbs +11 -0
- data/sig/lib/zip/file.rbs +11 -0
- metadata +34 -123
- data/spec/assets/test_words.en_US.txt +0 -23
- data/spec/assets/test_words.es_DO.txt +0 -24
- data/spec/integration/rambling/trie_spec.rb +0 -116
- data/spec/lib/rambling/trie/comparable_spec.rb +0 -87
- data/spec/lib/rambling/trie/compressor_spec.rb +0 -111
- data/spec/lib/rambling/trie/configuration/properties_spec.rb +0 -75
- data/spec/lib/rambling/trie/configuration/provider_collection_spec.rb +0 -177
- data/spec/lib/rambling/trie/container_spec.rb +0 -466
- data/spec/lib/rambling/trie/enumerable_spec.rb +0 -50
- data/spec/lib/rambling/trie/inspectable_spec.rb +0 -62
- data/spec/lib/rambling/trie/nodes/compressed_spec.rb +0 -43
- data/spec/lib/rambling/trie/nodes/node_spec.rb +0 -9
- data/spec/lib/rambling/trie/nodes/raw_spec.rb +0 -184
- data/spec/lib/rambling/trie/readers/plain_text_spec.rb +0 -26
- data/spec/lib/rambling/trie/readers/reader_spec.rb +0 -14
- data/spec/lib/rambling/trie/serializers/file_spec.rb +0 -11
- data/spec/lib/rambling/trie/serializers/marshal_spec.rb +0 -10
- data/spec/lib/rambling/trie/serializers/serializer_spec.rb +0 -21
- data/spec/lib/rambling/trie/serializers/yaml_spec.rb +0 -10
- data/spec/lib/rambling/trie/serializers/zip_spec.rb +0 -36
- data/spec/lib/rambling/trie/stringifyable_spec.rb +0 -89
- data/spec/lib/rambling/trie_spec.rb +0 -244
- data/spec/spec_helper.rb +0 -42
- data/spec/support/config.rb +0 -15
- data/spec/support/helpers/add_word.rb +0 -20
- data/spec/support/helpers/one_line_heredoc.rb +0 -11
- data/spec/support/shared_examples/a_compressible_trie.rb +0 -46
- data/spec/support/shared_examples/a_container_partial_word.rb +0 -17
- data/spec/support/shared_examples/a_container_scan.rb +0 -14
- data/spec/support/shared_examples/a_container_word.rb +0 -43
- data/spec/support/shared_examples/a_container_words_within.rb +0 -44
- data/spec/support/shared_examples/a_serializable_trie.rb +0 -26
- data/spec/support/shared_examples/a_serializer.rb +0 -60
- data/spec/support/shared_examples/a_trie_data_structure.rb +0 -45
- data/spec/support/shared_examples/a_trie_node.rb +0 -135
- data/spec/support/shared_examples/a_trie_node_implementation.rb +0 -149
- data/spec/tmp/.gitkeep +0 -0
@@ -28,7 +28,7 @@ module Rambling
|
|
28
28
|
# @see Nodes::Raw#add
|
29
29
|
# @see Nodes::Compressed#add
|
30
30
|
def add word
|
31
|
-
root.add
|
31
|
+
root.add reversed_char_symbols word
|
32
32
|
end
|
33
33
|
|
34
34
|
# Adds all provided words to the trie.
|
@@ -62,16 +62,27 @@ module Rambling
|
|
62
62
|
|
63
63
|
# Checks if a path for a word or partial word exists in the trie.
|
64
64
|
# @param [String] word the word or partial word to look for in the trie.
|
65
|
-
# @return [Boolean]
|
65
|
+
# @return [Boolean] `true` if the word or partial word is found, `false` otherwise.
|
66
66
|
# @see Nodes::Node#partial_word?
|
67
67
|
def partial_word? word = ''
|
68
68
|
root.partial_word? word.chars
|
69
69
|
end
|
70
70
|
|
71
|
+
# Adds all provided words to the trie.
|
72
|
+
# @param [Array<String>] words the words to add the branch from.
|
73
|
+
# @return [Array<Nodes::Node>] the collection of nodes added.
|
74
|
+
# @raise [InvalidOperation] if the trie is already compressed.
|
75
|
+
# @see #concat
|
76
|
+
# @see Nodes::Raw#add
|
77
|
+
# @see Nodes::Compressed#add
|
78
|
+
def push *words
|
79
|
+
concat words
|
80
|
+
end
|
81
|
+
|
71
82
|
# Checks if a whole word exists in the trie.
|
72
83
|
# @param [String] word the word to look for in the trie.
|
73
|
-
# @return [Boolean]
|
74
|
-
#
|
84
|
+
# @return [Boolean] `true` only if the word is found and the last character corresponds to a terminal node,
|
85
|
+
# `false` otherwise.
|
75
86
|
# @see Nodes::Node#word?
|
76
87
|
def word? word = ''
|
77
88
|
root.word? word.chars
|
@@ -87,7 +98,7 @@ module Rambling
|
|
87
98
|
|
88
99
|
# Returns all words within a string that match a word contained in the trie.
|
89
100
|
# @param [String] phrase the string to look for matching words in.
|
90
|
-
# @return [
|
101
|
+
# @return [Array<String>] all the words in the given string that match a word in the trie.
|
91
102
|
# @yield [String] each word found in phrase.
|
92
103
|
def words_within phrase
|
93
104
|
words_within_root(phrase).to_a
|
@@ -95,7 +106,7 @@ module Rambling
|
|
95
106
|
|
96
107
|
# Checks if there are any valid words in a given string.
|
97
108
|
# @param [String] phrase the string to look for matching words in.
|
98
|
-
# @return [Boolean]
|
109
|
+
# @return [Boolean] `true` if any word within phrase is contained in the trie, `false` otherwise.
|
99
110
|
# @see Container#words_within
|
100
111
|
def words_within? phrase
|
101
112
|
words_within_root(phrase).any?
|
@@ -103,7 +114,7 @@ module Rambling
|
|
103
114
|
|
104
115
|
# Compares two trie data structures.
|
105
116
|
# @param [Container] other the trie to compare against.
|
106
|
-
# @return [Boolean]
|
117
|
+
# @return [Boolean] `true` if the tries are equal, `false` otherwise.
|
107
118
|
def == other
|
108
119
|
root == other.root
|
109
120
|
end
|
@@ -114,9 +125,7 @@ module Rambling
|
|
114
125
|
def each
|
115
126
|
return enum_for :each unless block_given?
|
116
127
|
|
117
|
-
root.each
|
118
|
-
yield word
|
119
|
-
end
|
128
|
+
root.each { |word| yield word }
|
120
129
|
end
|
121
130
|
|
122
131
|
# @return [String] a string representation of the container.
|
@@ -141,21 +150,21 @@ module Rambling
|
|
141
150
|
|
142
151
|
# Root node's children tree.
|
143
152
|
# @return [Hash<Symbol, Nodes::Node>] the children tree hash contained in the root node, consisting of
|
144
|
-
#
|
153
|
+
# `:letter => node`.
|
145
154
|
# @see Nodes::Node#children_tree
|
146
155
|
def children_tree
|
147
156
|
root.children_tree
|
148
157
|
end
|
149
158
|
|
150
159
|
# Indicates if the root {Nodes::Node Node} can be compressed or not.
|
151
|
-
# @return [Boolean]
|
160
|
+
# @return [Boolean] `true` for non-{Nodes::Node#terminal? terminal} nodes with one child, `false` otherwise.
|
152
161
|
def compressed?
|
153
162
|
root.compressed?
|
154
163
|
end
|
155
164
|
|
156
165
|
# Array of words contained in the root {Nodes::Node Node}.
|
157
166
|
# @return [Array<String>] all words contained in this trie.
|
158
|
-
# @see https://ruby-doc.org/
|
167
|
+
# @see https://ruby-doc.org/3.3.0/Enumerable.html#method-i-to_a Enumerable#to_a
|
159
168
|
def to_a
|
160
169
|
root.to_a
|
161
170
|
end
|
@@ -190,22 +199,21 @@ module Rambling
|
|
190
199
|
return enum_for :words_within_root, phrase unless block_given?
|
191
200
|
|
192
201
|
chars = phrase.chars
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
end
|
202
|
+
size = chars.length
|
203
|
+
# rubocop:disable Style/CommentedKeyword
|
204
|
+
0.upto(size - 1).each do |starting_index|
|
205
|
+
new_phrase = chars.slice starting_index, size # : Array[String]
|
206
|
+
root.match_prefix(new_phrase) { |word| yield word }
|
207
|
+
end # : Enumerator[String, void]
|
208
|
+
# rubocop:enable Style/CommentedKeyword
|
199
209
|
end
|
200
210
|
|
201
211
|
def compress_root
|
202
|
-
compressor.compress root
|
212
|
+
compressor.compress root # : Nodes::Compressed
|
203
213
|
end
|
204
214
|
|
205
|
-
def
|
206
|
-
|
207
|
-
word.reverse.each_char { |c| symbols << c.to_sym }
|
208
|
-
symbols
|
215
|
+
def reversed_char_symbols word
|
216
|
+
word.reverse.chars.map(&:to_sym).to_a
|
209
217
|
end
|
210
218
|
end
|
211
219
|
end
|
@@ -6,8 +6,11 @@ module Rambling
|
|
6
6
|
module Enumerable
|
7
7
|
include ::Enumerable
|
8
8
|
|
9
|
+
# Empty enumerator constant for early each exits.
|
10
|
+
EMPTY_ENUMERATOR = [].to_enum :each
|
11
|
+
|
9
12
|
# Returns number of words contained in the trie
|
10
|
-
# @see https://ruby-doc.org/
|
13
|
+
# @see https://ruby-doc.org/3.3.0/Enumerable.html#method-i-count Enumerable#count
|
11
14
|
alias_method :size, :count
|
12
15
|
|
13
16
|
# Iterates over the words contained in the trie.
|
@@ -18,11 +21,7 @@ module Rambling
|
|
18
21
|
|
19
22
|
yield as_word if terminal?
|
20
23
|
|
21
|
-
children_tree.each_value
|
22
|
-
child.each do |word|
|
23
|
-
yield word
|
24
|
-
end
|
25
|
-
end
|
24
|
+
children_tree.each_value { |child| child.each { |word| yield word } }
|
26
25
|
|
27
26
|
self
|
28
27
|
end
|
@@ -4,7 +4,19 @@ module Rambling
|
|
4
4
|
module Trie
|
5
5
|
module Nodes
|
6
6
|
# A representation of a node in an compressed trie data structure.
|
7
|
+
# :reek:RepeatedConditional { max_ifs: 4 }
|
7
8
|
class Compressed < Rambling::Trie::Nodes::Node
|
9
|
+
# Creates a new compressed node.
|
10
|
+
# @param [Symbol, nil] letter the Node's letter value.
|
11
|
+
# @param [Node, nil] parent the parent of the current node.
|
12
|
+
# @param [Hash<Symbol, Node>] children_tree the children tree of the current node.
|
13
|
+
def initialize letter = nil, parent = nil, children_tree = {}
|
14
|
+
super
|
15
|
+
|
16
|
+
# Ensure all children have the current compressed node as the parent
|
17
|
+
children_tree.each_value { |child| child.parent = self }
|
18
|
+
end
|
19
|
+
|
8
20
|
# Always raises {Rambling::Trie::InvalidOperation InvalidOperation} when
|
9
21
|
# trying to add a word to the current compressed trie node
|
10
22
|
# @param [String] _ the word to add to the trie.
|
@@ -14,8 +26,8 @@ module Rambling
|
|
14
26
|
raise Rambling::Trie::InvalidOperation, 'Cannot add word to compressed trie'
|
15
27
|
end
|
16
28
|
|
17
|
-
# Always return
|
18
|
-
# @return [Boolean] always
|
29
|
+
# Always return `true` for a compressed node.
|
30
|
+
# @return [Boolean] always `true` for a compressed node.
|
19
31
|
def compressed?
|
20
32
|
true
|
21
33
|
end
|
@@ -23,13 +35,13 @@ module Rambling
|
|
23
35
|
private
|
24
36
|
|
25
37
|
def partial_word_chars? chars
|
26
|
-
child = children_tree[chars.first.to_sym]
|
38
|
+
child = children_tree[(chars.first || raise).to_sym]
|
27
39
|
return false unless child
|
28
40
|
|
29
41
|
child_letter = child.letter.to_s
|
30
42
|
|
31
43
|
if chars.size >= child_letter.size
|
32
|
-
letter = chars.
|
44
|
+
letter = (chars.shift(child_letter.size) || raise).join
|
33
45
|
return child.partial_word? chars if child_letter == letter
|
34
46
|
end
|
35
47
|
|
@@ -39,7 +51,7 @@ module Rambling
|
|
39
51
|
end
|
40
52
|
|
41
53
|
def word_chars? chars
|
42
|
-
letter = chars.
|
54
|
+
letter = chars.shift || raise
|
43
55
|
letter_sym = letter.to_sym
|
44
56
|
|
45
57
|
child = children_tree[letter_sym]
|
@@ -50,7 +62,7 @@ module Rambling
|
|
50
62
|
|
51
63
|
break if chars.empty?
|
52
64
|
|
53
|
-
letter << chars.
|
65
|
+
letter << (chars.shift || raise)
|
54
66
|
letter_sym = letter.to_sym
|
55
67
|
end
|
56
68
|
|
@@ -58,13 +70,13 @@ module Rambling
|
|
58
70
|
end
|
59
71
|
|
60
72
|
def closest_node chars
|
61
|
-
child = children_tree[chars.first.to_sym]
|
73
|
+
child = children_tree[(chars.first || raise).to_sym]
|
62
74
|
return missing unless child
|
63
75
|
|
64
76
|
child_letter = child.letter.to_s
|
65
77
|
|
66
78
|
if chars.size >= child_letter.size
|
67
|
-
letter = chars.
|
79
|
+
letter = (chars.shift(child_letter.size) || raise).join
|
68
80
|
return child.scan chars if child_letter == letter
|
69
81
|
end
|
70
82
|
|
@@ -77,19 +89,17 @@ module Rambling
|
|
77
89
|
def children_match_prefix chars
|
78
90
|
return enum_for :children_match_prefix, chars unless block_given?
|
79
91
|
|
80
|
-
return if chars.empty?
|
92
|
+
return EMPTY_ENUMERATOR if chars.empty?
|
81
93
|
|
82
|
-
child = children_tree[chars.first.to_sym]
|
83
|
-
return unless child
|
94
|
+
child = children_tree[(chars.first || raise).to_sym]
|
95
|
+
return EMPTY_ENUMERATOR unless child
|
84
96
|
|
85
97
|
child_letter = child.letter.to_s
|
86
|
-
letter = chars.
|
98
|
+
letter = (chars.shift(child_letter.size) || raise).join
|
87
99
|
|
88
|
-
return unless child_letter == letter
|
100
|
+
return EMPTY_ENUMERATOR unless child_letter == letter
|
89
101
|
|
90
|
-
child.match_prefix
|
91
|
-
yield word
|
92
|
-
end
|
102
|
+
child.match_prefix(chars) { |word| yield word }
|
93
103
|
end
|
94
104
|
end
|
95
105
|
end
|
@@ -4,6 +4,8 @@ module Rambling
|
|
4
4
|
module Trie
|
5
5
|
module Nodes
|
6
6
|
# A representation of a node in the trie data structure.
|
7
|
+
# :reek:MissingSafeMethod { exclude: [ terminal! ] }
|
8
|
+
# :reek:RepeatedConditional { max_ifs: 3 }
|
7
9
|
class Node
|
8
10
|
include Rambling::Trie::Compressible
|
9
11
|
include Rambling::Trie::Enumerable
|
@@ -22,7 +24,7 @@ module Rambling
|
|
22
24
|
attr_reader :letter
|
23
25
|
|
24
26
|
# Child nodes tree.
|
25
|
-
# @return [Hash<Symbol, Node>] the children tree hash, consisting of
|
27
|
+
# @return [Hash<Symbol, Node>] the children tree hash, consisting of `:letter => node`.
|
26
28
|
attr_accessor :children_tree
|
27
29
|
|
28
30
|
# Parent node.
|
@@ -32,6 +34,7 @@ module Rambling
|
|
32
34
|
# Creates a new node.
|
33
35
|
# @param [Symbol, nil] letter the Node's letter value.
|
34
36
|
# @param [Node, nil] parent the parent of the current node.
|
37
|
+
# @param [Hash<Symbol, Node>] children_tree the children tree of the current node.
|
35
38
|
def initialize letter = nil, parent = nil, children_tree = {}
|
36
39
|
@letter = letter
|
37
40
|
@parent = parent
|
@@ -52,16 +55,18 @@ module Rambling
|
|
52
55
|
# rubocop:disable Lint/UnreachableLoop
|
53
56
|
children_tree.each_value { |child| return child }
|
54
57
|
# rubocop:enable Lint/UnreachableLoop
|
58
|
+
|
59
|
+
nil
|
55
60
|
end
|
56
61
|
|
57
62
|
# Indicates if the current node is the root node.
|
58
|
-
# @return [Boolean]
|
63
|
+
# @return [Boolean] `true` if the node does not have a parent, `false` otherwise.
|
59
64
|
def root?
|
60
65
|
!parent
|
61
66
|
end
|
62
67
|
|
63
68
|
# Indicates if a {Node Node} is terminal or not.
|
64
|
-
# @return [Boolean]
|
69
|
+
# @return [Boolean] `true` for terminal nodes, `false` otherwise.
|
65
70
|
def terminal?
|
66
71
|
!!terminal
|
67
72
|
end
|
@@ -79,7 +84,7 @@ module Rambling
|
|
79
84
|
|
80
85
|
# Checks if a path for a set of characters exists in the trie.
|
81
86
|
# @param [Array<String>] chars the characters to look for in the trie.
|
82
|
-
# @return [Boolean]
|
87
|
+
# @return [Boolean] `true` if the characters are found, `false` otherwise.
|
83
88
|
def partial_word? chars
|
84
89
|
return true if chars.empty?
|
85
90
|
|
@@ -88,7 +93,7 @@ module Rambling
|
|
88
93
|
|
89
94
|
# Checks if a path for set of characters represents a word in the trie.
|
90
95
|
# @param [Array<String>] chars the characters to look for in the trie.
|
91
|
-
# @return [Boolean]
|
96
|
+
# @return [Boolean] `true` if the characters are found and form a word, `false` otherwise.
|
92
97
|
def word? chars = []
|
93
98
|
return terminal? if chars.empty?
|
94
99
|
|
@@ -106,7 +111,7 @@ module Rambling
|
|
106
111
|
end
|
107
112
|
|
108
113
|
# Returns all words that match a prefix of any length within chars.
|
109
|
-
# @param [String] chars the chars to base the prefix on.
|
114
|
+
# @param [Array[String]] chars the chars to base the prefix on.
|
110
115
|
# @return [Enumerator<String>] all the words that match a prefix by chars.
|
111
116
|
# @yield [String] each word found.
|
112
117
|
def match_prefix chars
|
@@ -122,7 +127,7 @@ module Rambling
|
|
122
127
|
# Get {Node Node} corresponding to a given letter.
|
123
128
|
# @param [Symbol] letter the letter to search for in the node.
|
124
129
|
# @return [Node] the node corresponding to that letter.
|
125
|
-
# @see https://ruby-doc.org/
|
130
|
+
# @see https://ruby-doc.org/3.3.0/Hash.html#method-i-5B-5D Hash#[]
|
126
131
|
def [] letter
|
127
132
|
children_tree[letter]
|
128
133
|
end
|
@@ -131,15 +136,15 @@ module Rambling
|
|
131
136
|
# @param [Symbol] letter the letter to insert or update in the node's
|
132
137
|
# @param [Node] node the {Node Node} to assign to that letter.
|
133
138
|
# @return [Node] the node corresponding to the inserted or updated letter.
|
134
|
-
# @see https://ruby-doc.org/
|
139
|
+
# @see https://ruby-doc.org/3.3.0/Hash.html#method-i-5B-5D Hash#[]
|
135
140
|
def []= letter, node
|
136
141
|
children_tree[letter] = node
|
137
142
|
end
|
138
143
|
|
139
144
|
# Check if a {Node Node}'s children tree contains a given letter.
|
140
145
|
# @param [Symbol] letter the letter to search for in the node.
|
141
|
-
# @return [Boolean]
|
142
|
-
# @see https://ruby-doc.org/
|
146
|
+
# @return [Boolean] `true` if the letter is present, `false` otherwise.
|
147
|
+
# @see https://ruby-doc.org/3.3.0/Hash.html#method-i-has_key-3F Hash#key?
|
143
148
|
def key? letter
|
144
149
|
children_tree.key? letter
|
145
150
|
end
|
@@ -147,8 +152,8 @@ module Rambling
|
|
147
152
|
# Delete a given letter and its corresponding {Node Node} from
|
148
153
|
# this {Node Node}'s children tree.
|
149
154
|
# @param [Symbol] letter the letter to delete from the node's children tree.
|
150
|
-
# @return [Node] the node corresponding to the deleted letter.
|
151
|
-
# @see https://ruby-doc.org/
|
155
|
+
# @return [Node, nil] the node corresponding to the deleted letter.
|
156
|
+
# @see https://ruby-doc.org/3.3.0/Hash.html#method-i-delete Hash#delete
|
152
157
|
def delete letter
|
153
158
|
children_tree.delete letter
|
154
159
|
end
|
@@ -164,6 +169,24 @@ module Rambling
|
|
164
169
|
private
|
165
170
|
|
166
171
|
attr_accessor :terminal
|
172
|
+
|
173
|
+
# abstract methods
|
174
|
+
|
175
|
+
def children_match_prefix chars
|
176
|
+
raise NotImplementedError
|
177
|
+
end
|
178
|
+
|
179
|
+
def partial_word_chars? chars
|
180
|
+
raise NotImplementedError
|
181
|
+
end
|
182
|
+
|
183
|
+
def word_chars? chars
|
184
|
+
raise NotImplementedError
|
185
|
+
end
|
186
|
+
|
187
|
+
def closest_node chars
|
188
|
+
raise NotImplementedError
|
189
|
+
end
|
167
190
|
end
|
168
191
|
end
|
169
192
|
end
|
@@ -4,21 +4,23 @@ module Rambling
|
|
4
4
|
module Trie
|
5
5
|
module Nodes
|
6
6
|
# A representation of a node in an uncompressed trie data structure.
|
7
|
+
# :reek:RepeatedConditional { max_ifs: 4 }
|
7
8
|
class Raw < Rambling::Trie::Nodes::Node
|
8
9
|
# Adds a word to the current raw (uncompressed) trie node.
|
9
|
-
# @param [Array<Symbol>]
|
10
|
-
# @return [
|
10
|
+
# @param [Array<Symbol>] reversed_chars the char array to add to the trie, in reverse order.
|
11
|
+
# @return [Node] the added/modified node based on the word added.
|
11
12
|
# @note This method clears the contents of the chars variable.
|
12
|
-
def add
|
13
|
-
if
|
14
|
-
terminal!
|
13
|
+
def add reversed_chars
|
14
|
+
if reversed_chars.empty?
|
15
|
+
terminal! unless root?
|
16
|
+
self
|
15
17
|
else
|
16
|
-
add_to_children_tree
|
18
|
+
add_to_children_tree reversed_chars
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
|
-
# Always return
|
21
|
-
# @return [Boolean] always
|
22
|
+
# Always return `false` for a raw (uncompressed) node.
|
23
|
+
# @return [Boolean] always `false` for a raw (uncompressed) node.
|
22
24
|
def compressed?
|
23
25
|
false
|
24
26
|
end
|
@@ -26,7 +28,7 @@ module Rambling
|
|
26
28
|
private
|
27
29
|
|
28
30
|
def add_to_children_tree chars
|
29
|
-
letter = chars.pop
|
31
|
+
letter = chars.pop || raise
|
30
32
|
child = children_tree[letter] || new_node(letter)
|
31
33
|
child.add chars
|
32
34
|
child
|
@@ -39,7 +41,7 @@ module Rambling
|
|
39
41
|
end
|
40
42
|
|
41
43
|
def partial_word_chars? chars = []
|
42
|
-
letter = chars.shift.to_sym
|
44
|
+
letter = (chars.shift || raise).to_sym
|
43
45
|
child = children_tree[letter]
|
44
46
|
return false unless child
|
45
47
|
|
@@ -47,7 +49,7 @@ module Rambling
|
|
47
49
|
end
|
48
50
|
|
49
51
|
def word_chars? chars = []
|
50
|
-
letter = chars.shift.to_sym
|
52
|
+
letter = (chars.shift || raise).to_sym
|
51
53
|
child = children_tree[letter]
|
52
54
|
return false unless child
|
53
55
|
|
@@ -55,7 +57,7 @@ module Rambling
|
|
55
57
|
end
|
56
58
|
|
57
59
|
def closest_node chars
|
58
|
-
letter = chars.shift.to_sym
|
60
|
+
letter = (chars.shift || raise).to_sym
|
59
61
|
child = children_tree[letter]
|
60
62
|
return missing unless child
|
61
63
|
|
@@ -65,16 +67,12 @@ module Rambling
|
|
65
67
|
def children_match_prefix chars
|
66
68
|
return enum_for :children_match_prefix, chars unless block_given?
|
67
69
|
|
68
|
-
return if chars.empty?
|
70
|
+
return EMPTY_ENUMERATOR if chars.empty?
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
return unless child
|
72
|
+
child = children_tree[(chars.shift || raise).to_sym]
|
73
|
+
return EMPTY_ENUMERATOR unless child
|
74
74
|
|
75
|
-
child.match_prefix
|
76
|
-
yield word
|
77
|
-
end
|
75
|
+
child.match_prefix(chars) { |word| yield word }
|
78
76
|
end
|
79
77
|
end
|
80
78
|
end
|
data/lib/rambling/trie/nodes.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
end
|
3
|
+
path = File.join 'rambling', 'trie', 'nodes'
|
4
|
+
%w(node missing compressed raw).each { |file| require File.join(path, file) }
|
6
5
|
|
7
6
|
module Rambling
|
8
7
|
module Trie
|
@@ -3,14 +3,14 @@
|
|
3
3
|
module Rambling
|
4
4
|
module Trie
|
5
5
|
module Readers
|
6
|
-
# File reader for
|
6
|
+
# File reader for `.txt` files.
|
7
7
|
class PlainText < Reader
|
8
|
-
# Yields each word read from a
|
8
|
+
# Yields each word read from a `.txt` file.
|
9
9
|
# @param [String] filepath the full path of the file to load the words from.
|
10
10
|
# @yield [String] Each line read from the file.
|
11
11
|
# @return [self]
|
12
12
|
def each_word filepath
|
13
|
-
return enum_for :each_word unless block_given?
|
13
|
+
return enum_for :each_word, filepath unless block_given?
|
14
14
|
|
15
15
|
::File.foreach(filepath) { |line| yield line.chomp! }
|
16
16
|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
end
|
3
|
+
path = File.join 'rambling', 'trie', 'readers'
|
4
|
+
%w(reader plain_text).each { |file| require File.join(path, file) }
|
6
5
|
|
7
6
|
module Rambling
|
8
7
|
module Trie
|
@@ -17,9 +17,7 @@ module Rambling
|
|
17
17
|
# @param [String] filepath the filepath to dump the contents to.
|
18
18
|
# @return [Numeric] number of bytes written to disk.
|
19
19
|
def dump contents, filepath
|
20
|
-
::File.
|
21
|
-
f.write contents
|
22
|
-
end
|
20
|
+
::File.write filepath, contents
|
23
21
|
end
|
24
22
|
end
|
25
23
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Rambling
|
4
4
|
module Trie
|
5
5
|
module Serializers
|
6
|
-
# Serializer for Ruby marshal format (
|
6
|
+
# Serializer for Ruby marshal format (`.marshal`) files.
|
7
7
|
class Marshal < Serializer
|
8
8
|
# Creates a new Marshal serializer.
|
9
9
|
# @param [Serializer] serializer the serializer responsible to write to and read from disk.
|
@@ -15,8 +15,8 @@ module Rambling
|
|
15
15
|
# Loads marshaled object from contents in filepath and deserializes it into a {Nodes::Node Node}.
|
16
16
|
# @param [String] filepath the full path of the file to load the marshaled object from.
|
17
17
|
# @return [Nodes::Node] The deserialized {Nodes::Node Node}.
|
18
|
-
# @see https://ruby-doc.org/
|
19
|
-
# @note Use of {https://ruby-doc.org/
|
18
|
+
# @see https://ruby-doc.org/3.3.0/Marshal.html#method-c-load Marshal.load
|
19
|
+
# @note Use of {https://ruby-doc.org/3.3.0/Marshal.html#method-c-load Marshal.load} is generally
|
20
20
|
# discouraged. Only use this with trusted input.
|
21
21
|
def load filepath
|
22
22
|
::Marshal.load serializer.load filepath
|
@@ -26,7 +26,7 @@ module Rambling
|
|
26
26
|
# @param [Nodes::Node] node the node to serialize
|
27
27
|
# @param [String] filepath the full path of the file to dump the marshaled object into.
|
28
28
|
# @return [Numeric] number of bytes written to disk.
|
29
|
-
# @see https://ruby-doc.org/
|
29
|
+
# @see https://ruby-doc.org/3.3.0/Marshal.html#method-c-dump Marshal.dump
|
30
30
|
def dump node, filepath
|
31
31
|
serializer.dump ::Marshal.dump(node), filepath
|
32
32
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Rambling
|
4
4
|
module Trie
|
5
5
|
module Serializers
|
6
|
-
# Serializer for Ruby yaml format (
|
6
|
+
# Serializer for Ruby yaml format (`.yaml`, or `.yml`) files.
|
7
7
|
class Yaml < Serializer
|
8
8
|
# Creates a new Yaml serializer.
|
9
9
|
# @param [Serializer] serializer the serializer responsible to write to and read from disk.
|
@@ -15,7 +15,7 @@ module Rambling
|
|
15
15
|
# Loads serialized object from YAML file in filepath and deserializes it into a {Nodes::Node Node}.
|
16
16
|
# @param [String] filepath the full path of the file to load the serialized YAML object from.
|
17
17
|
# @return [Nodes::Node] The deserialized {Nodes::Node Node}.
|
18
|
-
# @see https://ruby-doc.org/
|
18
|
+
# @see https://ruby-doc.org/3.3.0/exts/psych/Psych.html#method-c-safe_load Psych.safe_load
|
19
19
|
def load filepath
|
20
20
|
require 'yaml'
|
21
21
|
::YAML.safe_load(
|
@@ -33,7 +33,7 @@ module Rambling
|
|
33
33
|
# @param [Nodes::Node] node the node to serialize
|
34
34
|
# @param [String] filepath the full path of the file to dump the YAML object into.
|
35
35
|
# @return [Numeric] number of bytes written to disk.
|
36
|
-
# @see https://ruby-doc.org/
|
36
|
+
# @see https://ruby-doc.org/3.3.0/exts/psych/Psych.html#method-c-dump Psych.dump
|
37
37
|
def dump node, filepath
|
38
38
|
require 'yaml'
|
39
39
|
serializer.dump ::YAML.dump(node), filepath
|
@@ -3,9 +3,9 @@
|
|
3
3
|
module Rambling
|
4
4
|
module Trie
|
5
5
|
module Serializers
|
6
|
-
# Zip file serializer. Dumps/loads contents from
|
7
|
-
# Automatically detects if zip file contains a
|
8
|
-
# or any other registered
|
6
|
+
# Zip file serializer. Dumps/loads contents from `.zip` files.
|
7
|
+
# Automatically detects if zip file contains a `.marshal` or `.yml` file,
|
8
|
+
# or any other registered `:format => serializer` combo.
|
9
9
|
class Zip < Serializer
|
10
10
|
# Creates a new Zip serializer.
|
11
11
|
# @param [Configuration::Properties] properties the configuration
|
@@ -26,10 +26,15 @@ module Rambling
|
|
26
26
|
|
27
27
|
::Zip::File.open filepath do |zip|
|
28
28
|
entry = zip.entries.first
|
29
|
-
|
29
|
+
raise unless entry
|
30
|
+
|
31
|
+
entry_name = entry.name
|
32
|
+
entry_path = path entry_name
|
30
33
|
entry.extract entry_path
|
31
34
|
|
32
|
-
serializer = serializers.resolve
|
35
|
+
serializer = serializers.resolve entry_name
|
36
|
+
raise unless serializer
|
37
|
+
|
33
38
|
serializer.load entry_path
|
34
39
|
end
|
35
40
|
end
|
@@ -48,6 +53,9 @@ module Rambling
|
|
48
53
|
|
49
54
|
entry_path = path filename
|
50
55
|
serializer = serializers.resolve filename
|
56
|
+
|
57
|
+
raise unless serializer
|
58
|
+
|
51
59
|
serializer.dump contents, entry_path
|
52
60
|
|
53
61
|
zip.add filename, entry_path
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
end
|
3
|
+
path = File.join 'rambling', 'trie', 'serializers'
|
4
|
+
%w(serializer file marshal yaml zip).each { |file| require File.join(path, file) }
|
6
5
|
|
7
6
|
module Rambling
|
8
7
|
module Trie
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Rambling
|
4
4
|
module Trie
|
5
|
-
# Provides the
|
5
|
+
# Provides the `String` representation behavior for the trie data structure.
|
6
6
|
module Stringifyable
|
7
7
|
# String representation of the current node, if it is a terminal node.
|
8
8
|
# @return [String] the string representation of the current node.
|