indexアクションをCSVやExcel(JRuby + POI)に対応してみる
scaffoldで作った場合、デフォルトではHTMLとXMLに対応していますが、これをCSVとかExcelに対応してみようというものです。
まず事前準備として、.csvや.xlsといった拡張子に反応するよう、config/environment.rbに登録します。
... # Activate observers that should always be running # config.active_record.observers = :cacher, :garbage_collector end Mime::Type.register "text/csv", :csv Mime::Type.register "application/vnd.ms-excel", :xls
次にscaffoldで作られたindexアクションをごりごりと変えていきます。
require 'java' require 'stringio' require 'lib/poi-3.1-FINAL-20080629.jar' class PeopleController < ApplicationController class RubyOutputStream < java.io.OutputStream def set_io(io) @io = io end def write(b) if b.is_a?(Fixnum) if b < 0 b = b + 256 end @io.print(b.chr) else (0...b.length).each do |index| write(b[index]) end end end def close @io.close end end # GET /people # GET /people.xml def index @people = Person.find(:all) respond_to do |format| format.html # index.html.erb format.xml { render :xml => @people } format.csv { result = "" @people.each do |person| result += "#{person.id},#{person.name}\n" end render :text => result } format.xls { io = StringIO.new rio = RubyOutputStream.new rio.set_io(io) wb = org.apache.poi.hssf.usermodel.HSSFWorkbook.new sheet = wb.createSheet("People") rowHeader = sheet.createRow(0) rowHeader.createCell(0).setCellValue("ID") rowHeader.createCell(1).setCellValue("NAME") i = 1 @people.each do |person| row = sheet.createRow(i) row.createCell(0).setCellValue(person.id) row.createCell(1).setCellValue(person.name) i += 1 end wb.write(rio) render :text => io.string io.close } end end ... 以下(showアクションなど)同じ
CSVの方は特別なことは何もなくて、idとnameをカンマでつないでrenderしているだけです。
Excelの方は、JRuby + Apache POIを使ってます。
昨日作ったRubyのIOをラップするRubyOutputStreamを使って、
ここではExcelブックの内容を一旦StringIOに蓄積します。
次にStringIOからstringを取り出してrenderしてあげます。
こうすることで、一旦ファイルに保存することなく、Excelをダウンロードさせることが可能になります。
JRuby on Railsって、CRubyがJRubyに変わっただけじゃんとか思ってましたが、
Javaのライブラリを活用できることで、Railsのパワーをさらに増すことが出来るので、
結構面白いな思いました。
ちなみにですが、ru_excelとかいうRubyのライブラリを使えば、
CRubyでもCOMに依存せずに(つまりWindowsじゃなくても)Excelを扱えるようです。