Решение на Пета задача - DataModel от Георги Иванов

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

Към профила на Георги Иванов

Резултати

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

Код

class DataModel
class<<self
def attributes(*attributes)
return @attributes if attributes == []
@attributes = attributes.dup
(@attributes + [:id]).each do |name|
attr_accessor name.to_sym
define_method("find_by_#{name}") { |value| where({name: value}) }
end
end
def data_store(data_store = nil)
return @data_store if data_store == nil
@data_store = data_store
end
def where(**requirements)
requirements.keys.each do |attribute|
raise DataModel::UnknownAttributeError
.new(
"Unknown attribute #{attribute}"
) unless (@attributes + [:id]).include? attribute
end
@data_store.find(requirements).map { |hash| self.new(hash) }
end
end
class DeleteUnsavedRecordError < StandardError; end
class UnknownAttributeError < StandardError; end
def initialize(**attributes)
attributes.each do |k, v|
send("#{k}=", v) if respond_to?("#{k}=")
end
end
def ==(other)
return false unless self.instance_of? other.class
return @id == other.id if @id != nil && other.id != nil
self.object_id == other.object_id
end
def to_hash
self.class.attributes.map do |attribute|
[attribute.to_sym, send(attribute.to_s)]
end.to_h
end
def save
if @id == nil
@id = self.class.data_store.create(to_hash)
else
self.class.data_store.update(@id, to_hash)
end
self
end
def delete
self.class.data_store.delete(self.to_hash)
end
end
class ArrayStore
attr_reader :storage
def initialize
@storage = []
@next_id = 1
end
def create(record)
while @storage[@next_id] != nil do
(@next_id += 1)
end
record[:id] ||= @next_id
@storage[record[:id]] = record
record[:id]
end
def find(**query)
@storage.select { |record| (query.to_a - record.to_a).empty? }.to_a
end
def update(id, record)
@storage[id] = record
end
def delete(query)
to_be_deleted = @storage.select { |record| (query.to_a - record.to_a).empty? }
raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == []
@storage -= to_be_deleted
end
end
class HashStore
attr_reader :storage
def initialize
@storage = {}
@next_id = 1
end
def create(record)
while @storage[@next_id] != nil do
(@next_id += 1)
end
record[:id] ||= @next_id
@storage[record[:id]] = record
record[:id]
end
def find(**query)
@storage.select { |_, record| (query.to_a - record.to_a).empty? }
.map { |_, record| record }.to_a
end
def update(id, record)
@storage[id] = record
end
def delete(query)
to_be_deleted = @storage.select { |_, record| (query.to_a - record.to_a).empty? }
raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == {}
to_be_deleted.each { |id, _| @storage.delete(id) }
end
end

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

...F.F..................F

