Created
October 3, 2017 19:38
-
-
Save asterite/a04cb8920175c4b0fae42d88c4cce40d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "csv" | |
class CSV | |
macro mapping(mappings) | |
{% for key, value in mappings %} | |
{% mappings[key] = {type: value} unless value.is_a?(HashLiteral) %} | |
{% end %} | |
{% for key, mapping in mappings %} | |
property {{key}} : {{mapping[:type]}} | |
{% end %} | |
def initialize( | |
{% for key, mapping in mappings %} | |
@{{key.id}}, | |
{% end %} | |
) | |
end | |
def self.from_csv(string) | |
rows = CSV.parse(string) | |
headers = rows.shift.map &.strip | |
objects = Array(self).new(rows.size) | |
rows.each do |row| | |
row = row.map &.strip | |
next if row.all? &.empty? | |
{% for key, mapping in mappings %} | |
__{{key.id}} = nil | |
{% end %} | |
row.each_with_index do |cell, i| | |
case headers[i]? | |
{% for key, mapping in mappings %} | |
when {{(mapping[:name] || key).id.stringify}} | |
{% if mapping[:nil_if_empty] %} | |
next if cell.empty? | |
{% end %} | |
__{{key.id}} = {{mapping[:type]}}.from_csv_cell(cell) | |
{% end %} | |
end | |
end | |
objects << new( | |
{% for key, mapping in mappings %} | |
{% if mapping[:nilable] %} | |
__{{key.id}}, | |
{% else %} | |
if __{{key.id}}.nil? | |
raise "Missing CSV value for column '{{(mapping[:name] || key).id}}'" | |
else | |
__{{key.id}}.not_nil! | |
end, | |
{% end %} | |
{% end %} | |
) | |
end | |
objects | |
end | |
def self.to_csv(objects) | |
CSV.build do |csv| | |
csv.row( | |
{% for key, mapping in mappings %} | |
{{(mapping[:name] || key).id.stringify}}, | |
{% end %} | |
) | |
objects.each &.to_csv(csv) | |
end | |
end | |
def to_csv(csv) | |
csv.row( | |
{% for key, mapping in mappings %} | |
if @{{key.id}}.nil? | |
nil | |
else | |
{{mapping[:type]}}.to_csv_cell(@{{key.id}}.not_nil!) | |
end, | |
{% end %} | |
) | |
end | |
end | |
end | |
struct Number | |
def self.from_csv_cell(string) | |
new(string) | |
end | |
def self.to_csv_cell(number) | |
new(number) | |
end | |
end | |
class String | |
def self.from_csv_cell(string) | |
string | |
end | |
def self.to_csv_cell(string) | |
string | |
end | |
end | |
class Array | |
def to_csv | |
T.to_csv(self) | |
end | |
def self.from_csv_cell(string) | |
T.from_csv_cell(string) | |
end | |
end | |
class Person | |
CSV.mapping({ | |
name: String, | |
age: Int32, | |
}) | |
end | |
csv = <<-CSV | |
name,age | |
Foo,10 | |
Bar,20 | |
CSV | |
people = Person.from_csv(csv) | |
p people |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment