Решение на Пета задача - DataModel от Божидар Михайлов

Обратно към всички решения

Към профила на Божидар Михайлов

Резултати

  • 5 точки от тестове
  • 0 бонус точки
  • 5 точки общо
  • 21 успешни тест(а)
  • 4 неуспешни тест(а)

Код

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

Лог от изпълнението

.....FF.....FF...........

Failures:

  1) DataModel id generation does not reuse ids
     Failure/Error: expect(ivan.id).to eq 1
       
       expected: 1
            got: 0
       
       (compared using ==)
     # /tmp/d20161202-15620-13f2hnn/spec.rb:65:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:7:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:7:in `block (2 levels) in <top (required)>'

  2) DataModel id generation does not break when there are two models with the same store
     Failure/Error: expect(ivan.id).to eq 1
       
       expected: 1
            got: 0
       
       (compared using ==)
     # /tmp/d20161202-15620-13f2hnn/spec.rb:83:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:7:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:7:in `block (2 levels) in <top (required)>'

  3) DataModel.where raises an error if the query is by an unknown key
     Failure/Error: expect { user_model.where(middle_name: 'Ivanov') }.to raise_error(
       expected DataModel::UnknownAttributeError with "Unknown attribute middle_name", got #<DataModel::UnknownAttributeError: DataModel::UnknownAttributeError> with backtrace:
         # /tmp/d20161202-15620-13f2hnn/solution.rb:17:in `where'
         # /tmp/d20161202-15620-13f2hnn/spec.rb:143:in `block (4 levels) in <top (required)>'
         # /tmp/d20161202-15620-13f2hnn/spec.rb:143:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:7:in `block (3 levels) in <top (required)>'
         # ./lib/language/ruby/run_with_timeout.rb:7:in `block (2 levels) in <top (required)>'
     # /tmp/d20161202-15620-13f2hnn/spec.rb:143:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:7:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:7:in `block (2 levels) in <top (required)>'

  4) DataModel#delete deletes only the record for which it is called
     Failure/Error: ivan.delete
     ArgumentError:
       wrong number of arguments (given 0, expected 1)
     # /tmp/d20161202-15620-13f2hnn/spec.rb:156:in `delete'
     # /tmp/d20161202-15620-13f2hnn/spec.rb:156:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:7:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:7:in `block (2 levels) in <top (required)>'

Finished in 0.02574 seconds
25 examples, 4 failures

Failed examples:

rspec /tmp/d20161202-15620-13f2hnn/spec.rb:62 # DataModel id generation does not reuse ids
rspec /tmp/d20161202-15620-13f2hnn/spec.rb:74 # DataModel id generation does not break when there are two models with the same store
rspec /tmp/d20161202-15620-13f2hnn/spec.rb:142 # DataModel.where raises an error if the query is by an unknown key
rspec /tmp/d20161202-15620-13f2hnn/spec.rb:151 # DataModel#delete deletes only the record for which it is called

История (1 версия и 0 коментара)

Божидар обнови решението на 30.11.2016 23:20 (преди над 7 години)

+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