rambling-trie 2.6.0 → 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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +2 -1
  3. data/README.md +2 -2
  4. data/Steepfile +5 -1
  5. data/lib/rambling/trie/comparable.rb +4 -2
  6. data/lib/rambling/trie/compressible.rb +1 -1
  7. data/lib/rambling/trie/compressor.rb +2 -2
  8. data/lib/rambling/trie/configuration/properties.rb +3 -1
  9. data/lib/rambling/trie/configuration/provider_collection.rb +7 -7
  10. data/lib/rambling/trie/container.rb +37 -16
  11. data/lib/rambling/trie/enumerable.rb +8 -4
  12. data/lib/rambling/trie/nodes/compressed.rb +26 -18
  13. data/lib/rambling/trie/nodes/missing.rb +17 -0
  14. data/lib/rambling/trie/nodes/node.rb +25 -22
  15. data/lib/rambling/trie/nodes/raw.rb +3 -3
  16. data/lib/rambling/trie/not_implemented.rb +23 -0
  17. data/lib/rambling/trie/readers/plain_text.rb +4 -1
  18. data/lib/rambling/trie/readers/reader.rb +5 -2
  19. data/lib/rambling/trie/serializers/marshal.rb +5 -0
  20. data/lib/rambling/trie/serializers/serializer.rb +8 -4
  21. data/lib/rambling/trie/serializers/yaml.rb +5 -0
  22. data/lib/rambling/trie/serializers/zip.rb +31 -21
  23. data/lib/rambling/trie/stringifyable.rb +2 -5
  24. data/lib/rambling/trie/version.rb +1 -1
  25. data/lib/rambling/trie.rb +11 -7
  26. data/sig/lib/rambling/trie/comparable.rbs +4 -2
  27. data/sig/lib/rambling/trie/compressible.rbs +1 -1
  28. data/sig/lib/rambling/trie/compressor.rbs +1 -1
  29. data/sig/lib/rambling/trie/configuration/properties.rbs +1 -1
  30. data/sig/lib/rambling/trie/container.rbs +9 -8
  31. data/sig/lib/rambling/trie/enumerable.rbs +5 -4
  32. data/sig/lib/rambling/trie/inspectable.rbs +3 -3
  33. data/sig/lib/rambling/trie/nodes/compressed.rbs +4 -2
  34. data/sig/lib/rambling/trie/nodes/missing.rbs +9 -1
  35. data/sig/lib/rambling/trie/nodes/node.rbs +3 -2
  36. data/sig/lib/rambling/trie/nodes/raw.rbs +1 -1
  37. data/sig/lib/rambling/trie/not_implemented.rbs +10 -0
  38. data/sig/lib/rambling/trie/readers/plain_text.rbs +1 -1
  39. data/sig/lib/rambling/trie/readers/reader.rbs +2 -0
  40. data/sig/lib/rambling/trie/serializers/marshal.rbs +1 -1
  41. data/sig/lib/rambling/trie/serializers/serializer.rbs +2 -0
  42. data/sig/lib/rambling/trie/serializers/yaml.rbs +1 -1
  43. data/sig/lib/rambling/trie/serializers/zip.rbs +4 -2
  44. data/sig/lib/rambling/trie/stringifyable.rbs +2 -2
  45. data/sig/lib/rambling/trie.rbs +10 -8
  46. metadata +4 -4
  47. data/sig/lib/zip/entry.rbs +0 -11
  48. data/sig/lib/zip/file.rbs +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52fe8e610cf15d433a18cbb24cdd4b72c48a4ab50398868ab68cce58ea0a8de6
4
- data.tar.gz: a106639989c7ad33230c252d470a9c7924fd65204ab633743bd813d4f69fa4d4
3
+ metadata.gz: 3a9d76d13a38a54c80ca90236346dcfe280fea62d46d8e9318efc8f2653b6f40
4
+ data.tar.gz: 0ab262b5f456d0e299fc16614bb0c7103406899b0a88dce3ef69d0d1f403fd3c
5
5
  SHA512:
6
- metadata.gz: 6d4659cf7e712e4fbed4e6054d6f27f7db0ecbf4dca13fcad6cab34c3050b78cb63cee7993ccda2146868b8d7203932bea08667bb510d0afdcada8cb403c3418
7
- data.tar.gz: a36621b0ee84dbb8905ca8af25b8fc1b2b08a23ce171fb83c3ec632f39ffc1e7108828330eb93c4285948e5c32a1a6c6d116ad74d9da5a28aa9bd428110dafe5
6
+ metadata.gz: 13ccb7f63311ae91544442fe75e1620ecb91ef87501150fa46f454f80b7b4d51cda69ebeb8c82e474173db815e5f536b9cf03cccb3fa8e6233facfbd7f265949
7
+ data.tar.gz: dfab4eaea2613f6ad5957579089056441d3f01533112fff081504da2be2e18f77267350f6cedd4c61d65d88075cf0e83d22e5124370fac45fa2dadc36b22ddcb
data/Dockerfile CHANGED
@@ -1,4 +1,5 @@
1
- FROM ruby:3.3.6
1
+ ARG RUBY_VERSION=3.3.6
2
+ FROM ruby:${RUBY_VERSION}
2
3
 
3
4
  RUN bundle config --global frozen 1
4
5
 
data/README.md CHANGED
@@ -299,7 +299,7 @@ The Rambling Trie has been tested with the following Ruby versions:
299
299
 
