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

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

Към профила на Мариян Асенов

Резултати

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

Код

module DynamicMethods
private
def make_getter(attributes)
(attributes << :id).each do |name|
define_method name do
@hash.fetch(name)
end
end
end
def make_setter(attributes)
attributes.each do |name|
define_method "#{name}=" do |value|
@hash[name] = value
end
end
end
def make_find_by_attribute(attributes)
(attributes << :id).each do |name|
define_singleton_method "find_by_#{name}" do |value|
where({name => value})
end
end
end
end
module SingletonMethods
def attributes(*attributes_given)
unless attributes_given.empty?
@attributes = Array.new(attributes_given)
make_getter(@attributes.dup)
make_setter(@attributes)
make_find_by_attribute(@attributes.dup)
end
@attributes.dup
end
def data_store(data_store = nil)
@storage = data_store unless data_store.nil?
@storage.dup
end
private
def make_array_of_found_records(records)
records.map do |record|
object = self.new
object.instance_variable_set(:@hash, record)
object
end
end
end
class DataModel
extend DynamicMethods
extend SingletonMethods
class UnknownAttributeError < StandardError
end
class DeleteUnsavedRecordError < StandardError
end
def initialize(hash = {})
@hash = {id: nil}
self.class.attributes.each do |item|
@hash[item] = hash[item]
end
end
def save(hash = @hash)
if id.nil? && hash == @hash
self.class.data_store.create(@hash)
elsif !self.class.data_store.find(@hash).empty? && @hash != hash
self.class.data_store.update(id, hash)
else
self.class.data_store.create(@hash)
self.class.data_store.update(id, hash)
end
self.dup
end
def ==(other)
other.is_a?(self.class) &&
self.id == other.id ||
self.equal?(other)
end
def delete
raise DeleteUnsavedRecordError.new if id.nil?
self.class.data_store.delete(@hash)
end
def self.where(search)
if search.keys.all? { |key| attributes.include?(key) }
make_array_of_found_records(data_store.find(search))
else
unknown_attributes = search.keys.select { |key| !attributes.include?(key) }
raise UnknownAttributeError.new, "Unknown attribute #{unknown_attributes.first}"
end
end
end
class HashStore
def initialize
@records = {}
end
def create(hash)
if hash.fetch(:id).nil?
id = ID.next
hash[:id] = id
end
@records[id] = hash
end
def find(hash)
@records.values.select do |value|
hash.keys.all? do |key|
value.key?(key) &&
value.fetch(key) == hash.fetch(key)
end
end
end
def update(id, hash)
hash.each_key { |key| @records[id][key] = hash[key] }
end
def delete(hash)
@records.delete(hash.fetch(:id))
end
def storage
@records.dup
end
private
ID = (1...Float::INFINITY).to_enum
end
class ArrayStore
def initialize
@records = []
end
def create(hash)
hash[:id] = ID.next if hash.fetch(:id).nil?
@records.push(hash)
end
def find(hash)
@records.select do |value|
hash.keys.all? do |key|
value.key?(key) &&
value.fetch(key) == hash.fetch(key)
end
end
end
def update(id, hash)
current_object = @records.select { |item| item.fetch(:id) == id }
hash.each_key { |key| current_object.first[key] = hash[key] }
end
def delete(hash)
@records.delete(hash)
end
def storage
@records.dup
end
private
ID = (1...Float::INFINITY).to_enum
end

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

.F...FFFF........FFF....F

