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.
- 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.
|