Skip to content

Commit

Permalink
Merge d1a3936 into 320ad12
Browse files Browse the repository at this point in the history
  • Loading branch information
gonzedge committed Feb 20, 2024
2 parents 320ad12 + d1a3936 commit ddfaa51
Show file tree
Hide file tree
Showing 37 changed files with 622 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ruby.yml
Expand Up @@ -23,6 +23,8 @@ jobs:
bundler-cache: true
- name: Run rubocop
run: bundle exec rubocop
- name: Run rbs + steep
run: bundle exec steep check
spec:
runs-on: ubuntu-latest
strategy:
Expand Down
5 changes: 5 additions & 0 deletions .rubocop.yml
Expand Up @@ -1327,6 +1327,8 @@ Metrics/AbcSize:
# The ABC size is a calculated magnitude, so this number can be an Integer or
# a Float.
Max: 20
AllowedMethods:
- 'closest_node'
Exclude:
- 'tasks/*.rb'

Expand All @@ -1353,6 +1355,9 @@ Metrics/ClassLength:
# Avoid complex methods.
Metrics/CyclomaticComplexity:
Max: 6
AllowedMethods:
- 'children_match_prefix'
- 'closest_node'

Metrics/MethodLength:
CountComments: false # count full line comments?
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Expand Up @@ -12,9 +12,11 @@ group :development do
gem 'memory_profiler'
gem 'pry'
gem 'rake'
gem 'rbs'
gem 'rspec'
gem 'ruby-prof'
gem 'stackprof'
gem 'steep'
gem 'yard'
end

Expand Down
35 changes: 35 additions & 0 deletions Steepfile
@@ -0,0 +1,35 @@
# frozen_string_literal: true

# D = Steep::Diagnostic
#
target :lib do
signature 'sig'

check 'lib'
# check 'tasks'

# check 'Gemfile'
# check 'Guardfile'
# check 'Rakefile'
# check 'Steepfile'

# library 'rubyzip'
library 'yaml'
library 'securerandom'

# configure_code_diagnostics(D::Ruby.default) # `default` diagnostics setting (applies by default)
# configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
# configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
# configure_code_diagnostics(D::Ruby.silent) # `silent` diagnostics setting
# configure_code_diagnostics do |hash| # You can setup everything yourself
# hash[D::Ruby::NoMethod] = :information
# end
end

# target :test do
# signature 'sig', 'sig-private'
#
# check 'test'
#
# # library 'pathname' # Standard libraries
# end
6 changes: 3 additions & 3 deletions lib/rambling/trie.rb
Expand Up @@ -26,7 +26,7 @@ def create filepath = nil, reader = nil
if filepath
reader ||= readers.resolve filepath
# noinspection RubyMismatchedArgumentType,RubyNilAnalysis
reader.each_word filepath do |word|
(reader || raise).each_word filepath do |word|
container << word
end
end
Expand All @@ -48,7 +48,7 @@ def create filepath = nil, reader = nil
# discouraged. Only use the +.marshal+ format with trusted input.
def load filepath, serializer = nil
serializer ||= serializers.resolve filepath
root = serializer.load filepath
root = (serializer || raise).load filepath
Rambling::Trie::Container.new root, compressor do |container|
yield container if block_given?
end
Expand All @@ -66,7 +66,7 @@ def load filepath, serializer = nil
def dump trie, filepath, serializer = nil
serializer ||= serializers.resolve filepath
# noinspection RubyNilAnalysis
serializer.dump trie.root, filepath
(serializer || raise).dump trie.root, filepath
end

# Provides configuration properties for the +Rambling::Trie+ gem.
Expand Down
4 changes: 3 additions & 1 deletion lib/rambling/trie/compressor.rb
Expand Up @@ -5,9 +5,11 @@ module Trie
# Responsible for the compression process of a trie data structure.
class Compressor
# Compresses a {Nodes::Node Node} from a trie data structure.
# @param [Nodes::Raw] node the node to compress.
# @param [Nodes::Node] node the node to compress.
# @return [Nodes::Compressed] node the compressed version of the node.
def compress node
return if node.nil?

if node.compressible?
compress_child_and_merge node
else
Expand Down
5 changes: 4 additions & 1 deletion lib/rambling/trie/configuration/provider_collection.rb
Expand Up @@ -104,7 +104,10 @@ def file_format filepath
end

def contains? provider
provider.nil? || (providers.any? && provider_instances.include?(provider))
return true if provider.nil?

