Решение на Пета задача - DataModel от Никола Жишев

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

Към профила на Никола Жишев

Резултати

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

Код

class DataModel
class DeleteUnsavedRecordError < StandardError
end
attr_accessor :id
def initialize(attributes = {})
attributes.each do |attribute, value|
setter = "#{attribute}="
send(setter, value) if respond_to? setter
end
end
def store
self.class.data_store
end
def save
if @id
store.udpate(to_h)
else
@id = store.next_id
store.create(to_h)
end
self
end
def ==(other)
return false unless other.instance_of? self.class
return true if id == other.id
object_id == other.object_id
end
def delete
if @id
store.delete(to_h)
self
else
raise DeleteUnsavedRecordError
end
end
def attributes
self.class.attributes
end
def to_h
{}.tap do |hash|
hash[:id] = id
attributes.each do |getter|
hash[getter.to_sym] = send(getter)
end
end
end
end
class << DataModel
def where(query)
query.keys.each do |attribute|
unless attributes.include? attribute
throw DataModel::UnknownAttributeError, "Unknown attribute #{attribute}"
end
end
data_store.find(query).map do |params|
instance_eval do
new(params)
end
end
end
def attributes(*attributes)
return @attributes if @attributes
@attributes = attributes
attr_accessor(*attributes)
attributes.each do |attribute|
define_singleton_method "find_by_#{attribute}" do |value|
where({ attribute.to_sym => value })
end
end
end
def data_store(store = Store.new)
@store ||= store
end
end
class Store
attr_reader :store
def initialize
@id = 0
end
def next_id
@id += 1
end
end
class HashStore < Store
def initialize
@store = {}
super
end
def create(record)
store[record[:id]] = record
end
def find(query)
[].tap do |result|
get(query) do |_id, record|
result << record
end
end
end
def update(id, new_attributes)
store[id].merge!(new_attributes)
end
def delete(query)
get(query) { |id, _record| store.delete(id) }
end
private
def get(query)
store.each do |id, record|
yield [id, record] if record.merge(query) == record
end
end
end
class ArrayStore < Store
def initialize
@store = []
super
end
def create(record)
store << record
end
def find(query)
[].tap do |result|
get(query) do |record|
result << record
end
end
end
def update(id, new_attributes)
get(id: id) do |record|
record.merge!(new_attributes)
end
end
def delete(query)
get(query) { |record| store.delete(record) }
end
private
def get(query)
store.each do |record|
yield record if record.merge(query) == record
end
end
end

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

....F..FF...F...........F

Failures:

  1) DataModel id generation creates id on first save and does not change it
     Failure/Error: record.save
     NoMethodError:
       undefined method `udpate' for #<HashStore:0x007f9f9e3782d8>
     # /tmp/d20161202-15620-1j77o7x/solution.rb:20:in `save'
     # /tmp/d20161202-15620-1j77o7x/spec.rb:57: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 equality comparison compares by id if both records are saved
     Failure/Error: modified_ivan = user_model.where(id: ivan.id).first
     NameError:
       uninitialized constant DataModel::UnknownAttributeError
     # /tmp/d20161202-15620-1j77o7x/solution.rb:61:in `block in where'
     # /tmp/d20161202-15620-1j77o7x/solution.rb:59:in `each'
     # /tmp/d20161202-15620-1j77o7x/solution.rb:59:in `where'
     # /tmp/d20161202-15620-1j77o7x/spec.rb:102: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 equality comparison uses #equal? if there are no ids
     Failure/Error: expect(first_user).to_not eq second_user
       
       expected: value != #<#<Class:0x007f9f9e176a98>:0x007f9f9eea7de8 @first_name="Ivan">
            got: #<#<Class:0x007f9f9e176a98>:0x007f9f9eea7f00 @first_name="Ivan">
       
       (compared using ==)
       
       Diff:
       @@ -1,2 +1,2 @@
       -#<#<Class:0x007f9f9e176a98>:0x007f9f9eea7de8 @first_name="Ivan">
       +#<#<Class:0x007f9f9e176a98>:0x007f9f9eea7f00 @first_name="Ivan">
     # /tmp/d20161202-15620-1j77o7x/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)>'

  4) DataModel.where raises an error if the query is by an unknown key
     Failure/Error: DataModel::UnknownAttributeError,
     NameError:
       uninitialized constant DataModel::UnknownAttributeError
     # /tmp/d20161202-15620-1j77o7x/spec.rb:144: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) ArrayStore behaves like a data store #delete can delete multiple records with a single query
     Failure/Error: expect(store.find({})).to eq [gosho]
       
       expected: [{:id=>3, :name=>"Gosho"}]
            got: [{:id=>2, :name=>"Pesho"}, {:id=>3, :name=>"Gosho"}]
       
       (compared using ==)
       
       Diff:
       @@ -1,2 +1,2 @@
       -[{:id=>3, :name=>"Gosho"}]
       +[{:id=>2, :name=>"Pesho"}, {:id=>3, :name=>"Gosho"}]
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-1j77o7x/spec.rb:239
     # /tmp/d20161202-15620-1j77o7x/spec.rb:229: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.02823 seconds