300
300
  ## Compatible RBS and Steep versions
301
301
 
302
- Type signatures for `Rambling::Trie` are included in the [`sig` directory](./sig)! The current version (`2.6.0`) was
302
+ Type signatures for `Rambling::Trie` are included in the [`sig` directory](./sig)! The current version (`2.7.0`) was
303
303
  checked with RBS `3.10.4` and Steep `1.10.0`.
304
304
 
305
305
  ## Contributing to Rambling Trie
@@ -327,7 +327,7 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER I
327
327
  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
328
328
 
329
329
  [asdf]: https://asdf-vm.com/
330
- [badge_fury_badge]: https://badge.fury.io/rb/rambling-trie.svg?version=2.6.0
330
+ [badge_fury_badge]: https://badge.fury.io/rb/rambling-trie.svg?version=2.7.0
331
331
  [badge_fury_link]: https://badge.fury.io/rb/rambling-trie
332
332
  [chruby]: https://github.com/postmodern/chruby
333
333
  [code_climate_grade_badge]: https://codeclimate.com/github/gonzedge/rambling-trie/badges/gpa.svg
data/Steepfile CHANGED
@@ -13,9 +13,13 @@ target :lib do
13
13
  # check 'Rakefile'
14
14
  # check 'Steepfile'
15
15
 
16
- # library 'rubyzip'
16
+ collection_config 'rbs_collection.yaml'
17
+
18
+ library 'rubyzip'
19
+ library 'rake'
17
20
  library 'yaml'
18
21
  library 'securerandom'
22
+ library 'tmpdir'
19
23
 
20
24
  # configure_code_diagnostics(D::Ruby.default) # `default` diagnostics setting (applies by default)
21
25
  # configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
@@ -6,11 +6,13 @@ module Rambling
6
6
  module Comparable
7
7
  # Compares two nodes.
8
8
  # @param [Nodes::Node] other the node to compare against.
9
- # @return [Boolean] `true` if the nodes' {Nodes::Node#letter #letter} and
10
- # {Nodes::Node#children_tree #children_tree} are equal, `false` otherwise.
9
+ # @return [Boolean] `true` if the nodes' {Nodes::Node#letter #letter},
10
+ # {Nodes::Node#terminal? #terminal?}, {Nodes::Node#value #value}, and
11
+ # {Nodes::Node#children_tree #children_tree} are all equal, `false` otherwise.
11
12
  def == other
12
13
  letter == other.letter &&
13
14
  terminal? == other.terminal? &&
15
+ value == other.value &&
14
16
  children_tree == other.children_tree
15
17
  end
16
18
  end
@@ -7,7 +7,7 @@ module Rambling
7
7
  # Indicates if the current {Rambling::Trie::Nodes::Node Node} can be compressed or not.
8
8
  # @return [Boolean] `true` for non-{Nodes::Node#terminal? terminal} nodes with one child, `false` otherwise.
9
9
  def compressible?
10
- !(root? || terminal?) && 1 == children_tree.size
10
+ !root? && !terminal? && children_tree.one?
11
11
  end
12
12
  end
13
13
  end
@@ -37,7 +37,7 @@ module Rambling
37
37
  if other.terminal?
38
38
  compressed.terminal!
39
39
  value = other.value
40
- compressed.value = value if value
40
+ compressed.value = value unless value.nil?
41
41
  end
42
42
  compressed
43
43
  end
@@ -48,7 +48,7 @@ module Rambling
48
48
  if node.terminal?
49
49
  compressed.terminal!
50
50
  value = node.value
51
- compressed.value = value if value
51
+ compressed.value = value unless value.nil?
52
52
  end
53
53
  compressed
54
54
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tmpdir'
4
+
3
5
  module Rambling
4
6
  module Trie
5
7
  module Configuration
@@ -39,7 +41,7 @@ module Rambling
39
41
 
40
42
  @compressor = Rambling::Trie::Compressor.new
41
43
  @root_builder = -> { Rambling::Trie::Nodes::Raw.new }
42
- @tmp_path = '/tmp'
44
+ @tmp_path = Dir.tmpdir
43
45
  end
44
46
 
45
47
  private
@@ -17,7 +17,6 @@ module Rambling
17
17
  # providers.
18
18
  # @param [TProvider] provider the provider to use as default.
19
19
  # @raise [ArgumentError] when the given provider is not in the provider collection.
20
- # @note If no providers have been configured, `nil` will be assigned.
21
20
  # @return [TProvider, nil] the default provider to use when a provider cannot be resolved in
22
21
  # {ProviderCollection#resolve #resolve}.
23
22
  attr_reader :default
@@ -25,7 +24,8 @@ module Rambling
25
24
  # Creates a new provider collection.
26
25
  # @param [Symbol] name the name for this provider collection.
27
26
  # @param [Hash<Symbol, TProvider>] providers the configured providers.
28
- # @param [TProvider, nil] default the configured default provider.
27
+ # @param [TProvider, nil] default the configured default provider. When +nil+ (or no providers
28
+ # are given), falls back to the first configured provider, or +nil+ if none exist.
29
29
  def initialize name, providers = {}, default = nil
30
30
  @name = name
31
31
  @configured_providers = providers
@@ -74,9 +74,9 @@ module Rambling
74
74
  self.default = configured_default
75
75
  end
76
76
 
