[Ruby on Rails 5] Exporting records to TSV in Rails 5.1

MIME type の追加など、手順を忘れていたので記事に起こしておきます。

% rails --version
Rails 5.1.4

こちらを参考にさせて頂きました。

rails generate

省略。
rails g でこのようなプロジェクトを作成してあります。

ActiveRecord::Schema.define(version: 20180118080513) do
  create_table "data", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.integer "depth"
    t.string "title"
    t.string "filename"
    t.text "description"
    t.string "keywords"
    t.text "content_1"
    t.text "content_2"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
end

TSV Download

TSV は CSV.generate(col_sep: "\t") で作成します。

View

ダウンロードボタンをビューの一覧に追加します。

views/data/index.html.haml
.btn-group
  = link_to 'Download TSV', data_path(format: 'tsv'), class: 'btn btn-primary'

Controller

respond_to を利用して、フォーマットに応じた出力をコントローラで指定します。

controllers/data_controller.rb
class DataController < ApplicationController
  # GET /data
  # GET /data.json
  # GET /data.tsv
  def index
    @data = Datum.all
    respond_to do |format|
      format.html
      format.json { render json: @data }
      format.tsv { send_data @data.to_csv(col_sep: "\t") }
    end
  end
end

Model

モデルに to_csv メソッドを用意します。

models/datum.rb
class Datum < ApplicationRecord
  def self.to_csv(options = {})
    CSV.generate(options) do |csv|
      csv << column_names
      all.each do |data|
        csv << data.attributes.values_at(*column_names)
      end
    end
  end
end

Config

require 'csv' をします。

config/application.rb
require 'csv'

また未設定の場合、下記のようなエラーが表示されて、Rails Guides を見て MIME type を登録してね、と言われます。

To respond to a custom format, register it as a MIME type first: http://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. If you meant to respond to a variant like :tablet or :phone, not a custom format, be sure to nest your variant response within a format response: format.html { |html| html.tablet { ... } }

MIME type も追加しておきます。

config/initializers/mime_types.rb
Mime::Type.register 'text/tab-separated-values', :tsv

サーバを再起動するとダウンロードが可能になります。

補遺

View を用意するパターン。

# app/models/manufacture.rb
class Manufacture < ActiveRecord::Base
  has_many :products
end
# app/contollers/products_contoller.rb
class ProductsController < ApplicationController
  def index
    # N+1問題のため、allではなくincludes
    @products = Product.includes(:manufacture)
  end
end
# app/models/product.rb
class Product < ActiveRecord::Base
  belongs_to :manufacture
end