Божидар обнови решението на 30.11.2016 23:20 (преди около 8 години)
+module Meta
+ def attributes(*attrs)
+ return self.class_variable_get(:@@attributes) if attrs.empty?
+
+ attr_accessor *attrs
+ class_variable_set(:@@attributes, attrs)
+ define_finders
+ end
+
+ def data_store(ds = nil)
+ return self.class_variable_get(:@@data_store) unless ds
+ self.class_variable_set(:@@data_store, ds)
+ end
+
+ def where(**query)
+ unknown_attrs = query.keys - attributes - [:id]
+ raise DataModel::UnknownAttributeError unless unknown_attrs.empty?
+
+ data_store.find(query).map { |obj| self.new(obj) }
+ end
+
+ def define_finders
+ attributes.each do |attr|
+ define_singleton_method("find_by_#{attr}") do |val|
+ where(attr => val)
+ end
+ end
+ end
+end
+
+class DataModel
+ extend Meta
+
+ attr_reader :id
+
+ def initialize(**given_attrs)
+ self.class.attributes.each do |attr|
+ public_send("#{attr}=", given_attrs[attr])
+ end
+ @id = given_attrs[:id]
+ end
+
+ def to_hash
+ instance_variables.each_with_object({}) do |var, hash|
+ hash[var.to_s.delete('@').to_sym] = instance_variable_get(var)
+ end
+ end
+
+ def delete
+ the_store = self.class.data_store
+ raise DeleteUnsavedRecordError unless saved?
+ the_store.delete_by_id(@id)
+ end
+
+ def ==(other)
+ return false unless self.instance_of? other.class
+ saved? && other.saved? && id == other.id || equal?(other)
+ end
+
+ def save
+ obj = to_hash
+ the_store = self.class.data_store
+
+ if saved?
+ the_store.update(@id, obj)
+ else
+ @id = the_store.gen_id
+ the_store.create(obj.merge(id: @id))
+ end
+ end
+
+ def saved?
+ id && !self.class.where(id: id).empty?
+ end
+
+ class DeleteUnsavedRecordError < StandardError; end
+ class UnknownAttributeError < StandardError; end
+end
+
+module DataStore
+ def find(query)
+ entries.select do |obj|
+ obj.merge(query) == obj
+ end
+ end
+
+ def delete(query)
+ find(query).each do |obj|
+ delete_by_id(obj[:id])
+ end
+ end
+
+ def gen_id
+ genned_id = max_id
+ self.max_id += 1
+ genned_id
+ end
+end
+
+class HashStore
+ include DataStore
+ attr_accessor :the_store, :max_id
+
+ def initialize
+ @max_id = 0
+ @the_store = {}
+ end
+
+ def create(obj)
+ @the_store[obj[:id]] = obj
+ end
+
+ def update(id, obj)
+ raise KeyError unless @the_store.include?(id)
+ @the_store[id] = obj
+ end
+
+ def entries
+ the_store.values
+ end
+
+ def delete_by_id(id)
+ @the_store.delete(id)
+ end
+end
+
+class ArrayStore
+ include DataStore
+ attr_accessor :the_store, :max_id
+
+ def initialize
+ @max_id = 0
+ @the_store = []
+ end
+
+ def create(obj)
+ @the_store.push(obj)
+ end
+
+ def update(id, obj)
+ pos = @the_store.find_index { |obj| obj[:id] == id }
+ raise KeyError unless pos != nil
+ @the_store[pos] = obj
+ end
+
+ def entries
+ @the_store
+ end
+
+ def delete_by_id(id)
+ @the_store.delete_if { |obj| obj[:id] == id }
+ end
+end