77
- # Get provider corresponding to a given format.
78
- # @return [Array<Symbol>] the provider corresponding to that format.
79
- # @see https://ruby-doc.org/3.3.0/Hash.html#method-i-5B-5D
77
+ # List the formats of configured providers.
78
+ # @return [Array<Symbol>] the formats of all configured providers.
79
+ # @see https://ruby-doc.org/3.3.0/Hash.html#method-i-keys
80
80
  # Hash#keys
81
81
  def formats
82
82
  providers.keys
@@ -84,7 +84,7 @@ module Rambling
84
84
 
85
85
  # Get provider corresponding to a given format.
86
86
  # @param [Symbol] format the format to search for in the collection.
87
- # @return [TProvider] the provider corresponding to that format.
87
+ # @return [TProvider, nil] the provider corresponding to that format, or +nil+ if not found.
88
88
  # @see https://ruby-doc.org/3.3.0/Hash.html#method-i-5B-5D Hash#[]
89
89
  def [] format
90
90
  providers[format]
@@ -109,7 +109,7 @@ module Rambling
109
109
  def contains? provider
110
110
  return true if provider.nil?
111
111
 
112
- providers.any? && provider_instances.include?(provider || raise)
112
+ provider_instances.include?(provider || raise)
113
113
  end
114
114
 
115
115
  alias_method :provider_instances, :values
@@ -22,7 +22,8 @@ module Rambling
22
22
  end
23
23
 
24
24
  # Adds a word to the trie.
25
- # @param [String] word the word to add the branch from.
25
+ # @param [String] word the word to add to the trie.
26
+ # @param [Object, nil] value the value to associate with the word.
26
27
  # @return [Nodes::Node] the just added branch's root node.
27
28
  # @raise [InvalidOperation] if the trie is already compressed.
28
29
  # @see Nodes::Raw#add
@@ -32,13 +33,22 @@ module Rambling
32
33
  end
33
34
 
34
35
  # Adds all provided words to the trie.
35
- # @param [Array<String>] words the words to add the branch from.
36
+ # @param [Array<String>] words the words to add to the trie.
37
+ # @param [Array<Object>, nil] values the values to associate with each word, in the same order.
36
38
  # @return [Array<Nodes::Node>] the collection of nodes added.
37
39
  # @raise [InvalidOperation] if the trie is already compressed.
40
+ # @raise [ArgumentError] if words and values are given but differ in size.
38
41
  # @see Nodes::Raw#add
39
42
  # @see Nodes::Compressed#add
40
43
  def concat words, values = nil
41
44
  if values
45
+ words_size = words.size
46
+ values_size = values.size
47
+ unless words_size == values_size
48
+ raise ArgumentError,
49
+ "words and values must have the same size (words: #{words_size}, values: #{values_size})"
50
+ end
51
+
42
52
  words.each_with_index.map { |word, index| add(word, values[index]) }
43
53
  else
44
54
  words.map { |word| add word }
@@ -56,10 +66,18 @@ module Rambling
56
66
  end
57
67
 
58
68
  # Compresses the existing trie using redundant node elimination. Returns a new trie with the compressed root.
59
- # @return [Container] A new {Container} with the {Nodes::Compressed Compressed} root node
60
- # or self if the trie has already been compressed.
69
+ # @return [Container] A new {Container} with the {Nodes::Compressed Compressed} root node.
70
+ # @deprecated Calling {#compress} on an already-compressed trie is deprecated and will raise
71
+ # {InvalidOperation} in the next major version. Use {#compressed?} to guard if needed.
61
72
  def compress
62
- return self if root.compressed?
73
+ if root.compressed?
74
+ warn <<~WARN.chomp.tr("\n", ' ')
75
+ [DEPRECATED] Calling `compress` on an already-compressed trie is deprecated
76
+ and will raise `InvalidOperation` in the next major version.
77
+ Called from #{caller_locations(1, 1)&.first}
78
+ WARN
79
+ return Rambling::Trie::Container.new root, compressor
80
+ end
63
81
 
64
82
  Rambling::Trie::Container.new compress_root, compressor
65
83
  end
@@ -73,7 +91,7 @@ module Rambling
73
91
  end
74
92
 
75
93
  # Adds all provided words to the trie.
76
- # @param [Array<String>] words the words to add the branch from.
94
+ # @param [String] words the words to add to the trie.
77
95
  # @return [Array<Nodes::Node>] the collection of nodes added.
78
96
  # @raise [InvalidOperation] if the trie is already compressed.
79
97
  # @see #concat
@@ -103,7 +121,6 @@ module Rambling
103
121
  # Returns all words within a string that match a word contained in the trie.
104
122
  # @param [String] phrase the string to look for matching words in.
105
123
  # @return [Array<String>] all the words in the given string that match a word in the trie.
106
- # @yield [String] each word found in phrase.
107
124
  def words_within phrase
108
125
  words_within_root(phrase).to_a
109
126
  end
@@ -130,6 +147,8 @@ module Rambling
130
147
  return enum_for :each unless block_given?
131
148
 
132
149
  root.each { |word| yield word }
150
+
151
+ self
133
152
  end
134
153
 
135
154
  # @return [String] a string representation of the container.
@@ -181,8 +200,8 @@ module Rambling
181
200
  root.key? letter
182
201
  end
183
202
 
184
- # Size of the Root {Nodes::Node Node}'s children tree.
185
- # @return [Integer] the number of letters in the root node.
203
+ # Number of words contained in the trie.
204
+ # @return [Integer] the number of words stored in the trie.
186
205
  def size
187
206
  root.size
