Removes a lot of duplicated code.

This commit is contained in:
Philippe PITTOLI 2024-05-22 05:05:18 +02:00
parent 2b24fbc8a0
commit 7a342f8391
2 changed files with 72 additions and 177 deletions

View File

@ -96,28 +96,45 @@ class DODB::Partition(V) < DODB::Indexer(V)
# WARNING: throws an exception if no value is found.
# NOTE: for a safe version, use `#get?`.
def get(partition : String) : Array(V)
partition_directory = indexing_directory partition
raise MissingEntry.new(@name, partition) unless Dir.exists? partition_directory
r_value = Array(V).new
Dir.each_child partition_directory do |child|
r_value << @storage[get_key child]
get_with_keys(partition).map &.[0]
end
r_value
end
# Safe version of `#get`, gets data and returns a *nil* value in case of
# Safe version of `#get`, gets data and returns *an empty array* in case of
# a missing entry instead of an exception.
#
# ```
# red_cars = cars_by_color.get? "red" # No red cars = nil.
# red_cars = cars_by_color.get? "red"
# ```
def get?(partition : String) : Array(V)?
get partition
rescue MissingEntry
nil
Array(V).new
end
# Gets partition entries (and their keys) from the file-system representation.
#
# ```
# # Gets all red cars.
# cars_by_color.get "red"
# # Returns something like:
# # [ (@storage[42], 42)
# # , (@storage[91], 91)
# # ]
# # Each tuple is composed of a car and its key in the database.
# ```
# WARNING: throws a MissingEntry exception on non-existing partition.
def get_with_keys(partition : String) : Array(Tuple(V, Int32))
partition_directory = indexing_directory partition
raise MissingEntry.new(@name, partition) unless Dir.exists? partition_directory
r_value = Array(Tuple(V, Int32)).new
Dir.each_child partition_directory do |child|
key = get_key child
r_value << { @storage[key], key }
end
r_value
end
# Deletes all entries within the provided partition.
@ -125,6 +142,7 @@ class DODB::Partition(V) < DODB::Indexer(V)
# ```
# cars_by_color.delete "red" # Deletes all red cars.
# ```
# WARNING: throws a MissingEntry exception on non-existing partition.
def delete(partition : String)
delete partition, do true end
end
@ -137,17 +155,10 @@ class DODB::Partition(V) < DODB::Indexer(V)
# car.name == "Corvet"
# end
# ```
# TODO: in case the partition is left empty, should the partition's directory be removed?
def delete(partition, &matcher : Proc(V, Bool))
partition_directory = indexing_directory partition
return unless Dir.exists? partition_directory
Dir.each_child partition_directory do |child|
key = get_key child
item = @storage[key]
if yield item
# WARNING: throws a MissingEntry exception on non-existing partition.
def delete(partition : String, &matcher : Proc(V, Bool))
get_with_keys(partition).each do |entry, key|
if yield entry
@storage.delete key
end
end
@ -213,12 +224,6 @@ class DODB::CachedPartition(V) < DODB::Partition(V)
# WARNING: used for internal operations, do not change its content or access it directly.
property data = Hash(String, Array(Int32)).new
# Clears the cache and removes the `#indexing_directory`.
def nuke_index
super
data.clear
end
# Indexes the value on the file-system as `DODB::Partition#index` but also puts the index in a cache.
#
# NOTE: used for internal operations.
@ -253,6 +258,9 @@ class DODB::CachedPartition(V) < DODB::Partition(V)
end
# Gets partition entries and the database key for each entry.
# In `DODB::CachedPartition`, `#get_with_keys(partition : String)` is modified to retrieve data keys from
# the index cache.
# In case the data isn't already in the cache, it is retrieved from the file-system.
#
# ```
# # For example, get all red cars.
@ -263,6 +271,7 @@ class DODB::CachedPartition(V) < DODB::Partition(V)
# # ]
# # Each tuple is composed of a car and its key in the database.
# ```
# WARNING: throws a MissingEntry exception on non-existing partition.
def get_with_keys(partition : String) : Array(Tuple(V, Int32))
r_value = Array(Tuple(V, Int32)).new
@ -272,73 +281,17 @@ class DODB::CachedPartition(V) < DODB::Partition(V)
r_value << { @storage[data_key], data_key }
end
else
# Get the key from the database representation on the file-system.
partition_directory = indexing_directory partition
raise MissingEntry.new(@name, partition) unless Dir.exists? partition_directory
Dir.each_child partition_directory do |child|
r_value << { @storage[get_key child], get_key child }
end
# Gets data from the database representation on the file-system.
r_value = super(partition)
@data[partition] = r_value.map &.[1]
end
r_value
end
# Gets partition entries from the cache or the file-system representation.
#
# ```
# # For example, get all red cars.
# cars_by_color.get "red"
# ```
# NOTE: returns an empty list on empty or non-existing partition.
def get(partition : String) : Array(V)
r_value = Array(V).new
# In case the partition is cached.
if keys = @data[partition]?
keys.each do |data_key|
r_value << @storage[data_key]
end
else
# The keys to put in the partition.
p_value = Array(Int32).new
# Get the key from the database representation on the file-system.
partition_directory = indexing_directory partition
raise MissingEntry.new(@name, partition) unless Dir.exists? partition_directory
Dir.each_child partition_directory do |child|
key = get_key child
r_value << @storage[key]
p_value << key
end
@data[partition] = p_value
end
r_value
end
# Deletes entries within the provided partition and matching the provided block of code,
# both from the file-system representation and from the cache.
#
# ```
# # Deletes all red Corvets.
# cars_by_color.delete "red", do |car|
# car.name == "Corvet"
# end
# ```
# TODO: in case the partition is left empty, should the partition be removed from the cache?
def delete(partition : String, &matcher : Proc(V, Bool))
# Use `get_with_keys` to retrieve data on-disk, if necessary.
new_partition = get_with_keys(partition).map(&.[1]).select do |key|
item = @storage[key]
! yield item
end
@data[partition] = new_partition
super(partition, &matcher)
# Clears the cache and removes the `#indexing_directory`.
def nuke_index
super
data.clear
end
end
@ -393,52 +346,19 @@ class DODB::RAMOnlyPartition(V) < DODB::CachedPartition(V)
# # ]
# # Each tuple is composed of a car and its key in the database.
# ```
# WARNING: FOR CONSISTENCY, throws a MissingEntry exception on non-existing partition.
def get_with_keys(partition : String) : Array(Tuple(V, Int32))
r_value = Array(Tuple(V, Int32)).new
if keys = @data[partition]?
keys.each do |data_key|
r_value << { @storage[data_key], data_key }
end
else
raise MissingEntry.new(@name, partition)
end
r_value
end
# Gets partition entries from the in-memory partition cache.
#
# ```
# # Get all red cars.
# cars_by_color.get "red"
# ```
# NOTE: returns an empty list on empty or non-existing partition.
def get(partition : String) : Array(V)
r_value = Array(V).new
if keys = @data[partition]?
keys.each do |data_key|
r_value << @storage[data_key]
end
end
r_value
end
# Deletes entries within the provided partition and matching the provided block of code.
#
# ```
# # Deletes all red Corvets.
# cars_by_color.delete "red", do |car|
# car.name == "Corvet"
# end
# ```
# TODO: in case the partition is left empty, should it be removed from the cache?
def delete(partition : String, &matcher : Proc(V, Bool))
if keys = @data[partition]?
new_partition = keys.select do |key|
item = @storage[key]
! yield item
end
@data[partition] = new_partition
end
end
# Clears the cache.
def nuke_index
data.clear

