Às vezes você tem alguns dados como estes:
Dave,32,employee,15.75
Harris,23,employee,15.75
Mary,38,supervisor,21.25
E você o organiza com um objeto Struct:
require 'csv'
Person = Struct.new :name, :age, :category, :wage
people = CSV.parse(data).map { |args| Person.new(*args) }
Mas seus dados ficam assim:
#<struct Person name="Dave", age="32", category="employee" ... >
#<struct Person name="Harris", age="23", category="employee" ... >
...
Não seria bom ter o Struct lançando automaticamente cada argumento para um tipo de dados especificado? Na falta de qualquer método onipresente para lançar dados em Ruby, podemos fazer a próxima melhor coisa e especificar um método a ser chamado. Conheça CastingStruct:
class CastingStruct < Struct
def self.new(hash, &blk)
super(*hash.keys) do
define_method :initialize do |*args|
super *hash.values.map { |method|
args.shift.public_send method
}
end
class_eval(&blk) if blk
end
end
end
Agora você pode criar uma estrutura que recebe um hash no formato field_name => método .
Person = CastingStruct.new name: :to_s,
age: :to_i,
category: :to_sym,
wage: :to_f
people = CSV.parse(data).map { |args| Person.new(*args) }
Agora, o objeto em cada campo é uma instância da classe correta
irb> people.first
=> #<struct Person name="Dave", age=32, category=:employee, wage=15.75>
irb> people.first.category.class
=> Symbol
Muito melhor 🙂