188
207
  end
@@ -203,13 +222,12 @@ module Rambling
203
222
  return enum_for :words_within_root, phrase unless block_given?
204
223
 
205
224
  chars = phrase.chars
206
- size = chars.length
207
- # rubocop:disable Style/CommentedKeyword
208
- 0.upto(size - 1).each do |starting_index|
209
- new_phrase = chars.slice starting_index, size # : Array[String]
225
+ chars.each_index do |starting_index|
226
+ new_phrase = chars[starting_index..] || raise
210
227
  root.match_prefix(new_phrase) { |word| yield word }
211
- end # : Enumerator[String, void]
212
- # rubocop:enable Style/CommentedKeyword
228
+ end
229
+
230
+ self
213
231
  end
214
232
 
215
233
  def compress_root
@@ -217,7 +235,10 @@ module Rambling
217
235
  end
218
236
 
219
237
  def reversed_char_symbols word
220
- word.reverse.chars.map(&:to_sym).to_a
238
+ # @type var chars: Array[String]
239
+ chars = word.chars
240
+ chars.reverse!
241
+ chars.map(&:to_sym)
221
242
  end
222
243
  end
223
244
  end
@@ -6,10 +6,6 @@ module Rambling
6
6
  module Enumerable
7
7
  include ::Enumerable
8
8
 
9
- # Empty enumerator constant for early each exits.
10
- EMPTY_ENUMERATOR = [] # : Array[String]
11
- .to_enum :each
12
-
13
9
  # Returns number of words contained in the trie
14
10
  # @see https://ruby-doc.org/3.3.0/Enumerable.html#method-i-count Enumerable#count
15
11
  alias_method :size, :count
@@ -26,6 +22,14 @@ module Rambling
26
22
 
27
23
  self
28
24
  end
25
+
26
+ # Returns a new empty enumerator for early-exit returns.
27
+ # A method rather than a constant to prevent shared mutable state.
28
+ def empty_enum
29
+ # @type var empty_array: Array[String]
30
+ empty_array = []
31
+ empty_array.each
32
+ end
29
33
  end
30
34
  end
31
35
  end
@@ -19,10 +19,10 @@ module Rambling
19
19
 
20
20
  # Always raises {Rambling::Trie::InvalidOperation InvalidOperation} when
21
21
  # trying to add a word to the current compressed trie node
22
- # @param [String] _ the word to add to the trie.
23
- # @raise [InvalidOperation] if the trie is already compressed.
24
- # @return [void]
25
- def add _, _ = nil
22
+ # @param [Array<Symbol>] _word the word chars to add to the trie.
23
+ # @param [Object, nil] _value the value to associate with the word.
24
+ # @raise [InvalidOperation] always.
25
+ def add _word, _value = nil
26
26
  raise Rambling::Trie::InvalidOperation, 'Cannot add word to compressed trie'
27
27
  end
28
28
 
@@ -42,12 +42,12 @@ module Rambling
42
42
 
43
43
  if chars.size >= child_letter.size
44
44
  letter = (chars.shift(child_letter.size) || raise).join
45
- return child.partial_word? chars if child_letter == letter
46
- end
45
+ return false unless child_letter == letter
47
46
 
48
- letter = chars.join
49
- child_letter = child_letter.slice 0, letter.size
50
- child_letter == letter
47
+ child.partial_word? chars
48
+ else
49
+ child_letter.start_with? chars.join
50
+ end
51
51
  end
52
52
 
53
53
  def word_chars? chars
@@ -75,29 +75,37 @@ module Rambling
75
75
 
76
76
  child_letter = child.letter.to_s
77
77
 
78
- if chars.size >= child_letter.size
79
- letter = (chars.shift(child_letter.size) || raise).join
80
- return child.scan chars if child_letter == letter
78
+ if chars.size < child_letter.size
79
+ return child_letter.start_with?(chars.join) ? child : missing
81
80
  end
82
81
 
83
- letter = chars.join
84
- child_letter = child_letter.slice 0, letter.size
82
+ letter = (chars.shift(child_letter.size) || raise).join
83
+ return missing unless child_letter == letter
85
84
 
86
- child_letter == letter ? child : missing
85
+ child.scan chars
87
86
  end
88
87
 
89
88
  def children_match_prefix chars
90
89
  return enum_for :children_match_prefix, chars unless block_given?
91
90
 
92
- return EMPTY_ENUMERATOR if chars.empty?
91
+ return empty_enum if chars.empty?
93
92
 
94
93
  child = children_tree[(chars.first || raise).to_sym]
95
- return EMPTY_ENUMERATOR unless child
94
+ return empty_enum unless child
96
95
 
96
+ match_child_prefix(child, chars) { |word| yield word }
97
+ end
98
+
99
+ def match_child_prefix child, chars
97
100
  child_letter = child.letter.to_s
101
+
102
+ # stop early if we already know that the remaining characters in the
103
+ # given phrase do not even cover the current node's compressed key
104
+ return empty_enum if chars.size < child_letter.size
105
+
98
106
  letter = (chars.shift(child_letter.size) || raise).join
99
107
 
100
- return EMPTY_ENUMERATOR unless child_letter == letter
108
+ return empty_enum unless child_letter == letter
101
109
 
102
110
  child.match_prefix(chars) { |word| yield word }
103
111
  end
@@ -5,6 +5,23 @@ module Rambling
5
5
  module Nodes
6
6
  # A representation of a missing node in the trie data structure. Returned when a node is not found.