Failures:

  1) DataModel has #find_by_<attribute> methods
     Failure/Error: expect(user_model.find_by_first_name('Ivan').map(&:id)).to eq [record.id]
     NoMethodError:
       undefined method `find_by_first_name' for #<Class:0x007ff2aa064178>
     # /tmp/d20161202-15620-5xy4t7/spec.rb:43: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(georgi.id).to eq 2
       
       expected: 2
            got: 1
       
       (compared using ==)
     # /tmp/d20161202-15620-5xy4t7/spec.rb:71: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) 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: [nil, {:id=>3, :name=>"Gosho"}]
       
       (compared using ==)
       
       Diff:
       @@ -1,2 +1,2 @@
       -[{:id=>3, :name=>"Gosho"}]
       +[nil, {:id=>3, :name=>"Gosho"}]
     Shared Example Group: "a data store" called from /tmp/d20161202-15620-5xy4t7/spec.rb:239
     # /tmp/d20161202-15620-5xy4t7/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.02526 seconds
25 examples, 3 failures

Failed examples:

rspec /tmp/d20161202-15620-5xy4t7/spec.rb:39 # DataModel has #find_by_<attribute> methods
rspec /tmp/d20161202-15620-5xy4t7/spec.rb:62 # DataModel id generation does not reuse ids
rspec /tmp/d20161202-15620-5xy4t7/spec.rb:218 # ArrayStore behaves like a data store #delete can delete multiple records with a single query

История (5 версии и 2 коментара)

Георги обнови решението на 30.11.2016 13:44 (преди над 7 години)

+class DataModel
+ class<<self
+ def attributes(*attribute_names)
+ return @attribute_names if attribute_names == []
+ @attribute_names = attribute_names << :id
+ attribute_names.each do |name|
+ attr_accessor name.to_sym
+ define_method("find_by_#{name}") { where({name: value}) }
+ end
+ end
+
+ def data_store(store = nil)
+ return @store if store == nil
+ @store = store
+ end
+
+ def where(**requirements)
+ requirements.keys.each do |attribute_name|
+ raise DataModel::UnknownAttributeError
+ .new(
+ "Unknown attribute #{attribute_name}"
+ ) unless @attribute_names.include? attribute_name
+ end
+ @store.find(requirements).map { |hash| self.super.new(hash) }
+ end
+ end
+ class DeleteUnsavedRecordError < StandardError; end
+ class UnknownAttributeError < StandardError; end
+
+ def initialize(**attributes)
+ attributes.each do |k, v|
+ send("#{k}=", v) if respond_to?("#{k}=")
+ end
+ end
+
+ def ==(other)
+ return false unless self.instance_of? other.class
+ return @id == other.id if @id != nil && other.id != nil
+ self.object_id == other.object_id
+ end
+
+ def to_hash
+ self.class.attributes.map do |attribute|
+ [attribute.to_sym, send(attribute.to_s)]
+ end.to_h
+ end
+
+ def save
+ if self.id == nil
+ @id = self.class.data_store.create(self.to_hash)
+ else
+ self.class.data_store.update(self.to_hash)
+ end
+ end
+
+ def delete
+ self.class.data_store.delete(self.to_hash)
+ end
+end
+
+module Crud
+ def create(record)
+ while @storage[(@max_id += 1)] != nil
+ end
+ id = record[:id] || @max_id
+ record[:id] = id
+ @storage[id] = record
+ p @storage
+ id
+ end
+
+ def update(id, record)
+ @storage[id] = record
+ end
+
+ def delete(query)
+ to_be_deleted = @storage.select { |record| record.merge(query) == query }
+ raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == nil
+ @storage -= to_be_deleted
+ end
+end
+
+class ArrayStore
+ include Crud
+ attr_reader :storage
+
+ def initialize
+ @storage = []
+ @max_id = 0
+ end
+
+ def find(**query)
+ @storage.select { |record| (query.to_a - record.to_a).empty? }.to_a
+ end
+end
+class HashStore
+ include Crud
+ attr_reader :storage
+
+ def initialize
+ @storage = {}
+ @max_id = 0
+ end
+
+ def find(**query)
+ @storage.select { |_, record| (query.to_a - record.to_a).empty? }
+ .map { |_, record| record }.to_a
+ end
+end

Георги обнови решението на 30.11.2016 15:05 (преди над 7 години)

class DataModel
class<<self
def attributes(*attribute_names)
return @attribute_names if attribute_names == []
@attribute_names = attribute_names << :id
attribute_names.each do |name|
attr_accessor name.to_sym
define_method("find_by_#{name}") { where({name: value}) }
end
end
def data_store(store = nil)
return @store if store == nil
@store = store
end
def where(**requirements)
requirements.keys.each do |attribute_name|
raise DataModel::UnknownAttributeError
.new(
"Unknown attribute #{attribute_name}"
) unless @attribute_names.include? attribute_name
end
@store.find(requirements).map { |hash| self.super.new(hash) }
end
end
class DeleteUnsavedRecordError < StandardError; end
class UnknownAttributeError < StandardError; end
def initialize(**attributes)
attributes.each do |k, v|
send("#{k}=", v) if respond_to?("#{k}=")
end
end
def ==(other)
return false unless self.instance_of? other.class
return @id == other.id if @id != nil && other.id != nil
self.object_id == other.object_id
end
def to_hash
self.class.attributes.map do |attribute|
[attribute.to_sym, send(attribute.to_s)]
end.to_h
end
def save
if self.id == nil
@id = self.class.data_store.create(self.to_hash)
else
self.class.data_store.update(self.to_hash)
end
end
def delete
self.class.data_store.delete(self.to_hash)
end
end
-module Crud
+module StoreOperaions
def create(record)
while @storage[(@max_id += 1)] != nil
end
id = record[:id] || @max_id
record[:id] = id
@storage[id] = record
p @storage
id
end
def update(id, record)
@storage[id] = record
end
def delete(query)
to_be_deleted = @storage.select { |record| record.merge(query) == query }
raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == nil
@storage -= to_be_deleted
end
end
class ArrayStore
- include Crud
+ include StoreOperaions
attr_reader :storage
def initialize
@storage = []
@max_id = 0
end

Чудех се дали има начин да третирам двата контейнера еднакво(Array, Hash), т.е. да изнеса тази функция в модула. Макар двата класа да имат общи методи, повечето се различават коренно по поведението си - едните обхождат стойности, другите двойки (key, value) - разбираемо. Мислех си да имплементирам Array#to_hash и да го извиквам за контейнера. Това добра идея ли е или можа да го направя и без допълнителни функции като се поразровя още в документацията? Обхождане по индекс/ключ ми се струва грешно като идея, а в случая ми и неприложимо.

Нормално е да са отделни - не го мисли много. Аз не бих използвал StoreOperations - просто ще си ги напиша отделно. Да дублираш малко код е по-добре от това да направиш лоша абстракция. StoreOperations ще работи само за твоите два стора, не и за други. Освен това така ако искаш да промениш малко само единия клас няма да можеш лесно, понеже си ги завързал един за друг.

def find(**query)
@storage.select { |record| (query.to_a - record.to_a).empty? }.to_a
end
end
class HashStore
- include Crud
+ include StoreOperaions
attr_reader :storage
def initialize
@storage = {}
@max_id = 0
end
def find(**query)
@storage.select { |_, record| (query.to_a - record.to_a).empty? }
.map { |_, record| record }.to_a
end
end

Георги обнови решението на 01.12.2016 17:57 (преди над 7 години)

class DataModel
class<<self
- def attributes(*attribute_names)
- return @attribute_names if attribute_names == []
- @attribute_names = attribute_names << :id
- attribute_names.each do |name|
+ def attributes(*attributes)
+ return @attributes if attributes == []
+ @attributes = attributes.dup
+ (@attributes + [:id]).each do |name|
attr_accessor name.to_sym
- define_method("find_by_#{name}") { where({name: value}) }
+ define_method("find_by_#{name}") { |value| where({name: value}) }
end
end
- def data_store(store = nil)
- return @store if store == nil
- @store = store
+ def data_store(data_store = nil)
+ return @data_store if data_store == nil
+ @data_store = data_store
end
def where(**requirements)
- requirements.keys.each do |attribute_name|
+ requirements.keys.each do |attribute|
raise DataModel::UnknownAttributeError
.new(
- "Unknown attribute #{attribute_name}"
- ) unless @attribute_names.include? attribute_name
+ "Unknown attribute #{attribute}"
+ ) unless (@attributes + [:id]).include? attribute
end
- @store.find(requirements).map { |hash| self.super.new(hash) }
+ @data_store.find(requirements).map { |hash| self.new(hash) }
end
end
class DeleteUnsavedRecordError < StandardError; end
class UnknownAttributeError < StandardError; end
def initialize(**attributes)
attributes.each do |k, v|
send("#{k}=", v) if respond_to?("#{k}=")
end
end
def ==(other)
return false unless self.instance_of? other.class
return @id == other.id if @id != nil && other.id != nil
self.object_id == other.object_id
end
def to_hash
self.class.attributes.map do |attribute|
[attribute.to_sym, send(attribute.to_s)]
end.to_h
end
def save
if self.id == nil
@id = self.class.data_store.create(self.to_hash)
else
- self.class.data_store.update(self.to_hash)
+ self.class.data_store.update(self.id, self.to_hash)
end
+ self
end
def delete
self.class.data_store.delete(self.to_hash)
end
end
-module StoreOperaions
+class ArrayStore
+ attr_reader :storage
+
+ def initialize
+ @storage = []
+ @max_id = 0
+ end
+
def create(record)
while @storage[(@max_id += 1)] != nil
end
id = record[:id] || @max_id
record[:id] = id
@storage[id] = record
- p @storage
id
end
+ def find(**query)
+ @storage.select { |record| (query.to_a - record.to_a).empty? }.to_a
+ end
+
def update(id, record)
@storage[id] = record
end
def delete(query)
- to_be_deleted = @storage.select { |record| record.merge(query) == query }
- raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == nil
+ to_be_deleted = @storage.select { |record| (query.to_a - record.to_a).empty? }
+ raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == []
@storage -= to_be_deleted
end
end
-
-class ArrayStore
- include StoreOperaions
- attr_reader :storage
-
- def initialize
- @storage = []
- @max_id = 0
- end
-
- def find(**query)
- @storage.select { |record| (query.to_a - record.to_a).empty? }.to_a
- end
-end
class HashStore
- include StoreOperaions
attr_reader :storage
def initialize
@storage = {}
@max_id = 0
end
+ def create(record)
+ while @storage[(@max_id += 1)] != nil
+ end
+ id = record[:id] || @max_id
+ record[:id] = id
+ @storage[id] = record
+ id
+ end
+
def find(**query)
@storage.select { |_, record| (query.to_a - record.to_a).empty? }
.map { |_, record| record }.to_a
+ end
+
+ def update(id, record)
+ @storage[id] = record
+ end
+
+ def delete(query)
+ to_be_deleted = @storage.select { |_, record| (query.to_a - record.to_a).empty? }
+ raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == {}
+ to_be_deleted.each { |id, _| @storage.delete(id) }
end
end

Георги обнови решението на 01.12.2016 18:32 (преди над 7 години)

class DataModel
class<<self
def attributes(*attributes)
return @attributes if attributes == []
@attributes = attributes.dup
(@attributes + [:id]).each do |name|
attr_accessor name.to_sym
define_method("find_by_#{name}") { |value| where({name: value}) }
end
end
def data_store(data_store = nil)
return @data_store if data_store == nil
@data_store = data_store
end
def where(**requirements)
requirements.keys.each do |attribute|
raise DataModel::UnknownAttributeError
.new(
"Unknown attribute #{attribute}"
) unless (@attributes + [:id]).include? attribute
end
@data_store.find(requirements).map { |hash| self.new(hash) }
end
end
class DeleteUnsavedRecordError < StandardError; end
class UnknownAttributeError < StandardError; end
def initialize(**attributes)
attributes.each do |k, v|
send("#{k}=", v) if respond_to?("#{k}=")
end
end
def ==(other)
return false unless self.instance_of? other.class
return @id == other.id if @id != nil && other.id != nil
self.object_id == other.object_id
end
def to_hash
self.class.attributes.map do |attribute|
[attribute.to_sym, send(attribute.to_s)]
end.to_h
end
def save
- if self.id == nil
- @id = self.class.data_store.create(self.to_hash)
+ if @id == nil
+ @id = self.class.data_store.create(to_hash)
else
- self.class.data_store.update(self.id, self.to_hash)
+ self.class.data_store.update(@id, to_hash)
end
self
end
def delete
self.class.data_store.delete(self.to_hash)
end
end
class ArrayStore
attr_reader :storage
def initialize
@storage = []
@max_id = 0
end
def create(record)
while @storage[(@max_id += 1)] != nil
end
id = record[:id] || @max_id
record[:id] = id
@storage[id] = record
id
end
def find(**query)
@storage.select { |record| (query.to_a - record.to_a).empty? }.to_a
end
def update(id, record)
@storage[id] = record
end
def delete(query)
to_be_deleted = @storage.select { |record| (query.to_a - record.to_a).empty? }
raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == []
@storage -= to_be_deleted
end
end
class HashStore
attr_reader :storage
def initialize
@storage = {}
@max_id = 0
end
def create(record)
while @storage[(@max_id += 1)] != nil
end
id = record[:id] || @max_id
record[:id] = id
@storage[id] = record
id
end
def find(**query)
@storage.select { |_, record| (query.to_a - record.to_a).empty? }
.map { |_, record| record }.to_a
end
def update(id, record)
@storage[id] = record
end
def delete(query)
to_be_deleted = @storage.select { |_, record| (query.to_a - record.to_a).empty? }
raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == {}
to_be_deleted.each { |id, _| @storage.delete(id) }
end
end

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

class DataModel
class<<self
def attributes(*attributes)
return @attributes if attributes == []
@attributes = attributes.dup
(@attributes + [:id]).each do |name|
attr_accessor name.to_sym
define_method("find_by_#{name}") { |value| where({name: value}) }
end
end
def data_store(data_store = nil)
return @data_store if data_store == nil
@data_store = data_store
end
def where(**requirements)
requirements.keys.each do |attribute|
raise DataModel::UnknownAttributeError
.new(
"Unknown attribute #{attribute}"
) unless (@attributes + [:id]).include? attribute
end
@data_store.find(requirements).map { |hash| self.new(hash) }
end
end
class DeleteUnsavedRecordError < StandardError; end
class UnknownAttributeError < StandardError; end
def initialize(**attributes)
attributes.each do |k, v|
send("#{k}=", v) if respond_to?("#{k}=")
end
end
def ==(other)
return false unless self.instance_of? other.class
return @id == other.id if @id != nil && other.id != nil
self.object_id == other.object_id
end
def to_hash
self.class.attributes.map do |attribute|
[attribute.to_sym, send(attribute.to_s)]
end.to_h
end
def save
if @id == nil
@id = self.class.data_store.create(to_hash)
else
self.class.data_store.update(@id, to_hash)
end
self
end
def delete
self.class.data_store.delete(self.to_hash)
end
end
class ArrayStore
attr_reader :storage
def initialize
@storage = []
- @max_id = 0
+ @next_id = 1
end
def create(record)
- while @storage[(@max_id += 1)] != nil
+ while @storage[@next_id] != nil do
+ (@next_id += 1)
end
- id = record[:id] || @max_id
- record[:id] = id
- @storage[id] = record
- id
+ record[:id] ||= @next_id
+ @storage[record[:id]] = record
+ record[:id]
end
def find(**query)
@storage.select { |record| (query.to_a - record.to_a).empty? }.to_a
end
def update(id, record)
@storage[id] = record
end
def delete(query)
to_be_deleted = @storage.select { |record| (query.to_a - record.to_a).empty? }
raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == []
@storage -= to_be_deleted
end
end
class HashStore
attr_reader :storage
def initialize
@storage = {}
- @max_id = 0
+ @next_id = 1
end
def create(record)
- while @storage[(@max_id += 1)] != nil
+ while @storage[@next_id] != nil do
+ (@next_id += 1)
end
- id = record[:id] || @max_id
- record[:id] = id
- @storage[id] = record
- id
+ record[:id] ||= @next_id
+ @storage[record[:id]] = record
+ record[:id]
end
def find(**query)
@storage.select { |_, record| (query.to_a - record.to_a).empty? }
.map { |_, record| record }.to_a
end
def update(id, record)
@storage[id] = record
end
def delete(query)
to_be_deleted = @storage.select { |_, record| (query.to_a - record.to_a).empty? }
raise DataModel::DeleteUnsavedRecordError.new if to_be_deleted == {}
to_be_deleted.each { |id, _| @storage.delete(id) }
end
end