# frozen_string_literal: true require "sdbm" require "securerandom" require "date" require "ostruct" require_relative "dddr/version" module Dddr def self.configuration @configuration ||= OpenStruct.new end def self.configure configuration.env = ENV["DDDR_ENV"] || "development" configuration.data_dir = "/var/dddr" configuration.container = nil yield(configuration) if block_given? end class Error < StandardError; end module Entity def self.included(base) base.extend(ClassMethods) end attr_accessor :uid, :created_at, :last_updated_at, :deleted, :deleted_at 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) @entity_class = entity_class setup_data_directory(entity_class) 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 unless Dir.exist?(data_dir) if data_dir.include?("/var/") `sudo mkdir -p #{data_dir}/#{container}/#{env}/` `sudo chown $USER #{data_dir}/#{container}/#{env}/` else `mkdir -p #{data_dir}/#{container}/#{env}/` end end @name = "#{data_dir}/#{container}/#{env}/#{entity_class.name.downcase}" end def count all.count end def add(entity) uid = SecureRandom.uuid entity.uid = uid entity.created_at ||= DateTime.now.to_s entity.last_updated_at ||= entity.created_at SDBM.open(@name) do |db| db[uid] = Marshal.dump(entity.to_hash) end uid 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 entity.last_updated_at = DateTime.now.to_s SDBM.open(@name) do |db| db[entity.uid] = Marshal.dump(entity.to_hash) end entity 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 SDBM.open(@name) do |db| db.delete(entity.uid) 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) SDBM.open(@name) do |db| data = db[uid] return unless data entity = @entity_class.new entity.from_hash(uid, Marshal.load(data)) entity end end alias_method :find, :get def all result = [] SDBM.open(@name) do |db| db.each do |key, value| entity = @entity_class.new entity.from_hash(key, Marshal.load(value)) result << entity unless entity.deleted end end result 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 new? created_at.nil? 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 Dddr.configure