7
7
  class Missing < Rambling::Trie::Nodes::Node
8
+ def partial_word? _
9
+ false
10
+ end
11
+
12
+ private
13
+
14
+ def word_chars? _
15
+ false
16
+ end
17
+
18
+ def closest_node _
19
+ self
20
+ end
21
+
22
+ def children_match_prefix chars
23
+ enum_for :children_match_prefix, chars unless block_given?
24
+ end
8
25
  end
9
26
  end
10
27
  end
@@ -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
@@ -32,17 +33,19 @@ module Rambling
32
33
  attr_accessor :parent
33
34
 
34
35
  # Arbitrary value stored in this node
35
- # @return [TValue, nil] the parent of the current node.
36
+ # @return [TValue, nil] the value stored in this node.
36
37
  attr_accessor :value
37
38
 
38
39
  # Creates a new node.
39
40
  # @param [Symbol, nil] letter the Node's letter value.
40
41
  # @param [Node, nil] parent the parent of the current node.
41
- # @param [Hash<Symbol, Node>] children_tree the children tree of the current node.
42
- def initialize letter = nil, parent = nil, children_tree = {}
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
43
46
  @letter = letter
44
47
  @parent = parent
45
- @children_tree = children_tree
48
+ @children_tree = children_tree || {}
46
49
  end
47
50
 
48
51
  # Child nodes.
@@ -54,13 +57,7 @@ module Rambling
54
57
  # First child node.
55
58
  # @return [Node, nil] the first child contained in the current node.
56
59
  def first_child
57
- return if children_tree.empty?
58
-
59
- # rubocop:disable Lint/UnreachableLoop
60
- children_tree.each_value { |child| return child }
61
- # rubocop:enable Lint/UnreachableLoop
62
-
63
- nil
60
+ children_tree.each_value.first
64
61
  end
65
62
 
66
63
  # Indicates if the current node is the root node.
@@ -137,7 +134,7 @@ module Rambling
137
134
  end
138
135
 
139
136
  # Set the {Node Node} that corresponds to a given letter.
140
- # @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.
141
138
  # @param [Node] node the {Node Node} to assign to that letter.
142
139
  # @return [Node] the node corresponding to the inserted or updated letter.
143
140
  # @see https://ruby-doc.org/3.3.0/Hash.html#method-i-5B-5D Hash#[]
@@ -174,22 +171,28 @@ module Rambling
174
171
 
175
172
  attr_accessor :terminal
176
173
 
177
- # abstract methods
178
-
179
- def children_match_prefix chars
180
- raise NotImplementedError
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
181
178
  end
182
179
 
183
- def partial_word_chars? chars
184
- raise NotImplementedError
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
185
184
  end
186
185
 
187
- def word_chars? chars
188
- raise NotImplementedError
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
189
190
  end
190
191
 
191
- def closest_node chars
192
- raise NotImplementedError
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
193
196
  end
194
197
  end
195
198
  end
@@ -9,7 +9,7 @@ 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 clears the contents of the chars variable.
12
+ # @note This method consumes the array by popping each element during recursion, leaving it empty on return.
13
13
  def add reversed_chars, value = nil
14
14
  if reversed_chars.empty?
15
15
  unless root?
@@ -70,10 +70,10 @@ module Rambling
70
70
  def children_match_prefix chars
71
71
  return enum_for :children_match_prefix, chars unless block_given?
72
72
 
73
- return EMPTY_ENUMERATOR if chars.empty?
73
+ return empty_enum if chars.empty?
74
74
 
75
75
  child = children_tree[(chars.shift || raise).to_sym]
76
- return EMPTY_ENUMERATOR unless child
76
+ return empty_enum unless child
77
77
 
78
78
  child.match_prefix(chars) { |word| yield word }
79
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) { |line| yield line.chomp! }
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
- def each_word filepath
14
- raise NotImplementedError
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
- def load filepath
13
- raise NotImplementedError
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
- def dump contents, filepath
22
- raise NotImplementedError
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 [TContents] all contents of the unzipped loaded file.
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
- ::Zip::File.open filepath do |zip|
28
- entry = zip.entries.first
29
- raise unless entry
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
- entry_name = entry.name
32
- entry_path = path entry_name
33
- entry.extract ::File.basename(entry_path), destination_directory: tmp_path
32
+ entry_name = entry.name
33
+ entry_path = path_with_random_prefix entry_name
34
+ tmp_paths << entry_path
34
35
 
35
- serializer = serializers.resolve entry_name
36
- raise unless serializer
36
+ entry.extract ::File.basename(entry_path), destination_directory: tmp_path
37
37
 
38
- serializer.load entry_path
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 [TContents] number of bytes written to disk.
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
- ::Zip::File.open filepath, create: true do |zip|
52
- filename = ::File.basename filepath, '.zip'
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
- entry_path = path filename
55
- serializer = serializers.resolve filename
56
+ entry_path = path_with_random_prefix filename
57
+ tmp_paths << entry_path
56
58
 
57
- raise unless serializer
58
-
59
- serializer.dump contents, entry_path
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 path filename
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 is not terminal or is root.
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
@@ -3,6 +3,6 @@
3
3
  module Rambling
4
4
  module Trie
5
5
  # Current version of the rambling-trie.
6
- VERSION = '2.6.0'
6
+ VERSION = '2.7.0'
7
7
  end
8
8
  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,11 +24,9 @@ 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 ||= readers.resolve filepath
