Rails で,あるモデルのデータ一覧を CSV でも TSV でもダウンロードできるようにするとき,ちょっとハマったのでメモ。
やりたかったこと
ItemsController の index
アクションで
def index
@items = Item.all
respond_to do |format|
format.html do
end
format.csv do
end
format.tsv do
end
end
end
みたいなことがしたかった。
要するに,リクエストが
-
/items
なら HTML を -
/items.csv
なら CSV を -
/items.tsv
なら TSV を
返す,と。
んで,CSV の場合のテンプレートは Ruby で記述し /app/views/items/index.csv.ruby
というファイルで提供する。
ここまではよくある話。
では TSV のテンプレートをどうするか?
最初にやった方法
TSV 用のテンプレートを CSV 用とは別に用意してもいいんだが,ほぼカラム区切りが違うくらい。CSV ライブラリーで使う col_sep
オプションね。
だから,テンプレートを共通化したい。
テンプレート側ではインスタンス変数 @col_sep
を参照することにしよう。
この変数はアクションメソッド側で与えるので
format.tsv do
@col_set = "\t"
end
のようにするわけだが,これだけだとテンプレートファイル index.csv.ruby
を見つけてくれない。
そこで,
format.tsv do
@col_set = "\t"
render "index.csv"
end
のように書いた。
これで,狙い通り CSV 用のテンプレートを使ってレンダリングしてくれる。
警告が出る
ただし,これだと
DEPRECATION WARNING: Rendering actions with '.' in the name is deprecated: items/index.csv
といった警告が出る。テンプレートを指定するのに .
を使っちゃいけないのだ。
ええっと,じゃあ望ましい書き方は?
試行錯誤
ここから迷走しだした。いろいろ試行錯誤してもうまくいかない。format
オプションか?とか思って
render "index", format: "tsv"
とかやってもテンプレートを見つけてくれない。
文字列じゃなくてシンボルで与えるのか,とか,オプションは format
じゃなくて formats
(s がつく)だったか,とかは分かったのだけれど。
解決
しばらく格闘してふと気付いた。
レンダリングしてほしいのは TSV だが,呼び出したいテンプレートは CSV 用(のファイル名)だ。だから formats
オプションには :tsv
ではなく :csv
を与えなければならなかったんである。そりゃそうだ!
これでようやく解決。
なお,いまの場合,アクションが index
で,テンプレート名と一致しているから,render
に "index"
を渡す必要はない。
つまり,最もシンプルには
render formats: :csv
と書けばよいのであった。