ハローこんにちは、新人エンジニアのハトネコエです。
2016年10月22日発売の 『WEB+DB PRESS Vol.95』 に
UUUM のエンジニア達が書いた特集『試して学ぶHTTP』が載ります。よろしくお願いします。

- 作者: 小出淳子,黒澤剛志,牧大輔,横江亮佑,山口貴也,尾藤正人,佐藤琢哉,中橋研太郎,田中慎司,小西裕介,伊藤直也,稲富駿,前島真一,長野雅広,山際康貴,のざきひろふみ,うらがみ,岡林大,遠藤雅伸,ひげぽん,海野弘成,はまちや2,竹原,大場寧子,大場光一郎,野々下裕子,WEB+DB PRESS編集部
- 出版社/メーカー: 技術評論社
- 発売日: 2016/10/22
- メディア: 大型本
- この商品を含むブログを見る
Rubyテンプレート
Rails4になってcsvが出力しやすくなった - Qiita
ここの記事にあるように、view テンプレートとして .ruby
ファイルが使えるようになっています。
長くなりがちな CSV 出力の処理を controller 内でなく別ファイルに書けるので controller がすっきりしますね。
Excel 出力もこんなふうにしたい
Excel で表データを扱うことを前提にすると、CSV では、文字コードとか先頭に0を詰める出力がつらい。
CSV でなく Excel 用の .xlsx
形式で書き出したいよねーというわけで、
お世話になっているのが Axlsx という gem。
これで上記のような方法で controller と出力処理を分離しつつエクスポートする方法を探りました。
Before
もとは controller 内にこんなコードを書いていました。
require "axlsx" class UsersController < ApplicationController def export Axlsx::Package.new do |p| filename = "users_list_" + Time.zone.now.to_date.to_s styles = load_styles(p.workbook) p.workbook.add_worksheet(name: filename) do |sheet| sheet.add_row %w(ID メールアドレス ユーザー名), style: styles[:column_title] User.find_each do |user| sheet.add_row [ user.id, user.email, user.name, ], style: [ styles[:fill_zero], styles[:default], styles[:default], ] end end send_data( p.to_stream.read, type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename: filename + ".xlsx" ) end end private # セルの書式設定を読み込む def load_styles(workbook) default_font_family = "MS Pゴシック" styles = {} styles[:default] = workbook.styles.add_style(font_name: default_font_family) styles[:column_title] = workbook.styles.add_style(font_name: default_font_family, b: true, alignment: { horizontal: :center }, bg_color: "f9ef93") styles[:fill_zero] = workbook.styles.add_style(font_name: default_font_family, format_code: "0000000000") styles end end
そして view に
<%= link_to "ユーザーリスト出力", export_users_path %>
とリンクがある形。
After
このように変えました。
(コードの動きがわかりやすいよう、実際に実装した順番とはやや異なります)
1. MIME-TYPE を記述
config/initializers/mime_types.rb
に xlsx 形式の MIME-TYPE を記述します。
なお、initializers の設定ですので、rails server
を実行中の場合は記述後に再起動してください。
Mime::Type.unregister :xlsx Mime::Type.register "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", :xlsx
2. 書式設定の読み込みは Service へ
セルの書式設定は使いまわしますので、
書式設定を読み込むメソッドは、AxlsxSupportService を作り、収めました。
class AxlsxSupportService # セルのカスタム書式設定を読み込む def load_styles(workbook) default_font_family = "MS Pゴシック" styles = {} styles[:default] = workbook.styles.add_style(font_name: default_font_family) styles[:column_title] = workbook.styles.add_style(font_name: default_font_family, b: true, alignment: { horizontal: :center }, bg_color: "f9ef93") styles[:fill_zero] = workbook.styles.add_style(font_name: default_font_family, format_code: "0000000000") styles end end
3. Rubyテンプレートの作成
views/user
ディレクトリ内に index.xlsx.ruby
を作成しました。
呼び出されると、Axlsx::Package クラスである変数 package
を返します。
require "axlsx" Axlsx::Package.new do |package| service = AxlsxSupportService.new styles = service.load_styles(package.workbook) sheet_name = "users_" + Time.zone.now.to_date.to_s package.workbook.add_worksheet(name: sheet_name) do |sheet| sheet.add_row %w(ID メールアドレス ユーザー名), style: styles[:column_title] User.find_each do |user| sheet.add_row [ user.id, user.email, user.name, ], style: [ styles[:fill_zero], styles[:default], styles[:default], ] end end end
4. controller から Rubyテンプレートを呼び出す
format ごとに呼び出す機能を変えています。
class UsersController < ApplicationController def index respond_to do |format| # index ページを表示するため html フォーマットについて記述 format.html do @users = User.all end # index.xlsx.ruby がレンダーされる format.xlsx do send_data( render_to_string.to_stream.read, type: :xlsx, filename: "users_list_" + Time.zone.now.to_date.to_s + ".xlsx" ) end end end end
render_to_string
には、先ほど作成した index.xlsx.ruby
をレンダリングした結果の文字列が入れられます。
今回の場合は index.xlsx.ruby
によって返ってくるのは Axlsx::Package クラスですから、
Axlsx::Package クラスの to_stream
メソッドで StringIO
クラスに変換したのち読み出し、send_data します。
URLとしては、example.com/users?format=xlsx
と打ち込めば、xlsx エクスポート機能が働きます。
5. リンクボタンの設置
view にはこのようなリンクボタンを設置します。
format の指定を入れる点に注意しましょう。
<%= link_to "ユーザーリスト出力", users_path(format: :xlsx) %>
おわりに
急ぎ足でしたが、同じことをやっている人がいなく実装に苦労しましたので、
実装例としてまとめさせていただきました。
情報がなさすぎて、 Axlsx-Rails を使わなければ実装できないのでは……
と挫けそうになったのですが、必要以上の gem 依存におちいることなく実装できて良かったです。
同じことを今後される方のご参考になりますと幸いです。
UUUM では Ruby on Rails が得意な方などなど、挑戦が大好きなエンジニアを募集中です。
お気軽にお問い合わせください。