Failures:

  1) DataModel has attributes and data_model getters
     Failure/Error: expect(user_model.data_store).to eq data_store
       
       expected: #<HashStore:0x007fae7795baf0 @records={}>
            got: #<HashStore:0x007fae778c9650 @records={}>
       
       (compared using ==)
       
       Diff:
       @@ -1,2 +1,2 @@
       -#<HashStore:0x007fae7795baf0 @records={}>
       +#<HashStore:0x007fae778c9650 @records={}>
     # /tmp/d20161202-15620-16gmb7x/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 id generation does not reuse ids
     Failure/Error: expect(ivan.id).to eq 1
       
       expected: 1
            got: 3
       
       (compared using ==)
     # /tmp/d20161202-15620-16gmb7x/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)>'

  3) 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: 4
       
       (compared using ==)
     # /tmp/d20161202-15620-16gmb7x/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)>'

  4) DataModel equality comparison compares by id if both records are saved
     Failure/Error: modified_ivan = user_model.where(id: ivan.id).first
     DataModel::UnknownAttributeError:
       Unknown attribute id
     # /tmp/d20161202-15620-16gmb7x/solution.rb:98:in `where'
     # /tmp/d20161202-15620-16gmb7x/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)>'

  5) DataModel equality comparison uses #equal? if there are no ids
     Failure/Error: expect(first_user).to_not eq second_user
       
       expected: value != #<#<Class:0x007fae783d2330>:0x007fae783c3e48 @hash={:id=>nil, :first_name=>"Ivan", :last_name=>nil, :age=>nil}>
            got: #<#<Class:0x007fae783d2330>:0x007fae783c3f60 @hash={:id=>nil, :first_name=>"Ivan", :last_name=>nil, :age=>nil}>
       
       (compared using ==)
       
       Diff:
       @@ -1,3 +1,3 @@
       -#<#<Class:0x007fae783d2330>:0x007fae783c3e48
       +#<#<Class:0x007fae783d2330>:0x007fae783c3f60
         @hash={:id=>nil, :first_name=>"Ivan", :last_name=>nil, :age=>nil}>
     # /tmp/d20161202-15620-16gmb7x/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)>'

  6) 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 `[]=' for nil:NilClass
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-16gmb7x/spec.rb:235
     # /tmp/d20161202-15620-16gmb7x/solution.rb:126:in `block in update'
     # /tmp/d20161202-15620-16gmb7x/solution.rb:126:in `each_key'
     # /tmp/d20161202-15620-16gmb7x/solution.rb:126:in `update'
     # /tmp/d20161202-15620-16gmb7x/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)>'

  7) 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 `[]=' for nil:NilClass
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-16gmb7x/spec.rb:235
     # /tmp/d20161202-15620-16gmb7x/solution.rb:126:in `block in update'
     # /tmp/d20161202-15620-16gmb7x/solution.rb:126:in `each_key'
     # /tmp/d20161202-15620-16gmb7x/solution.rb:126:in `update'
     # /tmp/d20161202-15620-16gmb7x/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)>'

  8) HashStore behaves like a data store #delete can delete multiple records with a single query
     Failure/Error: store.delete(name: 'Pesho')
     KeyError:
       key not found: :id
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-16gmb7x/spec.rb:235
     # /tmp/d20161202-15620-16gmb7x/solution.rb:130:in `fetch'
     # /tmp/d20161202-15620-16gmb7x/solution.rb:130:in `delete'
     # /tmp/d20161202-15620-16gmb7x/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)>'

  9) 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=>1, :name=>"Pesho"}, {:id=>2, :name=>"Pesho"}, {:id=>3, :name=>"Gosho"}]
       
       (compared using ==)
       
       Diff:
       @@ -1,2 +1,2 @@
       -[{:id=>3, :name=>"Gosho"}]
       +[{:id=>1, :name=>"Pesho"}, {:id=>2, :name=>"Pesho"}, {:id=>3, :name=>"Gosho"}]
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-16gmb7x/spec.rb:239
     # /tmp/d20161202-15620-16gmb7x/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.02956 seconds
25 examples, 9 failures

Failed examples:

rspec /tmp/d20161202-15620-16gmb7x/spec.rb:21 # DataModel has attributes and data_model getters
rspec /tmp/d20161202-15620-16gmb7x/spec.rb:62 # DataModel id generation does not reuse ids
rspec /tmp/d20161202-15620-16gmb7x/spec.rb:74 # DataModel id generation does not break when there are two models with the same store
rspec /tmp/d20161202-15620-16gmb7x/spec.rb:92 # DataModel equality comparison compares by id if both records are saved
rspec /tmp/d20161202-15620-16gmb7x/spec.rb:108 # DataModel equality comparison uses #equal? if there are no ids
rspec /tmp/d20161202-15620-16gmb7x/spec.rb:196 # HashStore behaves like a data store #update updates the attributes of a record with a given ID
rspec /tmp/d20161202-15620-16gmb7x/spec.rb:204 # HashStore behaves like a data store #update only updates records with the correct IDs
rspec /tmp/d20161202-15620-16gmb7x/spec.rb:218 # HashStore behaves like a data store #delete can delete multiple records with a single query
rspec /tmp/d20161202-15620-16gmb7x/spec.rb:218 # ArrayStore behaves like a data store #delete can delete multiple records with a single query

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

