この記事はもともと『UUUM攻殻機動隊(エンジニアブログ)』に掲載されていたものでしたが、
2024年頃にUUUMエンジニアブログが閉鎖されてしまったようですので、
Wayback Machine のデータをもとに、2025年1月にこちらのブログに移植いたしました。
ハローこんにちは、新人エンジニアのハトネコエです。
2016年10月22日発売の 『WEB+DB PRESS Vol.95』 に
UUUM のエンジニア達が書いた特集『試して学ぶHTTP』が載ります。よろしくお願いします。
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 = "Textar" 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サーバーを実行中の場合は、こちらを記述後に再起動してください。
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 = "Textar" 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 のように
?format=xlsx というパラメーターを指定すれば、xlsx エクスポート機能が働きます。
5. リンクボタンの設置
view には以下のようなリンクボタンを設置します。
format: :xlsx の指定を入れる点に注意しましょう。
<%= link_to "ユーザーリスト出力", users_path(format: :xlsx) %>
おわりに
急ぎ足でしたが、同じことをやっている人がいなく実装に苦労しましたので、
実装例としてまとめさせていただきました。
情報がなさすぎて、 Axlsx-Rails を使わなければ実装できないのでは……
と挫けそうになったのですが、必要以上の gem 依存におちいることなく実装できて良かったです。
【追記】 Axlsx-Rails は2019年に straydogstudio/axlsx_rails から caxlsx/caxlsx_rails へと、リポジトリ名と管理組織が変更されました
同じことを今後される方のご参考になりますと幸いです。