Решение на Пета задача - DataModel от Мартин Христов

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

Към профила на Мартин Христов

Резултати

  • 2 точки от тестове
  • 0 бонус точки
  • 2 точки общо
  • 8 успешни тест(а)
  • 17 неуспешни тест(а)

Код

module DataModelUtilities
def save
if @data_store.include? @settings
update_store
else
create_instance_id
@data_store.create(@settings)
end
self
end
def delete
if @data_store.include? @settings
@data_store.delete(@settings)
else
raise DataModel::DeleteUnsavedRecordError, 'intance is not saved'
end
end
def ==(entity)
if self.class == entity.class
same_attributes = (self.settings.to_a - entity.settings.to_a).empty?
if same_attributes && (@data_store.include? entity.settings)
self.id == entity.id
else
self.object_id == entity.object_id
end
end
end
private
def update_store
updates = {}
@attributes.each_key do |key|
updates[key] = self.send(key)
@settings[key] = updates[key]
end
@data_store.update(@settings[:id], updates)
end
def create_instance_id
@id = @last_id
@last_id += 1
@settings[:id] = @id
end
end
module DataModelErrors
class DeleteUnsavedRecordError < ::StandardError
end
class UnknownAttributeError < ::StandardError
end
end
module DataModelMethods
def attributes(*attribute_store)
if attribute_store.empty?
@attributes.keys
else
@attributes = {}
save_attributes(attribute_store)
end
end
def data_store(store = nil)
@data_store = store if store
end
def where(entities = {})
result = []
@data_store.each do |setting|
if (entities.keys - @attributes.keys).empty?
if (entities.to_a - setting.to_a).empty?
result << self.new(setting) && result
end
else
raise DataModel::UnknownAttributeError, "Unknown attribute
#{(entities.keys - @attributes.keys)[0]}."
end
end
end
def take_attributes
@attributes
end
private
def save_attributes(attribute_store)
attribute_store.each_index do |index|
@attributes[attribute_store[index]] = true
method_name = ("find_by_" + attribute_store[index].to_s).to_sym
define_singleton_method(method_name) do |attribute|
where({ attribute_store[index] => attribute })
end
end
end
end
class DataModel
include DataModelUtilities
include DataModelErrors
@data_store = nil
@last_id = 1
attr_accessor :id, :settings
def initialize(settings = {})
@settings = settings
@attributes = self.class.take_attributes
@attributes.each_key do |key|
self.class.send(:attr_accessor, key)
instance_variable_set("@#{key}", settings[key])
end
self
end
extend DataModelMethods
end
class BaseStore
include Enumerable
attr_accessor :store, :id
def find(hash)
result = []
each do |setting|
result << setting if (hash.to_a - setting.to_a).empty?
end
result
end
end
class ArrayStore < BaseStore
def initialize
@store = []
end
def each
@store.each do |member|
yield(member)
end
end
def include?(hash)
@store.include? hash
end
def create(hash)
@store.push hash
end
def update(id, updates)
model = @store.select { |model| model[:id] == id }.first
updates.each_key do |key|
model[key] = updates[key]
end
end
end
class HashStore < BaseStore
def initialize
@store = {}
end
def each
@store.each do |_, value|
yield(value)
end
end
def include?(hash)
@store[:id] == hash
end
def create(hash)
@store[hash[:id]] = hash
end
def update(id, updates)
model = @store[id.to_sym]
updates.each_key do |key|
model[key] = updates[key]
end
end
end

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

.F.FFFFFFFFFFFF..FFF....F