Мариян обнови решението на 01.12.2016 17:16 (преди над 7 години)

+module DynamicMethods
+ private
+ def make_getter(attributes)
+ (attributes << :id).each do |name|
+ define_method name do
+ @hash.fetch(name)
+ end
+ end
+ end
+
+ def make_setter(attributes)
+ attributes.each do |name|
+ define_method "#{name}=" do |value|
+ @hash[name] = value
+ end
+ end
+ end
+
+ def make_find_by_attribute(attributes)
+ (attributes << :id).each do |name|
+ define_singleton_method "find_by_#{name}" do |value|
+ where({name => value})
+ end
+ end
+ end
+end
+
+module SingletonMethods
+ def attributes(*attributes_given)
+ unless attributes_given.empty?
+ @attributes = Array.new(attributes_given)
+ make_getter(@attributes.dup)
+ make_setter(@attributes)
+ make_find_by_attribute(@attributes.dup)
+ end
+ @attributes.dup
+ end
+
+ def data_store(data_store = nil)
+ @storage = data_store unless data_store.nil?
+ @storage.dup
+ end
+
+ private
+ def make_array_of_found_records(records)
+ records.map do |record|
+ object = self.new
+ object.instance_variable_set(:@hash, record)
+ object
+ end
+ end
+end
+
+class DataModel
+ extend DynamicMethods
+ extend SingletonMethods
+
+ class UnknownAttributeError < StandardError
+ end
+ class DeleteUnsavedRecordError < StandardError
+ end
+
+ def initialize(hash = {})
+ @hash = {id: nil}
+ self.class.attributes.each do |item|
+ @hash[item] = hash[item]
+ end
+ end
+
+ def save(hash = @hash)
+ if id.nil? && hash == @hash
+ self.class.data_store.create(@hash)
+ elsif !self.class.data_store.find(@hash).empty? && @hash != hash
+ self.class.data_store.update(id, hash)
+ else
+ self.class.data_store.create(@hash)
+ self.class.data_store.update(id, hash)
+ end
+ self.dup
+ end
+
+ def ==(other)
+ other.is_a?(self.class) &&
+ self.id == other.id ||
+ self.equal?(other)
+ end
+
+ def delete
+ raise DeleteUnsavedRecordError.new if id.nil?
+ self.class.data_store.delete(@hash)
+ end
+
+ def self.where(search)
+ if search.keys.all? { |key| attributes.include?(key) }
+ make_array_of_found_records(data_store.find(search))
+ else
+ unknown_attributes = search.keys.select { |key| !attributes.include?(key) }
+ raise UnknownAttributeError.new, "Unknown attribute #{unknown_attributes.first}"
+ end
+ end
+end
+
+class HashStore
+ def initialize
+ @records = {}
+ end
+
+ def create(hash)
+ if hash.fetch(:id).nil?
+ id = ID.next
+ hash[:id] = id
+ end
+ @records[id] = hash
+ end
+
+ def find(hash)
+ @records.values.select do |value|
+ hash.keys.all? do |key|
+ value.key?(key) &&
+ value.fetch(key) == hash.fetch(key)
+ end
+ end
+ end
+
+ def update(id, hash)
+ hash.each_key { |key| @records[id][key] = hash[key] }
+ end
+
+ def delete(hash)
+ @records.delete(hash.fetch(:id))
+ end
+
+ def storage
+ @records.dup
+ end
+ private
+ ID = (1...Float::INFINITY).to_enum
+end
+
+class ArrayStore
+ def initialize
+ @records = []
+ end
+
+ def create(hash)
+ hash[:id] = ID.next if hash.fetch(:id).nil?
+ @records.push(hash)
+ end
+
+ def find(hash)
+ @records.select do |value|
+ hash.keys.all? do |key|
+ value.key?(key) &&
+ value.fetch(key) == hash.fetch(key)
+ end
+ end
+ end
+
+ def update(id, hash)
+ current_object = @records.select { |item| item.fetch(:id) == id }
+ hash.each_key { |key| current_object.first[key] = hash[key] }
+ end
+
+ def delete(hash)
+ @records.delete(hash)
+ end
+
+ def storage
+ @records.dup
+ end
+ private
+ ID = (1...Float::INFINITY).to_enum
+end