27
- # noinspection RubyMismatchedArgumentType,RubyNilAnalysis
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
32
  yield container if block_given? # steep:ignore
@@ -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 ||= Rambling::Trie::Configuration::Properties.new
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
@@ -1,13 +1,15 @@
1
1
  module Rambling
2
2
  module Trie
3
- module Comparable[TValue < _Inspect]
3
+ module Comparable[TValue < BasicObject & _Inspect & _Nilable]
4
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
 
@@ -1,6 +1,6 @@
1
1
  module Rambling
2
2
  module Trie
3
- module Compressible[TValue < _Inspect]
3
+ module Compressible[TValue < BasicObject & _Inspect & _Nilable]
4
4
  def compressible?: -> bool
5
5
 
6
6
  private
@@ -1,6 +1,6 @@
1
1
  module Rambling
2
2
  module Trie
3
- class Compressor[TValue < _Inspect]
3
+ class Compressor[TValue < BasicObject & _Inspect & _Nilable]
4
4
  def compress: (Nodes::Node[TValue]?) -> Nodes::Compressed[TValue]?
5
5
 
6
6
  private
@@ -1,7 +1,7 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  module Configuration
4
- class Properties[TValue < _Inspect]
4
+ class Properties[TValue < BasicObject & _Inspect & _Nilable]
5
5
  attr_reader readers: ProviderCollection[Readers::Reader]
6
6
  attr_reader serializers: ProviderCollection[Serializers::Serializer[Nodes::Node[TValue]]]
7
7
  attr_accessor compressor: Compressor[TValue]
@@ -1,6 +1,6 @@
1
1
  module Rambling
2
2
  module Trie
3
- class Container[TValue < _Inspect]
3
+ class Container[TValue < BasicObject & _Inspect & _Nilable]
4
4
  include ::Enumerable[String]
5
5
 
6
6
  @compressor: Compressor[TValue]
@@ -17,15 +17,16 @@ module Rambling
17
17
 
18
18
  def compress: -> Container[TValue]
19
19
 
20
- def each: { (String) -> void } -> (Enumerator[String, void] | Enumerable[TValue])
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
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
 
@@ -45,11 +46,11 @@ module Rambling
45
46
 
46
47
  def key?: (Symbol) -> bool
47
48
 
48
- def size: -> Numeric
49
+ def size: -> Integer
49
50
 
50
51
  alias include? word?
51
52
  alias match? partial_word?
52
- alias words? scan
53
+ alias words scan
53
54
  alias << add
54
55
  alias has_key? key?
55
56
  alias has_letter? key?
@@ -59,7 +60,7 @@ module Rambling
59
60
  attr_reader compressor: Compressor[TValue]
60
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
65
  def compress_root: -> Nodes::Compressed[TValue]
65
66
 
@@ -1,13 +1,12 @@
1
1
  module Rambling
2
2
  module Trie
3
- module Enumerable[TValue < _Inspect]
3
+ module Enumerable[TValue < BasicObject & _Inspect & _Nilable]
4
4
  include ::Enumerable[String]
5
5
 
6
- EMPTY_ENUMERATOR: Enumerator[String, void]
7
-
8
6
  alias size count
9
7
 
10
- def each: { (String) -> void } -> (Enumerator[String, void] | Enumerable[TValue])
8
+ def each: { (String) -> void } -> Enumerable[TValue]
9
+ | -> Enumerator[String, void]
11
10
 
12
11
  private
13
12
 
@@ -15,6 +14,8 @@ module Rambling
15
14
 
16
15
  def as_word: -> String
17
16
 
17
+ def empty_enum: -> Enumerator[String, void]
18
+
18
19
  def terminal?: -> bool
19
20
 
20
21
  def children_tree: -> Hash[Symbol, Nodes::Node[TValue]]
@@ -1,6 +1,6 @@
1
1
  module Rambling
2
2
  module Trie
3
- module Inspectable[TValue < _Inspect]
3
+ module Inspectable[TValue < BasicObject & _Inspect & _Nilable]
4
4
 
5
5
  def inspect: -> String
6
6
 
@@ -20,9 +20,9 @@ module Rambling
20
20
 
21
21
  # abstract methods
22
22
 
23
- def letter: -> Symbol
23
+ def letter: -> Symbol?
24
24
 
25
- def value: -> TValue
25
+ def value: -> TValue?
26
26
 
27
27
  def terminal: -> bool?
28
28
 
@@ -1,10 +1,10 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  module Nodes
4
- class Compressed[TValue < _Inspect] < Node[TValue]
4
+ class Compressed[TValue < BasicObject & _Inspect & _Nilable] < Node[TValue]
5
5
  def initialize: (?Symbol?, ?Node[TValue]?, ?Hash[Symbol, Node[TValue]]) -> void
6
6
 
7
- def add: (Array[Symbol], ?TValue) -> Node[TValue]
7
+ def add: (Array[Symbol], ?TValue?) -> Node[TValue]
8
8
 
9
9
  def compressed?: -> bool
10
10
 
@@ -14,6 +14,8 @@ module Rambling
14
14
 
15
15
  def children_match_prefix: (Array[String]) { (String) -> void } -> Enumerator[String, void]
16
16
 
17
+ def match_child_prefix: (Node[TValue], Array[String]) { (String) -> void } -> Enumerator[String, void]
18
+
17
19
  def partial_word_chars?: (Array[String]) -> bool
18
20
 