Failures:

  1) DataModel has attributes and data_model getters
     Failure/Error: expect(user_model.data_store).to eq data_store
       
       expected: #<HashStore:0x007ff3cb1c3c68 @store={}>
            got: nil
       
       (compared using ==)
     # /tmp/d20161202-15620-1bji784/spec.rb:24:in `block (2 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 has #find_by_<attribute> methods
     Failure/Error: record.save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:41:in `block (2 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 id generation creates id on first save and does not change it
     Failure/Error: record.save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:52: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 id generation does not reuse ids
     Failure/Error: ivan.save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:64: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)>'

  5) DataModel id generation does not break when there are two models with the same store
     Failure/Error: ivan.save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:82: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)>'

  6) DataModel equality comparison compares by id if both records are saved
     Failure/Error: ivan.save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:94: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)>'

  7) DataModel equality comparison uses #equal? if there are no ids
     Failure/Error: expect(first_user).to_not eq second_user
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:23:in `=='
     # /tmp/d20161202-15620-1bji784/spec.rb:112: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)>'

  8) DataModel.where finds records by attributes
     Failure/Error: user_model.new(first_name: 'Ivan', last_name: 'Ivanov').save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:119: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)>'

  9) DataModel.where finds records by multiple attributes
     Failure/Error: user_model.new(first_name: 'Ivan', last_name: 'Ivanov').save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:119: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)>'

  10) DataModel.where returns empty collection when nothing is found
     Failure/Error: user_model.new(first_name: 'Ivan', last_name: 'Ivanov').save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:119: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)>'

  11) DataModel.where raises an error if the query is by an unknown key
     Failure/Error: user_model.new(first_name: 'Ivan', last_name: 'Ivanov').save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:119: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)>'

  12) DataModel#delete deletes only the record for which it is called
     Failure/Error: ivan = user_model.new(first_name: 'Ivan').save
     NoMethodError:
       undefined method `include?' for nil:NilClass
     # /tmp/d20161202-15620-1bji784/solution.rb:3:in `save'
     # /tmp/d20161202-15620-1bji784/spec.rb:152: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)>'

  13) DataModel#delete raises an error if the record is not saved
     Failure/Error: expect { user_model.new(first_name: 'Ivan').delete }.to raise_error(
       expected DataModelErrors::DeleteUnsavedRecordError, got #<NoMethodError: undefined method `include?' for nil:NilClass> with backtrace:
         # /tmp/d20161202-15620-1bji784/solution.rb:13:in `delete'
         # /tmp/d20161202-15620-1bji784/spec.rb:163:in `block (4 levels) in <top (required)>'
         # /tmp/d20161202-15620-1bji784/spec.rb:163: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-1bji784/spec.rb:163: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)>'

  14) HashStore behaves like a data store #update updates the attributes of a record with a given ID
     Failure/Error: store.update(2, {id: 2, name: 'Georgi'})
     NoMethodError:
       undefined method `to_sym' for 2:Fixnum
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-1bji784/spec.rb:235
     # /tmp/d20161202-15620-1bji784/solution.rb:177:in `update'
     # /tmp/d20161202-15620-1bji784/spec.rb:199: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)>'

  15) HashStore behaves like a data store #update only updates records with the correct IDs
     Failure/Error: store.update(2, {id: 2, name: 'Sasho'})
     NoMethodError:
       undefined method `to_sym' for 2:Fixnum
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-1bji784/spec.rb:235
     # /tmp/d20161202-15620-1bji784/solution.rb:177:in `update'
     # /tmp/d20161202-15620-1bji784/spec.rb:210: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)>'

  16) HashStore behaves like a data store #delete can delete multiple records with a single query
     Failure/Error: store.delete(name: 'Pesho')
     NoMethodError:
       undefined method `delete' for #<HashStore:0x007ff3cba64b88>
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-1bji784/spec.rb:235
     # /tmp/d20161202-15620-1bji784/spec.rb:227: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)>'

  17) ArrayStore behaves like a data store #delete can delete multiple records with a single query
     Failure/Error: store.delete(name: 'Pesho')
     NoMethodError:
       undefined method `delete' for #<ArrayStore:0x007ff3cb9cc518>
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-1bji784/spec.rb:239
     # /tmp/d20161202-15620-1bji784/spec.rb:227: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.02458 seconds
25 examples, 17 failures

Failed examples:

rspec /tmp/d20161202-15620-1bji784/spec.rb:21 # DataModel has attributes and data_model getters
rspec /tmp/d20161202-15620-1bji784/spec.rb:39 # DataModel has #find_by_<attribute> methods
rspec /tmp/d20161202-15620-1bji784/spec.rb:48 # DataModel id generation creates id on first save and does not change it
rspec /tmp/d20161202-15620-1bji784/spec.rb:62 # DataModel id generation does not reuse ids
rspec /tmp/d20161202-15620-1bji784/spec.rb:74 # DataModel id generation does not break when there are two models with the same store
rspec /tmp/d20161202-15620-1bji784/spec.rb:92 # DataModel equality comparison compares by id if both records are saved
rspec /tmp/d20161202-15620-1bji784/spec.rb:108 # DataModel equality comparison uses #equal? if there are no ids
rspec /tmp/d20161202-15620-1bji784/spec.rb:124 # DataModel.where finds records by attributes
rspec /tmp/d20161202-15620-1bji784/spec.rb:129 # DataModel.where finds records by multiple attributes
rspec /tmp/d20161202-15620-1bji784/spec.rb:138 # DataModel.where returns empty collection when nothing is found
rspec /tmp/d20161202-15620-1bji784/spec.rb:142 # DataModel.where raises an error if the query is by an unknown key
rspec /tmp/d20161202-15620-1bji784/spec.rb:151 # DataModel#delete deletes only the record for which it is called
rspec /tmp/d20161202-15620-1bji784/spec.rb:162 # DataModel#delete raises an error if the record is not saved
rspec /tmp/d20161202-15620-1bji784/spec.rb:196 # HashStore behaves like a data store #update updates the attributes of a record with a given ID
rspec /tmp/d20161202-15620-1bji784/spec.rb:204 # HashStore behaves like a data store #update only updates records with the correct IDs
rspec /tmp/d20161202-15620-1bji784/spec.rb:218 # HashStore behaves like a data store #delete can delete multiple records with a single query
rspec /tmp/d20161202-15620-1bji784/spec.rb:218 # ArrayStore behaves like a data store #delete can delete multiple records with a single query

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