p = (provider || raise)
providers.any? && provider_instances.include?(p)
end

alias_method :provider_instances, :values
Expand Down
10 changes: 6 additions & 4 deletions lib/rambling/trie/container.rb
Expand Up @@ -98,7 +98,7 @@ def scan word = ''

# Returns all words within a string that match a word contained in the trie.
# @param [String] phrase the string to look for matching words in.
# @return [Enumerator<String>] all the words in the given string that match a word in the trie.
# @return [Array<String>] all the words in the given string that match a word in the trie.
# @yield [String] each word found in phrase.
def words_within phrase
words_within_root(phrase).to_a
Expand Down Expand Up @@ -201,16 +201,18 @@ def words_within_root phrase
return enum_for :words_within_root, phrase unless block_given?

chars = phrase.chars
# rubocop:disable Style/CommentedKeyword
0.upto(chars.length - 1).each do |starting_index|
new_phrase = chars.slice starting_index..(chars.length - 1)
new_phrase = chars.slice starting_index..(chars.length - 1) # : Array[String]
root.match_prefix new_phrase do |word|
yield word
end
end
end # : Enumerator[String, void]
# rubocop:enable Style/CommentedKeyword
end

def compress_root
compressor.compress root
compressor.compress root # : Nodes::Compressed
end

def char_symbols word
Expand Down
2 changes: 2 additions & 0 deletions lib/rambling/trie/enumerable.rb
Expand Up @@ -6,6 +6,8 @@ module Trie
module Enumerable
include ::Enumerable

EMPTY_ENUMERATOR = [].to_enum :each

# Returns number of words contained in the trie
# @see https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-count Enumerable#count
alias_method :size, :count
Expand Down
22 changes: 11 additions & 11 deletions lib/rambling/trie/nodes/compressed.rb
Expand Up @@ -23,13 +23,13 @@ def compressed?
private

def partial_word_chars? chars
child = children_tree[chars.first.to_sym]
child = children_tree[(chars.first || raise).to_sym]
return false unless child

child_letter = child.letter.to_s

if chars.size >= child_letter.size
letter = chars.slice!(0, child_letter.size).join
letter = (chars.slice!(0, child_letter.size) || raise).join
return child.partial_word? chars if child_letter == letter
end

Expand All @@ -39,7 +39,7 @@ def partial_word_chars? chars
end

def word_chars? chars
letter = chars.slice! 0
letter = chars.slice!(0) || raise
letter_sym = letter.to_sym

child = children_tree[letter_sym]
Expand All @@ -50,21 +50,21 @@ def word_chars? chars

break if chars.empty?

letter << chars.slice!(0)
letter << (chars.slice!(0) || raise)
letter_sym = letter.to_sym
end

false
end

def closest_node chars
child = children_tree[chars.first.to_sym]
child = children_tree[(chars.first || raise).to_sym]
return missing unless child

child_letter = child.letter.to_s

if chars.size >= child_letter.size
letter = chars.slice!(0, child_letter.size).join
letter = (chars.slice!(0, child_letter.size) || raise).join
return child.scan chars if child_letter == letter
end

Expand All @@ -77,15 +77,15 @@ def closest_node chars
def children_match_prefix chars
return enum_for :children_match_prefix, chars unless block_given?

return if chars.empty?
return EMPTY_ENUMERATOR if chars.empty?

child = children_tree[chars.first.to_sym]
return unless child
child = children_tree[(chars.first || raise).to_sym]
return EMPTY_ENUMERATOR unless child

child_letter = child.letter.to_s
letter = chars.slice!(0, child_letter.size).join
letter = (chars.slice!(0, child_letter.size) || raise).join

return unless child_letter == letter
return EMPTY_ENUMERATOR unless child_letter == letter

child.match_prefix chars do |word|
yield word
Expand Down
6 changes: 4 additions & 2 deletions lib/rambling/trie/nodes/node.rb
Expand Up @@ -52,6 +52,8 @@ def first_child
# rubocop:disable Lint/UnreachableLoop
children_tree.each_value { |child| return child }
# rubocop:enable Lint/UnreachableLoop

nil
end

# Indicates if the current node is the root node.
Expand Down Expand Up @@ -106,7 +108,7 @@ def scan chars
end

