module Dddr module Sdbm module ClassMethods def queries(&block) @queries_module ||= Module.new @queries_module.module_eval(&block) end def const_missing(name) if name == :Repository create_repository_for(self) else super end end private def create_repository_for(entity_class) repository_class = Class.new(RepositoryBase) do define_singleton_method(:new) do |*args| super(entity_class) end end repository_class.include(@queries_module) if instance_variable_defined?(:@queries_module) const_set(:Repository, repository_class) end end class RepositoryBase def initialize(entity_class) @mutex = Mutex.new @entity_class = entity_class setup_data_directory(entity_class) @serializer = Dddr::Serializer.new(Dddr.configuration.serializer) end def setup_data_directory(entity_class) env = Dddr.configuration.env data_dir = Dddr.configuration.data_dir container = Dddr.configuration.container raise Dddr::Error, "Container name is required" unless container # Construct the target directory path @data_dir = "#{data_dir}/#{container}/#{env}/#{entity_class.name.downcase}" begin # Ensure all directories in the path are created FileUtils.mkdir_p(@data_dir) # Change ownership only if the path starts with "/var/" if @data_dir.start_with?("/var/") FileUtils.chown_R(ENV["USER"], nil, @data_dir) end rescue Errno::ENOENT => e # Log or handle the error raise "Failed to create or access the directory: #{e.message}" end end def count all.count end def to_csv env = Dddr.configuration.env data_dir = Dddr.configuration.data_dir container = Dddr.configuration.container CSV.open("#{data_dir}/#{container}/#{env}/#{self.class.name}.csv", "w") do |csv| csv = all.first&.to_hash&.keys all.each do |entity| csv << entity.to_hash.values end end end def add(entity) @mutex.synchronize do uid = SecureRandom.uuid entity.uid = uid entity.created_at ||= DateTime.now.to_s entity.last_updated_at ||= entity.created_at SDBM.open(@data_dir) do |db| db[uid] = @serializer.dump(entity.to_hash) end uid end end alias_method :create, :add alias_method :insert, :add alias_method :put, :add alias_method :append, :add alias_method :store, :add def update(entity) return unless entity.uid @mutex.synchronize do entity.last_updated_at = DateTime.now.to_s SDBM.open(@data_dir) do |db| db[entity.uid] = @serializer.dump(entity.to_hash) end entity end end alias_method :modify, :update alias_method :change, :update alias_method :edit, :update alias_method :revise, :update alias_method :alter, :update def delete(entity) return unless entity.uid @mutex.synchronize do SDBM.open(@data_dir) do |db| db.delete(entity.uid) end end end alias_method :remove, :delete alias_method :erase, :delete alias_method :discard, :delete alias_method :destroy, :delete alias_method :wipe, :delete def get(uid) @mutex.synchronize do SDBM.open(@data_dir) do |db| data = db[uid] return unless data entity = @entity_class.new entity.from_hash(uid, @serializer.load(data)) entity end end end alias_method :find, :get def all @mutex.synchronize do result = [] SDBM.open(@data_dir) do |db| db.each do |key, value| entity = @entity_class.new entity.from_hash(key, @serializer.load(value)) result << entity unless entity.deleted end end result end end end def to_hash result = {} instance_variables.each do |var_name| attribute_name = var_name.to_s[1..].to_sym result[attribute_name] = instance_variable_get(var_name) end result end def from_hash(uid, data_hash) self.uid = uid data_hash&.each do |attribute, value| attribute = attribute.to_sym if respond_to?(:"#{attribute}=") send(:"#{attribute}=", value) end end end end end