やりたいこと
CSV 出力は案件ごとに手製で作ることが多いと思います。業務で関わるサービスだと列数も多く、並び替えや条件による列の追加・削除でバグが起きやすいですよね。
問題はデータ部の出力と列の並びが直結しているので、それを分けてやると管理しやすいと考えました
require 'csv'
Book = Struct.new(:id, :title, :price)
books = [Book.new(1, 'How to cook delicious meals', 1500), Book.new(2, '10 tips to lose weight', 250)]
table = CSV::Table.new([])
headers = [
'id',
'title',
...
'price'
]
# メンテナが代わることによる無理な列名の追加...
headers << 'tax price' if ....
books.each do |book|
# ヘッダ部をみながらデータ部の並びを整えるのが大変...
table << CSV::Row.new(headers, [book.id, book.title, ..., book.price])
end
puts table.to_csv
やってみたこと
数年前ですが、csv_box を作ってみました。Rails で使われることを想定していて、いまのところカラムの内容をそのまま出力します。
CSVBox.add
で出力したい列を定義します。ここでは並びは関係なく、何を出力したいかです。引数にはモデル名など対象がわかるようにすると良いと思います。
CSVBox.add 'book' do
field 'id'
field 'title'
field 'price'
end
CSVBox.layouts
は CSVBox.add
で登録した対象にどの順番で出力したいかを決めることができます。レイアウトに layout
で名前をつけることができて CSV の作成時に使います。
CSVBox.layouts 'book' do
layout 'shuffled layout' do |box|
box.price
box.id
box.title
end
layout 'simple layout' do |box|
box.title
box.price
end
end
CSVBox.take
で対象の名前とレイアウト名を指定して、何をどうやって出力するかを決めます。<<
で CSV::Table
を使って行を追加してたように追加することができます。
box = CSVBox.take 'book', 'shuffled layout'
books.each do |book|
box << book
end
puts box.to_csv
__END__
price,id,title
1500,1,How to cook delicious meals
250,2,10 tips to lose weight
レイアウトの変更も楽で条件によって変えるのであれば次のようになります。
box = if ...
CSVBox.take 'book', 'shuffled layout'
else
CSVBox.take 'book', 'simple layout'
end
books.each do |book|
box << book
end
puts box.to_csv
CSV 出力と分けて、対象の何を出力するのか、カラムの順番や条件をレイアウト毎に変えてやることによってかなりスッキリしたのではないでしょうか?今後ですが、例えば税込価格を表示したいなどその時だけの計算もあると思いますデコレータで逃げてもいいと思いますが、計算も組み込めるようになるのが今後の展望です。