19
21
  def word_chars?: (Array[String]) -> bool
@@ -1,8 +1,16 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  module Nodes
4
- class Missing[TValue < _Inspect] < Node[TValue]
4
+ class Missing[TValue < BasicObject & _Inspect & _Nilable] < Node[TValue]
5
5
  def initialize: -> void
6
+ def partial_word?: (Array[String] chars) -> bool
7
+
8
+ private
9
+
10
+ def word_chars?: (Array[String] chars) -> bool
11
+ def closest_node: (Array[String] chars) -> Node[TValue]
12
+ def children_match_prefix: (Array[String] chars) -> void
13
+ | (Array[String] chars) { (String word) -> void } -> void
6
14
  end
7
15
  end
8
16
  end
@@ -1,7 +1,8 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  module Nodes
4
- class Node[TValue < _Inspect]
4
+ class Node[TValue < BasicObject & _Inspect & _Nilable]
5
+ include NotImplemented
5
6
  include Compressible[TValue]
6
7
  include Enumerable[TValue]
7
8
  include Comparable[TValue]
@@ -13,7 +14,7 @@ module Rambling
13
14
  attr_accessor parent: Node[TValue]?
14
15
  attr_accessor value: TValue?
15
16
 
16
- def initialize: (?Symbol?, ?Node[TValue]?, ?Hash[Symbol, Node[TValue]]) -> void
17
+ def initialize: (?Symbol?, ?Node[TValue]?, ?Hash[Symbol, Node[TValue]]?) -> void
17
18
 
18
19
  def add: (Array[Symbol], ?TValue?) -> Node[TValue]
19
20
 
@@ -1,7 +1,7 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  module Nodes
4
- class Raw[TValue < _Inspect] < Node[TValue]
4
+ class Raw[TValue < BasicObject & _Inspect & _Nilable] < Node[TValue]
5
5
  def add: (Array[Symbol], ?TValue?) -> Node[TValue]
6
6
 
7
7
  def compressed?: -> bool
@@ -0,0 +1,10 @@
1
+ module Rambling
2
+ module Trie
3
+ # @api private
4
+ module NotImplemented : ::Object
5
+ private
6
+
7
+ def not_implemented: () -> bot
8
+ end
9
+ end
10
+ end
@@ -2,7 +2,7 @@ module Rambling
2
2
  module Trie
3
3
  module Readers
4
4
  class PlainText < Reader
5
- def each_word: (String) { (String?) -> void } -> (Enumerator[String?, void] | PlainText)
5
+ def each_word: (String) { (String) -> void } -> (Enumerator[String, void] | PlainText)
6
6
  end
7
7
  end
8
8
  end
@@ -2,6 +2,8 @@ module Rambling
2
2
  module Trie
3
3
  module Readers
4
4
  class Reader
5
+ include NotImplemented
6
+
5
7
  def each_word: (String) { (String) -> void } -> (Enumerator[String, void] | Reader)
6
8
  end
7
9
  end
@@ -1,7 +1,7 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  module Serializers
4
- class Marshal[TValue < _Inspect] < Serializer[Nodes::Node[TValue]]
4
+ class Marshal[TValue < BasicObject & _Inspect & _Nilable] < Serializer[Nodes::Node[TValue]]
5
5
  @serializer: Serializer[String]
6
6
 
7
7
  private
@@ -2,6 +2,8 @@ module Rambling
2
2
  module Trie
3
3
  module Serializers
4
4
  class Serializer[TContents]
5
+ include NotImplemented
6
+
5
7
  def load: (String) -> TContents
6
8
  def dump: (TContents, String) -> Numeric
7
9
  end
@@ -1,7 +1,7 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  module Serializers
4
- class Yaml[TValue < _Inspect] < Serializer[Nodes::Node[TValue]]
4
+ class Yaml[TValue < BasicObject & _Inspect & _Nilable] < Serializer[Nodes::Node[TValue]]
5
5
  @serializer: Serializer[String]
6
6
 
7
7
  private
@@ -1,7 +1,7 @@
1
1
  module Rambling
2
2
  module Trie
3
3
  module Serializers
4
- class Zip[TValue < _Inspect] < Serializer[Nodes::Node[TValue]]
4
+ class Zip[TValue < BasicObject & _Inspect & _Nilable] < Serializer[Nodes::Node[TValue]]
5
5
  @properties: Configuration::Properties[TValue]
6
6
 
7
7
  def initialize: (Configuration::Properties[TValue]) -> void
@@ -14,7 +14,9 @@ module Rambling
14
14
 
15
15
  def tmp_path: -> String
16
16
 
17
- def path: (String) -> String
17
+ def path_with_random_prefix: (String) -> String
18
+
19
+ def clean_up_tmp_files: [T] { (Array[String]) -> T } -> T
18
20
  end
19
21
  end
20
22
  end
@@ -1,6 +1,6 @@
1
1
  module Rambling
2
2
  module Trie
3
- module Stringifyable[TValue < _Inspect]
3
+ module Stringifyable[TValue < BasicObject & _Inspect & _Nilable]
4
4
  def as_word: -> String
5
5
 
6
6
  def to_s: -> String
@@ -9,7 +9,7 @@ module Rambling
9
9
 
10
10
  # abstract methods
11
11
 
12
- def letter: -> Symbol
12
+ def letter: -> Symbol?
13
13
 
14
14
  def terminal?: -> bool
15
15
 
@@ -2,24 +2,26 @@ module Rambling
2
2
  module Trie
