Ruby: CSVでヘッダとボディを同時に定義するやり方

こんにちは @kazz です。長い梅雨が終わって暑い夏がやってきました。夏のTechRachoフェアということで、今日はみなさん1度は実装したことのあるCSV出力に関して語って見たいと思います。 前提 今回は以下のテーブルをCSVに出力したいとおもいます。 DB class CreateArticles < ActiveRecord::Migration[5.2] def change create_table :articles do |t| t.integer :year # 発行年 t.integer :no # 号数 t.string :title # タイトル t.string :description # 内容 t.integer :price # 価格 t.timestamps end end end ※ NOT NULLとかINDEXとかはご容赦ください 適当にデータを突っ込んだ画面です。 ※新発売!はけんちゃんラーメン方式です CSVの出力 はじめの要件 全レコードの「タイトル」と「内容」「価格」だけをCSVに出力したい ヘッダは日本語でつけてね Articleのクラスメソッドで実装するならこんな実装になるかと思います。 class Article < ApplicationRecord def self.csv_dump header = %w[タイトル 内容 価格] CSV.generate do |csv| csv << header all.each do |article| row = [] row << article.title row << article.description row << article.price csv << row end end end end もちろん仕様変更します! 「内容」は全部 新発売! じゃないか!いらない 発行年度と号数を出したい こんな感じで改修すると思います。 class Article < ApplicationRecord def self.csv_dump header = %w[年度 号数 タイトル 価格] CSV.generate do |csv| csv << header all.each do |article| row = [ article.year, article.no, article.title, article.price ] csv << row end end end end もちろん仕様変更します!! じつは価格として税込価格(8%でいいよ)も追加して あぁ販売テーブルとJOINして販売数とかも追加して…. …😢 要求変更に関わる実装上の課題 このように仕様の変更が起こる度に、CSVの列に変更が入ります。 この実装例の場合、 header と row の両方を変更する必要があります。 列数が少なければ目視で確認できますが、列数が増えてくると取り違えする可能性が高くなります。 例えば私ならこういうCSVを作成してしまう可能性は120%自信があります(ドヤ 年度 号数 タイトル 販売数 価格 2018 1 2018年01号 540 500 2018 2 2018年02号 540 482 2018 3 2018年03号 540 38 … ※「販売数」と「価格」を取り違えた例。データ次第では気が付かない コードを改善しよう ヘッダと、ヘッダに対応する各値の実装が分離しているから取り違えてしまいます。 ならば同時に書ければ取り違えは起こりません。 こんなコードにしてみました。 def self.dump_csv rule = { ‘年度’ => lambda(&:year), ‘号数’ => … Continue reading Ruby: CSVでヘッダとボディを同時に定義するやり方