-
-
Save yesnik/60389cbc330352139b29 to your computer and use it in GitHub Desktop.
require 'time' | |
module Converter | |
extend Enumerable | |
@@methods = {} | |
def new(*args, &block) | |
initialize(*args, &block) | |
end | |
def initialize(*args, &block) | |
@arr = args[0] | |
convert | |
end | |
def attrb(attribute, method=nil, &block) | |
method = block if block_given? | |
@@methods[attribute] = method | |
end | |
def convert | |
@arr.map do |hash| | |
convert_hash(hash) | |
end | |
@arr | |
end | |
def convert_hash(hash) | |
hash = hash.each do |k, v| | |
new_value = convert_item(k, v) | |
hash[k] = new_value if not new_value.nil? | |
end | |
end | |
def convert_item(k, v) | |
if @@methods[k.to_sym] | |
method = @@methods[k.to_sym] | |
if method.is_a? Proc | |
method.call(v) | |
else | |
v.send(method) | |
end | |
end | |
end | |
end | |
# Пример преобразователя: | |
class Transactions | |
extend Converter | |
#attrb :uid # значение будет передаваться как есть без преобразования | |
attrb :sum, :to_f # для преобразования значения будет вызван его(значения) метод to_f '50.25'.to_f | |
# для преобразования значения будет вызван блок, | |
# который принимает исходное значение и возвращает преобразованное | |
attrb (:timestamp) { |value| Time.parse(value) } | |
end | |
transactions = Transactions.new([ | |
# входной массив хэшей где все значения - строки | |
{:uid => 'HT150', :sum => '50.25', :timestamp => '2014-04-04 05:50'}, | |
{:uid => 'HT151', :sum => '119.63', :timestamp => '2014-04-04 06:18'} | |
]) | |
# Значения преобразованы согласно правилам преобразованиия, описанным в Transactions | |
p transactions.to_a | |
# => [{:uid=>"HT150", :sum=>50.25, :timestamp=>2014-04-04 05:50:00 +0600}, | |
# {:uid=>"HT151", :sum=>119.63, :timestamp=>2014-04-04 06:18:00 +0600}] | |
p transactions.first # => {:uid=>"HT150", :sum=>50.25, :timestamp=>2014-04-04 05:50:00 +0600} | |
p transactions.select { |tx| tx[:sum] > 100 } | |
# => [{:uid => 'HT151', :sum => 119.63, :timestamp => 2014-04-04 06:18:00 +0600}] | |
=begin | |
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |
Не могу понять, как нужно реализовать Converter, | |
чтобы можно было так вызвать метод sum | |
p transactions.sum # => 169.88 | |
Такое возможно, если сделать include в класс Transactions | |
другого модуля, содержащего метод sum. | |
Однако в условии класс Transactions только | |
содержит extend модуля Converter. | |
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |
=end | |
#Проверка | |
class People | |
extend Converter | |
attrb :name | |
attrb :height, :to_i | |
attrb :birthday do |value| | |
Date.parse(value) | |
end | |
end | |
# Он должен будет работать так: | |
ppl = People.new([{:name => 'Vlas', :height => '205', :birthday => '1990-08-08'}]) | |
p ppl.first # => {:name => 'Vlas', :height => 205, :birthday => Wed, 08 Aug 1990} |
Спасибо большое за подробный комментарий! 😄 Выполнил работу над ошибками.
- sum реализовал в Transactions
- устранил магию с _new(_args, &block), initialize(args, &block), заменив на new(arr) -> initailize(arr)
- convert возвращал массив, чтобы работали выражения, задействующие методы модуля Enumerable: transactions.first, transactions.select. Я отказался от включения Enumerable в модуль Converter, вместо этого реализовал методы, используемые в условии задачи: .to_a, .first, .select
- вместо if not написал unless. Вопрос: имею ли я право писать "if !" или "if not"?
- убрал преобразование ключей в символы. Теперь буду знать, что символы не вычищаются сборщиком мусора (до версии 2.2). Вопрос: получается, что в версии Ruby >= 2.2 в данном задании лучше преобразовать ключи в символы, чтобы улучшить скорость поиска значений по хэшу?
require 'time'
module Converter
@@methods = {}
def new(arr)
initialize(arr)
self
end
def initialize(arr)
@arr = arr
convert
end
def attrb(attribute, method=nil, &block)
method = block if block_given?
@@methods[attribute] = method
end
def convert
@arr.map do |hash|
convert_hash(hash)
end
end
def convert_hash(hash)
hash = hash.each do |k, v|
new_value = convert_item(k, v)
hash[k] = new_value unless new_value.nil?
end
end
def to_a
@arr
end
def first
@arr[0]
end
def select(&block)
@arr.select(&block)
end
def convert_item(k, v)
if @@methods[k]
method = @@methods[k]
if method.is_a? Proc
method.call(v)
else
v.send(method)
end
end
end
end
# Пример преобразователя:
class Transactions
extend Converter
attrb :uid # значение будет передаваться как есть без преобразования
attrb :sum, :to_f # для преобразования значения будет вызван его(значения) метод to_f '50.25'.to_f
# для преобразования значения будет вызван блок,
# который принимает исходное значение и возвращает преобразованное
attrb (:timestamp) { |value| Time.parse(value) }
def self.sum
@arr.inject(0) { |sum, hash| sum += hash[:sum] }
end
end
transactions = Transactions.new([
# входной массив хэшей где все значения - строки
{:uid => 'HT150', :sum => '50.25', :timestamp => '2014-04-04 05:50'},
{:uid => 'HT151', :sum => '119.63', :timestamp => '2014-04-04 06:18'}
])
# Значения преобразованы согласно правилам преобразованиия, описанным в Transactions
p transactions.to_a
# => [{:uid=>"HT150", :sum=>50.25, :timestamp=>2014-04-04 05:50:00 +0500},
# {:uid=>"HT151", :sum=>119.63, :timestamp=>2014-04-04 06:18:00 +0500}]
p transactions.first
# => {:uid=>"HT150", :sum=>50.25, :timestamp=>2014-04-04 05:50:00 +0500}
p transactions.select { |tx| tx[:sum] > 100 }
# => [{:uid=>"HT151", :sum=>119.63, :timestamp=>2014-04-04 06:18:00 +0500}]
p transactions.sum
# => 169.88
#Тест 1
class People
extend Converter
attrb :name
attrb :height, :to_i
attrb :birthday do |value|
Date.parse(value)
end
end
# Он должен будет работать так:
ppl = People.new([{:name => 'Vlas', :height => '205', :birthday => '1990-08-08'}])
p ppl.first
# => {:name=>"Vlas", :height=>205, :birthday=>#<Date: 1990-08-08 ((2448112j,0s,0n),+0s,2299161j)>}
# Тест 2
class ToFloat
extend Converter
attrb :value, :to_f
end
floats = ToFloat.new([{value: '10'}])
p floats.first[:value] # => 10.0
# Тест 3
class ToArray
extend Converter
attrb(:value) { |v| [v] }
end
to_array = ToArray.new([{value: 1}])
p to_array.first[:value] # => [1]
# Тест 4
class ToHash
extend Converter
attrb(:value) { |v| {key: v} }
end
to_hash = ToHash.new([{value: 1}])
p to_hash.first[:value] # => {:key=>1}
В приведенном решении действительно лучше использовать модуль Enumerable. В этом случае для transactions можно будет использовать методы map, inject, all? и др. Для этого следует в модуль Converter включить класс Enumerable, а также обязательно в Converter реализовать метод each.
require 'time'
module Converter
@@methods = {}
include Enumerable
def new(arr)
initialize(arr)
end
def initialize(arr)
@arr = arr
convert
self
end
def attrb(attribute, method=nil, &block)
method = block if block_given?
@@methods[attribute] = method
end
def convert
@arr.map do |hash|
convert_hash(hash)
end
end
def convert_hash(hash)
hash = hash.each do |k, v|
new_value = convert_item(k, v)
hash[k] = new_value unless new_value.nil?
end
end
def to_a
@arr
end
def each(&block)
@arr.each(&block)
end
def convert_item(k, v)
if @@methods[k]
method = @@methods[k]
if method.is_a? Proc
method.call(v)
else
v.send(method)
end
end
end
end
# Пример преобразователя:
class Transactions
extend Converter
attrb :uid # значение будет передаваться как есть без преобразования
attrb :sum, :to_f # для преобразования значения будет вызван его(значения) метод to_f '50.25'.to_f
# для преобразования значения будет вызван блок,
# который принимает исходное значение и возвращает преобразованное
attrb (:timestamp) { |value| Time.parse(value) }
def self.sum
@arr.inject(0) { |sum, hash| sum += hash[:sum] }
end
end
transactions = Transactions.new([
# входной массив хэшей где все значения - строки
{:uid => 'HT150', :sum => '50.25', :timestamp => '2014-04-04 05:50'},
{:uid => 'HT151', :sum => '119.63', :timestamp => '2014-04-04 06:18'}
])
# Значения преобразованы согласно правилам преобразованиия, описанным в Transactions
p transactions.to_a
# => [{:uid=>"HT150", :sum=>50.25, :timestamp=>2014-04-04 05:50:00 +0500},
# {:uid=>"HT151", :sum=>119.63, :timestamp=>2014-04-04 06:18:00 +0500}]
p transactions.first
# => {:uid=>"HT150", :sum=>50.25, :timestamp=>2014-04-04 05:50:00 +0500}
p transactions.select { |tx| tx[:sum] > 100 }
# => [{:uid=>"HT151", :sum=>119.63, :timestamp=>2014-04-04 06:18:00 +0500}]
p transactions.sum
# => 169.88
#Тест 1
class People
extend Converter
attrb :name
attrb :height, :to_i
attrb :birthday do |value|
Date.parse(value)
end
end
# Он должен будет работать так:
ppl = People.new([{:name => 'Vlas', :height => '205', :birthday => '1990-08-08'}])
p ppl.first
# => {:name=>"Vlas", :height=>205, :birthday=>#<Date: 1990-08-08 ((2448112j,0s,0n),+0s,2299161j)>}
# Тест 2
class ToFloat
extend Converter
attrb :value, :to_f
end
floats = ToFloat.new([{value: '10'}])
p floats.first[:value] # => 10.0
# Тест 3
class ToArray
extend Converter
attrb(:value) { |v| [v] }
end
to_array = ToArray.new([{value: 1}])
p to_array.first[:value] # => [1]
# Тест 4
class ToHash
extend Converter
attrb(:value) { |v| {key: v} }
end
to_hash = ToHash.new([{value: 1}])
p to_hash.first[:value] # => {:key=>1}
Никита, а ведь ты не решил проблему.
class ToFloat
extend Converter
attrb :value, :to_f
end
class ToArray
extend Converter
attrb(:value) { |v| [v] }
end
floats = ToFloat.new([{value: '10'}])
p floats.first[:value] # => ["10"]
определение класса ToArray изменяет поведение ToFloat
Владимир, спасибо большое за это замечание! Действительно, в модуле Converter используется переменная класса @@methods, которая была задумана для хранения методов, нужных для преобразования. Все бы ничего, однако данная переменная доступна для всех экземпляров классов, которые используют модуль Converter. Поэтому пришлось ее заменить на переменную экземпляра @methods, которая существует в рамках отдельно взятого экземпляра.
Исправленная версия модуля Converter:
require 'time'
module Converter
include Enumerable
def new(arr)
initialize(arr)
end
def initialize(arr)
@arr = arr
convert
self
end
def attrb(attribute, method=nil, &block)
if @methods.nil?
@methods = {}
end
method = block if block_given?
@methods[attribute] = method unless method.nil?
end
def convert
@arr.map do |hash|
convert_hash(hash)
end
end
def convert_hash(hash)
hash = hash.each do |k, v|
new_value = convert_item(k, v)
hash[k] = new_value unless new_value.nil?
end
end
def to_a
@arr
end
def each(&block)
@arr.each(&block)
end
def convert_item(k, v)
if @methods[k]
method = @methods[k]
if method.is_a? Proc
method.call(v)
else
v.send(method)
end
end
end
end
В целом OK.