3
3
  VERSION: String
4
4
 
5
- def self.create: [TValue < _Inspect] (?String?, ?Readers::Reader?) ?{ (Container[TValue]) -> void } -> Container[TValue]
5
+ def self.create: [TValue < BasicObject & _Inspect & _Nilable] (?String?, ?Readers::Reader?) ?{ (Container[TValue]) -> void } -> Container[TValue]
6
6
 
7
- def self.load: [TValue < _Inspect] (String, ?Serializers::Serializer[Nodes::Node[TValue]]?) ?{ (Container[TValue]) -> void } -> Container[TValue]
7
+ def self.load: [TValue < BasicObject & _Inspect & _Nilable] (String, ?Serializers::Serializer[Nodes::Node[TValue]]?) ?{ (Container[TValue]) -> void } -> Container[TValue]
8
8
 
9
- def self.dump: [TValue < _Inspect] (Container[TValue], String, ?Serializers::Serializer[Nodes::Node[TValue]]?) -> void
9
+ def self.dump: [TValue < BasicObject & _Inspect & _Nilable] (Container[TValue], String, ?Serializers::Serializer[Nodes::Node[TValue]]?) -> void
10
10
 
11
- def self.config: [TValue < _Inspect] ?{ (Configuration::Properties[TValue]) -> void } -> Configuration::Properties[TValue]
11
+ def self.config: [TValue < BasicObject & _Inspect & _Nilable] ?{ (Configuration::Properties[TValue]) -> void } -> Configuration::Properties[TValue]
12
12
 
13
13
  private
14
14
 
15
- def self.properties: [TValue < _Inspect] -> Configuration::Properties[TValue]
15
+ PROPERTIES_MUTEX: Mutex
16
+
17
+ def self.properties: [TValue < BasicObject & _Inspect & _Nilable] -> Configuration::Properties[TValue]
16
18
 
17
19
  def self.readers: -> Configuration::ProviderCollection[Readers::Reader]
18
20
 
19
- def self.serializers: [TValue < _Inspect] -> Configuration::ProviderCollection[Serializers::Serializer[Nodes::Node[TValue]]]
21
+ def self.serializers: [TValue < BasicObject & _Inspect & _Nilable] -> Configuration::ProviderCollection[Serializers::Serializer[Nodes::Node[TValue]]]
20
22
 
21
- def self.compressor: [TValue < _Inspect] -> Compressor[TValue]
23
+ def self.compressor: [TValue < BasicObject & _Inspect & _Nilable] -> Compressor[TValue]
22
24
 
23
- def self.root_builder: [TValue < _Inspect] -> ^() -> Nodes::Node[TValue]
25
+ def self.root_builder: [TValue < BasicObject & _Inspect & _Nilable] -> ^() -> Nodes::Node[TValue]
24
26
  end
25
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rambling-trie
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.0
4
+ version: 2.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edgar Gonzalez
@@ -43,6 +43,7 @@ files:
43
43
  - lib/rambling/trie/nodes/missing.rb
44
44
  - lib/rambling/trie/nodes/node.rb
45
45
  - lib/rambling/trie/nodes/raw.rb
46
+ - lib/rambling/trie/not_implemented.rb
46
47
  - lib/rambling/trie/readers.rb
47
48
  - lib/rambling/trie/readers/plain_text.rb
48
49
  - lib/rambling/trie/readers/reader.rb
@@ -70,6 +71,7 @@ files:
70
71
  - sig/lib/rambling/trie/nodes/missing.rbs
71
72
  - sig/lib/rambling/trie/nodes/node.rbs
72
73
  - sig/lib/rambling/trie/nodes/raw.rbs
74
+ - sig/lib/rambling/trie/not_implemented.rbs
73
75
  - sig/lib/rambling/trie/readers/plain_text.rbs
74
76
  - sig/lib/rambling/trie/readers/reader.rbs
75
77
  - sig/lib/rambling/trie/serializers/file.rbs
@@ -78,8 +80,6 @@ files:
78
80
  - sig/lib/rambling/trie/serializers/yaml.rbs
79
81
  - sig/lib/rambling/trie/serializers/zip.rbs
80
82
  - sig/lib/rambling/trie/stringifyable.rbs
81
- - sig/lib/zip/entry.rbs
82
- - sig/lib/zip/file.rbs
83
83
  homepage: https://github.com/gonzedge/rambling-trie
84
84
  licenses:
85
85
  - MIT
@@ -104,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  - !ruby/object:Gem::Version
105
105
  version: '0'
106
106
  requirements: []
107
- rubygems_version: 4.0.3
107
+ rubygems_version: 4.0.6
108
108
  specification_version: 4
109
109
  summary: A Ruby implementation of the trie data structure.
110
110
  test_files: []
@@ -1,11 +0,0 @@
1
- module Zip
2
- class Entry
3
- include Enumerable[Entry]
4
-
5
- def name: -> String
6
-
7
- def extract: (String, ?destination_directory: String?) -> void
8
-
9
- def each: ?{ (Entry) -> void } -> (Enumerator[Entry, void])
10
- end
11
- end
data/sig/lib/zip/file.rbs DELETED
@@ -1,9 +0,0 @@
1
- module Zip
2
- class File
3
- def self.open: [TContent] (String, ?create: bool?) { (File) -> TContent } -> TContent
4
-
5
- def entries: -> Array[Entry]
6
-
7
- def add: (String, String) -> void
8
- end
9
- end