Мартин обнови решението на 01.12.2016 23:04 (преди над 7 години)

+module DataModelUtilities
+ def save
+ if @data_store.include? @settings
+ update_store
+ else
+ create_instance_id
+ @data_store.create(@settings)
+ end
+ self
+ end
+
+ def delete
+ if @data_store.include? @settings
+ @data_store.delete(@settings)
+ else
+ raise DataModel::DeleteUnsavedRecordError, 'intance is not saved'
+ end
+ end
+
+ def ==(entity)
+ if self.class == entity.class
+ same_attributes = (self.settings.to_a - entity.settings.to_a).empty?
+ if same_attributes && (@data_store.include? entity.settings)
+ self.id == entity.id
+ else
+ self.object_id == entity.object_id
+ end
+ end
+ end
+
+ private
+ def update_store
+ updates = {}
+ @attributes.each_key do |key|
+ updates[key] = self.send(key)
+ @settings[key] = updates[key]
+ end
+ @data_store.update(@settings[:id], updates)
+ end
+
+ def create_instance_id
+ @id = @last_id
+ @last_id += 1
+ @settings[:id] = @id
+ end
+end
+
+module DataModelErrors
+ class DeleteUnsavedRecordError < ::StandardError
+ end
+ class UnknownAttributeError < ::StandardError
+ end
+end
+
+module DataModelMethods
+ def attributes(*attribute_store)
+ if attribute_store.empty?
+ @attributes.keys
+ else
+ @attributes = {}
+ save_attributes(attribute_store)
+ end
+ end
+
+ def data_store(store = nil)
+ @data_store = store if store
+ end
+
+ def where(entities = {})
+ result = []
+ @data_store.each do |setting|
+ if (entities.keys - @attributes.keys).empty?
+ if (entities.to_a - setting.to_a).empty?
+ result << self.new(setting) && result
+ end
+ else
+ raise DataModel::UnknownAttributeError, "Unknown attribute
+ #{(entities.keys - @attributes.keys)[0]}."
+ end
+ end
+ end
+
+ def take_attributes
+ @attributes
+ end
+
+ private
+ def save_attributes(attribute_store)
+ attribute_store.each_index do |index|
+ @attributes[attribute_store[index]] = true
+ method_name = ("find_by_" + attribute_store[index].to_s).to_sym
+ define_singleton_method(method_name) do |attribute|
+ where({ attribute_store[index] => attribute })
+ end
+ end
+ end
+end
+
+class DataModel
+ include DataModelUtilities
+ include DataModelErrors
+ @data_store = nil
+ @last_id = 1
+ attr_accessor :id, :settings
+ def initialize(settings = {})
+ @settings = settings
+ @attributes = self.class.take_attributes
+ @attributes.each_key do |key|
+ self.class.send(:attr_accessor, key)
+ instance_variable_set("@#{key}", settings[key])
+ end
+ self
+ end
+ extend DataModelMethods
+end
+
+class BaseStore
+ include Enumerable
+ attr_accessor :store, :id
+
+ def find(hash)
+ result = []
+ each do |setting|
+ result << setting if (hash.to_a - setting.to_a).empty?
+ end
+ result
+ end
+end
+
+class ArrayStore < BaseStore
+ def initialize
+ @store = []
+ end
+
+ def each
+ @store.each do |member|
+ yield(member)
+ end
+ end
+
+ def include?(hash)
+ @store.include? hash
+ end
+
+ def create(hash)
+ @store.push hash
+ end
+
+ def update(id, updates)
+ model = @store.select { |model| model[:id] == id }.first
+ updates.each_key do |key|
+ model[key] = updates[key]
+ end
+ end
+end
+
+class HashStore < BaseStore
+ def initialize
+ @store = {}
+ end
+
+ def each
+ @store.each do |_, value|
+ yield(value)
+ end
+ end
+
+ def include?(hash)
+ @store[:id] == hash
+ end
+
+ def create(hash)
+ @store[hash[:id]] = hash
+ end
+
+ def update(id, updates)
+ model = @store[id.to_sym]
+ updates.each_key do |key|
+ model[key] = updates[key]
+ end
+ end
+end