# Returns all words that match a prefix of any length within chars.
# @param [String] chars the chars to base the prefix on.
# @param [Array[String]] chars the chars to base the prefix on.
# @return [Enumerator<String>] all the words that match a prefix by chars.
# @yield [String] each word found.
def match_prefix chars
Expand Down Expand Up @@ -147,7 +149,7 @@ def key? letter
# Delete a given letter and its corresponding {Node Node} from
# this {Node Node}'s children tree.
# @param [Symbol] letter the letter to delete from the node's children tree.
# @return [Node] the node corresponding to the deleted letter.
# @return [Node, nil] the node corresponding to the deleted letter.
# @see https://ruby-doc.org/core-2.7.0/Hash.html#method-i-delete Hash#delete
def delete letter
children_tree.delete letter
Expand Down
16 changes: 8 additions & 8 deletions lib/rambling/trie/nodes/raw.rb
Expand Up @@ -7,7 +7,7 @@ module Nodes
class Raw < Rambling::Trie::Nodes::Node
# Adds a word to the current raw (uncompressed) trie node.
# @param [Array<Symbol>] chars the char array to add to the trie.
# @return [Raw] the added/modified node based on the word added.
# @return [Node] the added/modified node based on the word added.
# @note This method clears the contents of the chars variable.
def add chars
if chars.empty?
Expand All @@ -27,7 +27,7 @@ def compressed?
private

def add_to_children_tree chars
letter = chars.pop
letter = chars.pop || raise
child = children_tree[letter] || new_node(letter)
child.add chars
child
Expand All @@ -40,23 +40,23 @@ def new_node letter
end

def partial_word_chars? chars = []
letter = chars.shift.to_sym
letter = (chars.shift || raise).to_sym
child = children_tree[letter]
return false unless child

child.partial_word? chars
end

def word_chars? chars = []
letter = chars.shift.to_sym
letter = (chars.shift || raise).to_sym
child = children_tree[letter]
return false unless child

child.word? chars
end

def closest_node chars
letter = chars.shift.to_sym
letter = (chars.shift || raise).to_sym
child = children_tree[letter]
return missing unless child

Expand All @@ -66,12 +66,12 @@ def closest_node chars
def children_match_prefix chars
return enum_for :children_match_prefix, chars unless block_given?

return if chars.empty?
return EMPTY_ENUMERATOR if chars.empty?

letter = chars.shift.to_sym
letter = (chars.shift || raise).to_sym
child = children_tree[letter]

return unless child
return EMPTY_ENUMERATOR unless child

child.match_prefix chars do |word|
yield word
Expand Down
7 changes: 6 additions & 1 deletion lib/rambling/trie/serializers/zip.rb
Expand Up @@ -26,12 +26,14 @@ def load filepath

::Zip::File.open filepath do |zip|
entry = zip.entries.first
return nil if entry.nil?
raise if entry.nil?

entry_path = path entry.name
entry.extract entry_path

serializer = serializers.resolve entry.name
raise if serializer.nil?

serializer.load entry_path
end
end
Expand All @@ -50,6 +52,9 @@ def dump contents, filepath

entry_path = path filename
serializer = serializers.resolve filename

raise if serializer.nil?

serializer.dump contents, entry_path

zip.add filename, entry_path
Expand Down
27 changes: 27 additions & 0 deletions sig/lib/rambling/trie.rbs
@@ -0,0 +1,27 @@
module Rambling
module Trie
VERSION: String

@properties: Configuration::Properties

def self.create: (?String?, ?Readers::Reader?) ?{ (Container) -> void } -> Container

def self.load: (String, ?Serializers::Serializer[Nodes::Node]?) ?{ (Container) -> void } -> Container

def self.dump: (Container, String, ?Serializers::Serializer[Nodes::Node]?) -> void

def self.config: ?{ (Configuration::Properties) -> void } -> Configuration::Properties

private

def self.properties: -> Configuration::Properties

def self.readers: -> Configuration::ProviderCollection[Readers::Reader]

def self.serializers: -> Configuration::ProviderCollection[Serializers::Serializer[Nodes::Node]]

def self.compressor: -> Compressor

def self.root_builder: -> ^() -> Nodes::Node
end
end
17 changes: 17 additions & 0 deletions sig/lib/rambling/trie/comparable.rbs
@@ -0,0 +1,17 @@
module Rambling
module Trie
module Comparable
def ==: (Nodes::Node) -> bool

private

# abstract methods

def letter: -> Symbol

def terminal?: -> bool

def children_tree: -> Hash[Symbol, Nodes::Node]
end
end
end

0 comments on commit ddfaa51

Please sign in to comment.