rambling-trie 2.5.1 → 2.7.0
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 +2 -1
- data/Gemfile +2 -0
- data/README.md +28 -20
- data/Steepfile +5 -1
- data/lib/rambling/trie/comparable.rb +4 -2
- data/lib/rambling/trie/compressible.rb +1 -1
- data/lib/rambling/trie/compressor.rb +16 -4
- data/lib/rambling/trie/configuration/properties.rb +3 -1
- data/lib/rambling/trie/configuration/provider_collection.rb +7 -7
- data/lib/rambling/trie/container.rb +46 -21
- data/lib/rambling/trie/enumerable.rb +8 -3
- data/lib/rambling/trie/inspectable.rb +6 -1
- data/lib/rambling/trie/nodes/compressed.rb +27 -19
- data/lib/rambling/trie/nodes/missing.rb +17 -0
- data/lib/rambling/trie/nodes/node.rb +28 -21
- data/lib/rambling/trie/nodes/raw.rb +11 -8
- data/lib/rambling/trie/not_implemented.rb +23 -0
- data/lib/rambling/trie/readers/plain_text.rb +4 -1
- data/lib/rambling/trie/readers/reader.rb +5 -2
- data/lib/rambling/trie/serializers/marshal.rb +5 -0
- data/lib/rambling/trie/serializers/serializer.rb +8 -4
- data/lib/rambling/trie/serializers/yaml.rb +5 -0
- data/lib/rambling/trie/serializers/zip.rb +31 -21
- data/lib/rambling/trie/stringifyable.rb +2 -5
- data/lib/rambling/trie/version.rb +1 -1
- data/lib/rambling/trie.rb +14 -10
- data/rambling-trie.gemspec +1 -1
- data/sig/lib/nilable.rbs +3 -0
- data/sig/lib/rambling/trie/comparable.rbs +6 -4
- data/sig/lib/rambling/trie/compressible.rbs +2 -2
- data/sig/lib/rambling/trie/compressor.rbs +6 -6
- data/sig/lib/rambling/trie/configuration/properties.rbs +5 -5
- data/sig/lib/rambling/trie/configuration/provider_collection.rbs +0 -4
- data/sig/lib/rambling/trie/container.rbs +24 -23
- data/sig/lib/rambling/trie/enumerable.rbs +6 -5
- data/sig/lib/rambling/trie/inspectable.rbs +8 -3
- data/sig/lib/rambling/trie/nodes/compressed.rbs +6 -4
- data/sig/lib/rambling/trie/nodes/missing.rbs +10 -2
- data/sig/lib/rambling/trie/nodes/node.rbs +21 -19
- data/sig/lib/rambling/trie/nodes/raw.rbs +5 -5
- data/sig/lib/rambling/trie/not_implemented.rbs +10 -0
- data/sig/lib/rambling/trie/readers/plain_text.rbs +2 -2
- data/sig/lib/rambling/trie/readers/reader.rbs +2 -0
- data/sig/lib/rambling/trie/serializers/marshal.rbs +1 -1
- data/sig/lib/rambling/trie/serializers/serializer.rbs +2 -0
- data/sig/lib/rambling/trie/serializers/yaml.rbs +1 -1
- data/sig/lib/rambling/trie/serializers/zip.rbs +8 -6
- data/sig/lib/rambling/trie/stringifyable.rbs +4 -4
- data/sig/lib/rambling/trie.rbs +10 -10
- metadata +8 -10
- data/sig/lib/zip/entry.rbs +0 -11
- data/sig/lib/zip/file.rbs +0 -11
|
@@ -7,6 +7,7 @@ module Rambling
|
|
|
7
7
|
# :reek:MissingSafeMethod { exclude: [ terminal! ] }
|
|
8
8
|
# :reek:RepeatedConditional { max_ifs: 3 }
|
|
9
9
|
class Node
|
|
10
|
+
include NotImplemented
|
|
10
11
|
include Rambling::Trie::Compressible
|
|
11
12
|
include Rambling::Trie::Enumerable
|
|
12
13
|
include Rambling::Trie::Comparable
|
|
@@ -31,14 +32,20 @@ module Rambling
|
|
|
31
32
|
# @return [Node, nil] the parent of the current node.
|
|
32
33
|
attr_accessor :parent
|
|
33
34
|
|
|
35
|
+
# Arbitrary value stored in this node
|
|
36
|
+
# @return [TValue, nil] the value stored in this node.
|
|
37
|
+
attr_accessor :value
|
|
38
|
+
|
|
34
39
|
# Creates a new node.
|
|
35
40
|
# @param [Symbol, nil] letter the Node's letter value.
|
|
36
41
|
# @param [Node, nil] parent the parent of the current node.
|
|
37
|
-
# @param [Hash<Symbol, Node
|
|
38
|
-
|
|
42
|
+
# @param [Hash<Symbol, Node>, nil] children_tree the children tree of the current node,
|
|
43
|
+
# or +nil+ to start with an empty tree. When a hash is provided, ownership transfers
|
|
44
|
+
# to the node; callers must not retain references to or mutate the hash after passing it.
|
|
45
|
+
def initialize letter = nil, parent = nil, children_tree = nil
|
|
39
46
|
@letter = letter
|
|
40
47
|
@parent = parent
|
|
41
|
-
@children_tree = children_tree
|
|
48
|
+
@children_tree = children_tree || {}
|
|
42
49
|
end
|
|
43
50
|
|
|
44
51
|
# Child nodes.
|
|
@@ -50,13 +57,7 @@ module Rambling
|
|
|
50
57
|
# First child node.
|
|
51
58
|
# @return [Node, nil] the first child contained in the current node.
|
|
52
59
|
def first_child
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# rubocop:disable Lint/UnreachableLoop
|
|
56
|
-
children_tree.each_value { |child| return child }
|
|
57
|
-
# rubocop:enable Lint/UnreachableLoop
|
|
58
|
-
|
|
59
|
-
nil
|
|
60
|
+
children_tree.each_value.first
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
# Indicates if the current node is the root node.
|
|
@@ -133,7 +134,7 @@ module Rambling
|
|
|
133
134
|
end
|
|
134
135
|
|
|
135
136
|
# Set the {Node Node} that corresponds to a given letter.
|
|
136
|
-
# @param [Symbol] letter the letter to insert or update in the node's
|
|
137
|
+
# @param [Symbol] letter the letter to insert or update in the node's children tree.
|
|
137
138
|
# @param [Node] node the {Node Node} to assign to that letter.
|
|
138
139
|
# @return [Node] the node corresponding to the inserted or updated letter.
|
|
139
140
|
# @see https://ruby-doc.org/3.3.0/Hash.html#method-i-5B-5D Hash#[]
|
|
@@ -170,22 +171,28 @@ module Rambling
|
|
|
170
171
|
|
|
171
172
|
attr_accessor :terminal
|
|
172
173
|
|
|
173
|
-
# abstract
|
|
174
|
-
|
|
175
|
-
def children_match_prefix
|
|
176
|
-
|
|
174
|
+
# @abstract Subclass and override {#children_match_prefix} to match a prefix against child nodes.
|
|
175
|
+
# @raise [NotImplementedError] when not overridden by a subclass
|
|
176
|
+
def children_match_prefix _chars
|
|
177
|
+
not_implemented
|
|
177
178
|
end
|
|
178
179
|
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
# @abstract Subclass and override {#partial_word_chars?} to check for a partial-word path.
|
|
181
|
+
# @raise [NotImplementedError] when not overridden by a subclass
|
|
182
|
+
def partial_word_chars? _chars
|
|
183
|
+
not_implemented
|
|
181
184
|
end
|
|
182
185
|
|
|
183
|
-
|
|
184
|
-
|
|
186
|
+
# @abstract Subclass and override {#word_chars?} to check for a full-word path.
|
|
187
|
+
# @raise [NotImplementedError] when not overridden by a subclass
|
|
188
|
+
def word_chars? _chars
|
|
189
|
+
not_implemented
|
|
185
190
|
end
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
# @abstract Subclass and override {#closest_node} to return the deepest matching node.
|
|
193
|
+
# @raise [NotImplementedError] when not overridden by a subclass
|
|
194
|
+
def closest_node _chars
|
|
195
|
+
not_implemented
|
|
189
196
|
end
|
|
190
197
|
end
|
|
191
198
|
end
|
|
@@ -9,13 +9,16 @@ module Rambling
|
|
|
9
9
|
# Adds a word to the current raw (uncompressed) trie node.
|
|
10
10
|
# @param [Array<Symbol>] reversed_chars the char array to add to the trie, in reverse order.
|
|
11
11
|
# @return [Node] the added/modified node based on the word added.
|
|
12
|
-
# @note This method
|
|
13
|
-
def add reversed_chars
|
|
12
|
+
# @note This method consumes the array by popping each element during recursion, leaving it empty on return.
|
|
13
|
+
def add reversed_chars, value = nil
|
|
14
14
|
if reversed_chars.empty?
|
|
15
|
-
|
|
15
|
+
unless root?
|
|
16
|
+
self.value = value
|
|
17
|
+
terminal!
|
|
18
|
+
end
|
|
16
19
|
self
|
|
17
20
|
else
|
|
18
|
-
add_to_children_tree reversed_chars
|
|
21
|
+
add_to_children_tree reversed_chars, value
|
|
19
22
|
end
|
|
20
23
|
end
|
|
21
24
|
|
|
@@ -27,10 +30,10 @@ module Rambling
|
|
|
27
30
|
|
|
28
31
|
private
|
|
29
32
|
|
|
30
|
-
def add_to_children_tree chars
|
|
33
|
+
def add_to_children_tree chars, value = nil
|
|
31
34
|
letter = chars.pop || raise
|
|
32
35
|
child = children_tree[letter] || new_node(letter)
|
|
33
|
-
child.add chars
|
|
36
|
+
child.add chars, value
|
|
34
37
|
child
|
|
35
38
|
end
|
|
36
39
|
|
|
@@ -67,10 +70,10 @@ module Rambling
|
|
|
67
70
|
def children_match_prefix chars
|
|
68
71
|
return enum_for :children_match_prefix, chars unless block_given?
|
|
69
72
|
|
|
70
|
-
return
|
|
73
|
+
return empty_enum if chars.empty?
|
|
71
74
|
|
|
72
75
|
child = children_tree[(chars.shift || raise).to_sym]
|
|
73
|
-
return
|
|
76
|
+
return empty_enum unless child
|
|
74
77
|
|
|
75
78
|
child.match_prefix(chars) { |word| yield word }
|
|
76
79
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rambling
|
|
4
|
+
# :reek:IrresponsibleModule (unclear why this is necessary here but not in other submodules)
|
|
5
|
+
module Trie
|
|
6
|
+
# Provides a private helper for marking abstract methods.
|
|
7
|
+
# @api private
|
|
8
|
+
module NotImplemented
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# Raises {NotImplementedError} with the concrete class and calling method name.
|
|
12
|
+
# @raise [NotImplementedError] always, with message
|
|
13
|
+
# <code>"ClassName#method_name is not implemented"</code>
|
|
14
|
+
# @return [void]
|
|
15
|
+
def not_implemented
|
|
16
|
+
raise NotImplementedError,
|
|
17
|
+
"#{self.class}##{caller_locations(1, 1)&.first&.label} is not implemented"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private_constant :NotImplemented
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -12,7 +12,10 @@ module Rambling
|
|
|
12
12
|
def each_word filepath
|
|
13
13
|
return enum_for :each_word, filepath unless block_given?
|
|
14
14
|
|
|
15
|
-
::File.foreach(filepath)
|
|
15
|
+
::File.foreach(filepath) do |line|
|
|
16
|
+
line.chomp!
|
|
17
|
+
yield line
|
|
18
|
+
end
|
|
16
19
|
|
|
17
20
|
self
|
|
18
21
|
end
|
|
@@ -5,13 +5,16 @@ module Rambling
|
|
|
5
5
|
module Readers
|
|
6
6
|
# Base class for all readers.
|
|
7
7
|
class Reader
|
|
8
|
+
include NotImplemented
|
|
9
|
+
|
|
8
10
|
# Yields each word read from given file.
|
|
9
11
|
# @abstract Subclass and override {#each_word} to fit to a particular file format.
|
|
10
12
|
# @param [String] filepath the full path of the file to load the words from.
|
|
11
13
|
# @yield [String] Each line read from the file.
|
|
12
14
|
# @return [self]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
# @raise [NotImplementedError] when not overridden by a subclass
|
|
16
|
+
def each_word _filepath
|
|
17
|
+
not_implemented
|
|
15
18
|
end
|
|
16
19
|
end
|
|
17
20
|
end
|
|
@@ -19,6 +19,11 @@ module Rambling
|
|
|
19
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
|
+
warn <<~WARN.chomp.tr("\n", ' ')
|
|
23
|
+
WARNING: Marshal.load is being used to deserialize trie data.
|
|
24
|
+
Loading untrusted input via Marshal.load can execute arbitrary code.
|
|
25
|
+
Only load Marshal data from trusted sources.
|
|
26
|
+
WARN
|
|
22
27
|
::Marshal.load serializer.load filepath
|
|
23
28
|
end
|
|
24
29
|
|
|
@@ -5,12 +5,15 @@ module Rambling
|
|
|
5
5
|
module Serializers
|
|
6
6
|
# Base class for all serializers.
|
|
7
7
|
class Serializer
|
|
8
|
+
include NotImplemented
|
|
9
|
+
|
|
8
10
|
# Loads contents from a specified filepath.
|
|
9
11
|
# @abstract Subclass and override {#load} to parse the desired format.
|
|
10
12
|
# @param [String] filepath the filepath to load contents from.
|
|
11
13
|
# @return [TContents] parsed contents from given file.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
# @raise [NotImplementedError] when not overridden by a subclass
|
|
15
|
+
def load _filepath
|
|
16
|
+
not_implemented
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
# Dumps contents into a specified filepath.
|
|
@@ -18,8 +21,9 @@ module Rambling
|
|
|
18
21
|
# @param [TContents] contents the contents to dump into given file.
|
|
19
22
|
# @param [String] filepath the filepath to dump the contents to.
|
|
20
23
|
# @return [Numeric] number of bytes written to disk.
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
# @raise [NotImplementedError] when not overridden by a subclass
|
|
25
|
+
def dump _contents, _filepath
|
|
26
|
+
not_implemented
|
|
23
27
|
end
|
|
24
28
|
end
|
|
25
29
|
end
|
|
@@ -17,6 +17,11 @@ module Rambling
|
|
|
17
17
|
# @return [Nodes::Node] The deserialized {Nodes::Node Node}.
|
|
18
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
|
+
warn <<~WARN.chomp.tr("\n", ' ')
|
|
21
|
+
WARNING: YAML aliases are enabled.
|
|
22
|
+
Loading untrusted YAML with aliases: true can trigger a billion-laughs memory attack.
|
|
23
|
+
Only load YAML data from trusted sources.
|
|
24
|
+
WARN
|
|
20
25
|
require 'yaml'
|
|
21
26
|
::YAML.safe_load(
|
|
22
27
|
serializer.load(filepath),
|
|
@@ -18,47 +18,47 @@ module Rambling
|
|
|
18
18
|
# Unzip contents from specified filepath and load in contents from
|
|
19
19
|
# unzipped files.
|
|
20
20
|
# @param [String] filepath the filepath to load contents from.
|
|
21
|
-
# @return [
|
|
21
|
+
# @return [Nodes::Node] all contents of the unzipped loaded file.
|
|
22
22
|
# @see https://github.com/rubyzip/rubyzip#reading-a-zip-file Zip
|
|
23
23
|
# reading a file
|
|
24
24
|
def load filepath
|
|
25
25
|
require 'zip'
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
clean_up_tmp_files do |tmp_paths|
|
|
28
|
+
::Zip::File.open filepath do |zip|
|
|
29
|
+
entry = zip.entries.first
|
|
30
|
+
raise unless entry
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
entry_name = entry.name
|
|
33
|
+
entry_path = path_with_random_prefix entry_name
|
|
34
|
+
tmp_paths << entry_path
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
raise unless serializer
|
|
36
|
+
entry.extract ::File.basename(entry_path), destination_directory: tmp_path
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
(serializers.resolve(entry_name) || raise).load entry_path
|
|
39
|
+
end
|
|
39
40
|
end
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
# Dumps contents and zips into a specified filepath.
|
|
43
44
|
# @param [String] contents the contents to dump.
|
|
44
45
|
# @param [String] filepath the filepath to dump the contents to.
|
|
45
|
-
# @return [
|
|
46
|
+
# @return [Integer] number of bytes written to disk.
|
|
46
47
|
# @see https://github.com/rubyzip/rubyzip#basic-zip-archive-creation
|
|
47
48
|
# Zip archive creation
|
|
48
49
|
def dump contents, filepath
|
|
49
50
|
require 'zip'
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
clean_up_tmp_files do |tmp_paths|
|
|
53
|
+
::Zip::File.open filepath, create: true do |zip|
|
|
54
|
+
filename = ::File.basename filepath, '.zip'
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
entry_path = path_with_random_prefix filename
|
|
57
|
+
tmp_paths << entry_path
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
zip.add filename, entry_path
|
|
59
|
+
(serializers.resolve(filename) || raise).dump contents, entry_path
|
|
60
|
+
zip.add filename, entry_path
|
|
61
|
+
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
::File.size filepath
|
|
@@ -76,10 +76,20 @@ module Rambling
|
|
|
76
76
|
properties.tmp_path
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
def
|
|
79
|
+
def path_with_random_prefix filename
|
|
80
80
|
require 'securerandom'
|
|
81
81
|
::File.join tmp_path, "#{SecureRandom.uuid}-#{filename}"
|
|
82
82
|
end
|
|
83
|
+
|
|
84
|
+
def clean_up_tmp_files
|
|
85
|
+
# @type var tmp_paths: Array[String]
|
|
86
|
+
tmp_paths = []
|
|
87
|
+
begin
|
|
88
|
+
yield tmp_paths
|
|
89
|
+
ensure
|
|
90
|
+
tmp_paths.each { |path| ::FileUtils.rm_f path }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
83
93
|
end
|
|
84
94
|
end
|
|
85
95
|
end
|
|
@@ -6,12 +6,9 @@ module Rambling
|
|
|
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.
|
|
9
|
-
# @raise [InvalidOperation] if node
|
|
9
|
+
# @raise [InvalidOperation] if the node has a letter and is not terminal.
|
|
10
10
|
def as_word
|
|
11
|
-
if letter && !terminal?
|
|
12
|
-
raise Rambling::Trie::InvalidOperation,
|
|
13
|
-
'Cannot represent branch as a word'
|
|
14
|
-
end
|
|
11
|
+
raise Rambling::Trie::InvalidOperation, 'Cannot represent branch as a word' if letter && !terminal?
|
|
15
12
|
|
|
16
13
|
to_s
|
|
17
14
|
end
|
data/lib/rambling/trie.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
path = File.join 'rambling', 'trie'
|
|
4
4
|
%w(
|
|
5
|
-
comparable compressible compressor configuration container enumerable inspectable invalid_operation
|
|
5
|
+
comparable compressible compressor configuration container enumerable inspectable invalid_operation not_implemented
|
|
6
6
|
readers serializers stringifyable nodes version
|
|
7
7
|
).each { |file| require File.join(path, file) }
|
|
8
8
|
|
|
@@ -10,6 +10,9 @@ path = File.join 'rambling', 'trie'
|
|
|
10
10
|
module Rambling
|
|
11
11
|
# Entry point for `rambling-trie` API.
|
|
12
12
|
module Trie
|
|
13
|
+
PROPERTIES_MUTEX = Mutex.new
|
|
14
|
+
private_constant :PROPERTIES_MUTEX
|
|
15
|
+
|
|
13
16
|
class << self
|
|
14
17
|
# Creates a new `Rambling::Trie`. Entry point for the `rambling-trie` API.
|
|
15
18
|
# @param [String, nil] filepath the file to load the words from.
|
|
@@ -21,14 +24,12 @@ module Rambling
|
|
|
21
24
|
root = root_builder.call
|
|
22
25
|
|
|
23
26
|
Rambling::Trie::Container.new root, compressor do |container|
|
|
24
|
-
# noinspection RubyMismatchedArgumentType
|
|
25
27
|
if filepath
|
|
26
|
-
reader
|
|
27
|
-
|
|
28
|
-
(reader || raise).each_word(filepath) { |word| container << word }
|
|
28
|
+
input_reader = reader || readers.resolve(filepath) || raise(ArgumentError, "no reader for #{filepath}")
|
|
29
|
+
input_reader.each_word(filepath) { |word| container << word }
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
yield container if block_given?
|
|
32
|
+
yield container if block_given? # steep:ignore
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -37,7 +38,7 @@ module Rambling
|
|
|
37
38
|
# Available formats are `yml`, `marshal`, and `zip` versions of all the
|
|
38
39
|
# previous formats. You can also define your own.
|
|
39
40
|
# @param [String] filepath the file to load the words from.
|
|
40
|
-
# @param [Serializer, nil] serializer the object responsible
|
|
41
|
+
# @param [Serializer, nil] serializer the object responsible for loading the trie from disk.
|
|
41
42
|
# @return [Container] the trie just loaded.
|
|
42
43
|
# @yield [Container] the trie just loaded.
|
|
43
44
|
# @see Rambling::Trie::Serializers Serializers.
|
|
@@ -47,7 +48,7 @@ module Rambling
|
|
|
47
48
|
serializer ||= serializers.resolve filepath
|
|
48
49
|
root = (serializer || raise).load filepath
|
|
49
50
|
Rambling::Trie::Container.new root, compressor do |container|
|
|
50
|
-
yield container if block_given?
|
|
51
|
+
yield container if block_given? # steep:ignore
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
54
|
|
|
@@ -62,7 +63,6 @@ module Rambling
|
|
|
62
63
|
# @see Serializers Serializers.
|
|
63
64
|
def dump trie, filepath, serializer = nil
|
|
64
65
|
serializer ||= serializers.resolve filepath
|
|
65
|
-
# noinspection RubyNilAnalysis
|
|
66
66
|
(serializer || raise).dump trie.root, filepath
|
|
67
67
|
end
|
|
68
68
|
|
|
@@ -77,7 +77,11 @@ module Rambling
|
|
|
77
77
|
private
|
|
78
78
|
|
|
79
79
|
def properties
|
|
80
|
-
@properties
|
|
80
|
+
return @properties if @properties
|
|
81
|
+
|
|
82
|
+
PROPERTIES_MUTEX.synchronize do
|
|
83
|
+
@properties ||= Rambling::Trie::Configuration::Properties.new
|
|
84
|
+
end
|
|
81
85
|
end
|
|
82
86
|
|
|
83
87
|
def readers
|
data/rambling-trie.gemspec
CHANGED
data/sig/lib/nilable.rbs
ADDED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
module Rambling
|
|
2
2
|
module Trie
|
|
3
|
-
module Comparable
|
|
4
|
-
def ==: (Nodes::Node) -> bool
|
|
3
|
+
module Comparable[TValue < BasicObject & _Inspect & _Nilable]
|
|
4
|
+
def ==: (Nodes::Node[TValue]) -> bool
|
|
5
5
|
|
|
6
6
|
private
|
|
7
7
|
|
|
8
8
|
# abstract methods
|
|
9
9
|
|
|
10
|
-
def letter: -> Symbol
|
|
10
|
+
def letter: -> Symbol?
|
|
11
|
+
|
|
12
|
+
def value: -> TValue?
|
|
11
13
|
|
|
12
14
|
def terminal?: -> bool
|
|
13
15
|
|
|
14
|
-
def children_tree: -> Hash[Symbol, Nodes::Node]
|
|
16
|
+
def children_tree: -> Hash[Symbol, Nodes::Node[TValue]]
|
|
15
17
|
end
|
|
16
18
|
end
|
|
17
19
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Rambling
|
|
2
2
|
module Trie
|
|
3
|
-
module Compressible
|
|
3
|
+
module Compressible[TValue < BasicObject & _Inspect & _Nilable]
|
|
4
4
|
def compressible?: -> bool
|
|
5
5
|
|
|
6
6
|
private
|
|
@@ -11,7 +11,7 @@ module Rambling
|
|
|
11
11
|
|
|
12
12
|
def terminal?: -> bool
|
|
13
13
|
|
|
14
|
-
def children_tree: -> Hash[Symbol, Nodes::Node]
|
|
14
|
+
def children_tree: -> Hash[Symbol, Nodes::Node[TValue]]
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
module Rambling
|
|
2
2
|
module Trie
|
|
3
|
-
class Compressor
|
|
4
|
-
def compress: (Nodes::Node?) -> Nodes::Compressed?
|
|
3
|
+
class Compressor[TValue < BasicObject & _Inspect & _Nilable]
|
|
4
|
+
def compress: (Nodes::Node[TValue]?) -> Nodes::Compressed[TValue]?
|
|
5
5
|
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def compress_only_child_and_merge: (Nodes::Node) -> Nodes::Compressed
|
|
8
|
+
def compress_only_child_and_merge: (Nodes::Node[TValue]) -> Nodes::Compressed[TValue]
|
|
9
9
|
|
|
10
|
-
def merge: (Nodes::Node, Nodes::Node) -> Nodes::Compressed
|
|
10
|
+
def merge: (Nodes::Node[TValue], Nodes::Node[TValue]) -> Nodes::Compressed[TValue]
|
|
11
11
|
|
|
12
|
-
def compress_children_and_copy: (Nodes::Node) -> Nodes::Compressed
|
|
12
|
+
def compress_children_and_copy: (Nodes::Node[TValue]) -> Nodes::Compressed[TValue]
|
|
13
13
|
|
|
14
|
-
def compress_children: (Hash[Symbol, Nodes::Node]) -> Hash[Symbol, Nodes::Node]
|
|
14
|
+
def compress_children: (Hash[Symbol, Nodes::Node[TValue]]) -> Hash[Symbol, Nodes::Node[TValue]]
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
end
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module Rambling
|
|
2
2
|
module Trie
|
|
3
3
|
module Configuration
|
|
4
|
-
class Properties
|
|
4
|
+
class Properties[TValue < BasicObject & _Inspect & _Nilable]
|
|
5
5
|
attr_reader readers: ProviderCollection[Readers::Reader]
|
|
6
|
-
attr_reader serializers: ProviderCollection[Serializers::Serializer[Nodes::Node]]
|
|
7
|
-
attr_accessor compressor: Compressor
|
|
8
|
-
attr_accessor root_builder: ^() -> Nodes::Node
|
|
6
|
+
attr_reader serializers: ProviderCollection[Serializers::Serializer[Nodes::Node[TValue]]]
|
|
7
|
+
attr_accessor compressor: Compressor[TValue]
|
|
8
|
+
attr_accessor root_builder: ^() -> Nodes::Node[TValue]
|
|
9
9
|
attr_accessor tmp_path: String
|
|
10
10
|
|
|
11
11
|
def initialize: -> void
|
|
@@ -15,7 +15,7 @@ module Rambling
|
|
|
15
15
|
private
|
|
16
16
|
|
|
17
17
|
attr_writer readers: ProviderCollection[Readers::Reader]
|
|
18
|
-
attr_writer serializers: ProviderCollection[Serializers::Serializer[Nodes::Node]]
|
|
18
|
+
attr_writer serializers: ProviderCollection[Serializers::Serializer[Nodes::Node[TValue]]]
|
|
19
19
|
|
|
20
20
|
def reset_readers: -> void
|
|
21
21
|
|
|
@@ -1,43 +1,44 @@
|
|
|
1
1
|
module Rambling
|
|
2
2
|
module Trie
|
|
3
|
-
class Container
|
|
3
|
+
class Container[TValue < BasicObject & _Inspect & _Nilable]
|
|
4
4
|
include ::Enumerable[String]
|
|
5
5
|
|
|
6
|
-
@compressor: Compressor
|
|
6
|
+
@compressor: Compressor[TValue]
|
|
7
7
|
|
|
8
|
-
attr_reader root: Nodes::Node
|
|
8
|
+
attr_reader root: Nodes::Node[TValue]
|
|
9
9
|
|
|
10
|
-
def initialize: (Nodes::Node, Compressor) ?{ (Container) -> void } -> void
|
|
10
|
+
def initialize: (Nodes::Node[TValue], Compressor[TValue]) ?{ (Container[TValue]) -> void } -> void
|
|
11
11
|
|
|
12
|
-
def add: (String) -> Nodes::Node
|
|
12
|
+
def add: (String, ?TValue?) -> Nodes::Node[TValue]
|
|
13
13
|
|
|
14
|
-
def concat: (Array[String]) -> Array[Nodes::Node]
|
|
14
|
+
def concat: (Array[String], ?Array[TValue?]?) -> Array[Nodes::Node[TValue]]
|
|
15
15
|
|
|
16
|
-
def compress!: -> Container
|
|
16
|
+
def compress!: -> Container[TValue]
|
|
17
17
|
|
|
18
|
-
def compress: -> Container
|
|
18
|
+
def compress: -> Container[TValue]
|
|
19
19
|
|
|
20
|
-
def each: { (String) -> void } ->
|
|
20
|
+
def each: { (String) -> void } -> Container[TValue]
|
|
21
|
+
| -> Enumerator[String, void]
|
|
21
22
|
|
|
22
|
-
def partial_word?: (String) -> bool
|
|
23
|
+
def partial_word?: (?String) -> bool
|
|
23
24
|
|
|
24
|
-
def push: (*String) -> Array[Nodes::Node]
|
|
25
|
+
def push: (*String) -> Array[Nodes::Node[TValue]]
|
|
25
26
|
|
|
26
|
-
def word?: (String) -> bool
|
|
27
|
+
def word?: (?String) -> bool
|
|
27
28
|
|
|
28
|
-
def scan: (String) -> Array[String]
|
|
29
|
+
def scan: (?String) -> Array[String]
|
|
29
30
|
|
|
30
31
|
def words_within: (String) -> Array[String]
|
|
31
32
|
|
|
32
33
|
def words_within?: (String) -> bool
|
|
33
34
|
|
|
34
|
-
def ==: (Container) -> bool
|
|
35
|
+
def ==: (Container[TValue]) -> bool
|
|
35
36
|
|
|
36
|
-
def []: (Symbol) -> Nodes::Node
|
|
37
|
+
def []: (Symbol) -> Nodes::Node[TValue]?
|
|
37
38
|
|
|
38
|
-
def children: -> Array[Nodes::Node]
|
|
39
|
+
def children: -> Array[Nodes::Node[TValue]]
|
|
39
40
|
|
|
40
|
-
def children_tree: -> Hash[Symbol, Nodes::Node]
|
|
41
|
+
def children_tree: -> Hash[Symbol, Nodes::Node[TValue]]
|
|
41
42
|
|
|
42
43
|
def compressed?: -> bool
|
|
43
44
|
|
|
@@ -45,23 +46,23 @@ module Rambling
|
|
|
45
46
|
|
|
46
47
|
def key?: (Symbol) -> bool
|
|
47
48
|
|
|
48
|
-
def size: ->
|
|
49
|
+
def size: -> Integer
|
|
49
50
|
|
|
50
51
|
alias include? word?
|
|
51
52
|
alias match? partial_word?
|
|
52
|
-
alias words
|
|
53
|
+
alias words scan
|
|
53
54
|
alias << add
|
|
54
55
|
alias has_key? key?
|
|
55
56
|
alias has_letter? key?
|
|
56
57
|
|
|
57
58
|
private
|
|
58
59
|
|
|
59
|
-
attr_reader compressor: Compressor
|
|
60
|
-
attr_writer root: Nodes::Node
|
|
60
|
+
attr_reader compressor: Compressor[TValue]
|
|
61
|
+
attr_writer root: Nodes::Node[TValue]
|
|
61
62
|
|
|
62
|
-
def words_within_root: (String) ?{ (String) -> void } -> Enumerator[String, void]
|
|
63
|
+
def words_within_root: (String) ?{ (String) -> void } -> (Container[TValue] | Enumerator[String, void])
|
|
63
64
|
|
|
64
|
-
def compress_root: -> Nodes::Compressed
|
|
65
|
+
def compress_root: -> Nodes::Compressed[TValue]
|
|
65
66
|
|
|
66
67
|
def reversed_char_symbols: (String) -> Array[Symbol]
|
|
67
68
|
end
|