25 examples, 5 failures

Failed examples:

rspec /tmp/d20161202-15620-1j77o7x/spec.rb:48 # DataModel id generation creates id on first save and does not change it
rspec /tmp/d20161202-15620-1j77o7x/spec.rb:92 # DataModel equality comparison compares by id if both records are saved
rspec /tmp/d20161202-15620-1j77o7x/spec.rb:108 # DataModel equality comparison uses #equal? if there are no ids
rspec /tmp/d20161202-15620-1j77o7x/spec.rb:142 # DataModel.where raises an error if the query is by an unknown key
rspec /tmp/d20161202-15620-1j77o7x/spec.rb:218 # ArrayStore behaves like a data store #delete can delete multiple records with a single query

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

Никола обнови решението на 01.12.2016 22:45 (преди около 8 години)

+class DataModel
+ class DeleteUnsavedRecordError < StandardError
+ end
+
+ attr_accessor :id
+
+ def initialize(attributes = {})
+ attributes.each do |attribute, value|
+ setter = "#{attribute}="
+ send(setter, value) if respond_to? setter
+ end
+ end
+
+ def store
+ self.class.data_store
+ end
+
+ def save
+ if @id
+ store.udpate(to_h)
+ else
+ @id = store.next_id
+ store.create(to_h)
+ end
+ self
+ end
+
+ def ==(other)
+ return false unless other.instance_of? self.class
+ return true if id == other.id
+ object_id == other.object_id
+ end
+
+ def delete
+ if @id
+ store.delete(to_h)
+ self
+ else
+ raise DeleteUnsavedRecordError
+ end
+ end
+
+ def attributes
+ self.class.attributes
+ end
+
+ def to_h
+ {}.tap do |hash|
+ hash[:id] = id
+ attributes.each do |getter|
+ hash[getter.to_sym] = send(getter)
+ end
+ end
+ end
+end
+
+class << DataModel
+ def where(query)
+ query.keys.each do |attribute|
+ unless attributes.include? attribute
+ throw DataModel::UnknownAttributeError, "Unknown attribute #{attribute}"
+ end
+ end
+ data_store.find(query).map do |params|
+ instance_eval do
+ new(params)
+ end
+ end
+ end
+
+ def attributes(*attributes)
+ return @attributes if @attributes
+
+ @attributes = attributes
+ attr_accessor(*attributes)
+
+ attributes.each do |attribute|
+ define_singleton_method "find_by_#{attribute}" do |value|
+ where({ attribute.to_sym => value })
+ end
+ end
+ end
+
+ def data_store(store = Store.new)
+ @store ||= store
+ end
+end
+
+class Store
+ attr_reader :store
+
+ def initialize
+ @id = 0
+ end
+
+ def next_id
+ @id += 1
+ end
+end
+
+class HashStore < Store
+ def initialize
+ @store = {}
+ super
+ end
+
+ def create(record)
+ store[record[:id]] = record
+ end
+
+ def find(query)
+ [].tap do |result|
+ get(query) do |_id, record|
+ result << record
+ end
+ end
+ end
+
+ def update(id, new_attributes)
+ store[id].merge!(new_attributes)
+ end
+
+ def delete(query)
+ get(query) { |id, _record| store.delete(id) }
+ end
+
+ private
+
+ def get(query)
+ store.each do |id, record|
+ yield [id, record] if record.merge(query) == record
+ end
+ end
+end
+
+class ArrayStore < Store
+ def initialize
+ @store = []
+ super
+ end
+
+ def create(record)
+ store << record
+ end
+
+ def find(query)
+ [].tap do |result|
+ get(query) do |record|
+ result << record
+ end
+ end
+ end
+
+ def update(id, new_attributes)
+ get(id: id) do |record|
+ record.merge!(new_attributes)
+ end
+ end
+
+ def delete(query)
+ get(query) { |record| store.delete(record) }
+ end
+
+ private
+
+ def get(query)
+ store.each do |record|
+ yield record if record.merge(query) == record
+ end
+ end
+end