View File

@ -64,14 +64,12 @@ class DODB::Tags(V) < DODB::Indexer(V)
end
def index(key : String, value : V)
indices = key_proc.call(value)
return if indices.is_a? NoIndex
tags = key_proc.call(value)
return if tags.is_a? NoIndex
indices.each do |i|
symlink = get_tagged_entry_path(i, key)
tags.each do |tag|
symlink = get_tagged_entry_path(tag, key)
Dir.mkdir_p ::File.dirname symlink
# FIXME: Should not happen anymore. Should we remove this?
::File.delete symlink if ::File.exists? symlink
::File.symlink get_data_symlink(key), symlink
end
end
@ -79,11 +77,11 @@ class DODB::Tags(V) < DODB::Indexer(V)
# :inherit:
# TODO: in case the tag is left empty, should the tag directory be removed?
def deindex(key : String, value : V)
indices = key_proc.call(value)
return if indices.is_a? NoIndex
tags = key_proc.call(value)
return if tags.is_a? NoIndex
indices.each do |i|
symlink = get_tagged_entry_path(i, key)
tags.each do |tag|
symlink = get_tagged_entry_path(tag, key)
begin
::File.delete symlink
@ -95,7 +93,7 @@ class DODB::Tags(V) < DODB::Indexer(V)
# Gets tag entries (and their keys) from the file-system representation of the tag.
#
# ```
# # Get all slow cars.
# # Gets all slow cars.
# cars_by_keywords.get "slow"
# # Returns something like:
# # [ (@storage[42], 42)
@ -273,11 +271,11 @@ class DODB::CachedTags(V) < DODB::Tags(V)
property data = Hash(String, Array(Int32)).new
def index(key : String, value : V)
indices = key_proc.call value
return if indices.is_a? NoIndex
tags = key_proc.call value
return if tags.is_a? NoIndex
super(key, value)
indices.each do |tag|
tags.each do |tag|
array = if v = @data[tag]?
v
else
@ -292,11 +290,11 @@ class DODB::CachedTags(V) < DODB::Tags(V)
# :inherit:
# TODO: in case the tag is left empty, should it be removed from the cache?
def deindex(key : String, value : V)
indices = key_proc.call value
return if indices.is_a? NoIndex
tags = key_proc.call value
return if tags.is_a? NoIndex
super(key, value)
indices.each do |tag|
tags.each do |tag|
if v = @data[tag]?
v.delete key.to_i
@data[tag] = v
@ -320,22 +318,16 @@ class DODB::CachedTags(V) < DODB::Tags(V)
def get_with_keys(tag : String) : Array(Tuple(V, Int32))
r_value = Array(Tuple(V, Int32)).new
# In case the tag is cached.
if keys = @data[tag]?
keys.each do |data_key|
r_value << { @storage[data_key], data_key }
end
else
# Get the key from the database representation on the file-system.
tag_directory = indexing_directory tag
raise MissingEntry.new(@name, tag) unless Dir.exists? tag_directory
Dir.each_child tag_directory do |child|
r_value << { @storage[get_key child], get_key child }
end
# Gets data from the database representation on the file-system.
r_value = super(tag)
@data[tag] = r_value.map &.[1]
end
r_value
end
@ -363,10 +355,10 @@ end
# NOTE: for an fs representation but still fast for retrieval, see `CachedTags`.
class DODB::RAMOnlyTags(V) < DODB::CachedTags(V)
def index(key : String, value : V)
indices = key_proc.call value
return if indices.is_a? NoIndex
tags = key_proc.call value
return if tags.is_a? NoIndex
indices.each do |tag|
tags.each do |tag|
array = if v = @data[tag]?
v
else
@ -379,10 +371,10 @@ class DODB::RAMOnlyTags(V) < DODB::CachedTags(V)
end
def deindex(key : String, value : V)
indices = key_proc.call value
return if indices.is_a? NoIndex
tags = key_proc.call value
return if tags.is_a? NoIndex
indices.each do |tag|
tags.each do |tag|
if v = @data[tag]?
v.delete key.to_i
@data[tag] = v
@ -414,23 +406,6 @@ class DODB::RAMOnlyTags(V) < DODB::CachedTags(V)
r_value
end
# Gets tag entries from the in-memory tag cache.
#
# ```
# # Get all slow cars.
# cars_by_keywords.get "slow"
# ```
# NOTE: returns an empty list on empty or non-existing tag.
def get(tag : String) : Array(V)
r_value = Array(V).new
if keys = @data[tag]?
keys.each do |data_key|
r_value << @storage[data_key]
end
end
r_value
end
# Clears the cache.
def nuke_index
data.clear