Rails 3.x export to CSV and TSV

published Wednesday, January 25, 2012

Register the TSV Mime Type

in `config/initializers/mime_types.rb` register the TSV mime type

Mime::Type.register "text/tab-separated-values", :tsv

Add to_csv and to_tsv to Arrays so as CSV and TSV renderers

In `lib/csv_and_tsv_renderers.rb`

# encoding: utf-8
#
require 'csv' # adds a .to_csv method to Array instances
class Array
  BOM = "\xEF\xBB\xBF" #Byte Order Mark UTF-8
  alias old_to_csv to_csv #keep reference to original to_csv method
  def to_csv(options = Hash.new)
    options = options.merge(:force_quotes => true)
    # override only if first element actually has as_csv method
    return old_to_csv(options) unless self.first.respond_to? :as_csv
    # use keys from first row as header columns
    out = first.as_csv.keys.collect{|k| k.to_s.titleize }.to_csv(options)
    # collect all entries as CSV, ensure that no line break is present within values
    self.each { |r| out << r.as_csv.values.to_csv(options).gsub(/\r?\n/, ' ').strip! + "\n" }
    # Force encoding to UTF-16LE and add Byte Order Mark for Excel compatibility
    (BOM + out).force_encoding("UTF-8")
  end
  def to_tsv(options = Hash.new)
    to_csv(options.merge(:col_sep => "\t"))
  end
end
ActionController::Renderers.add :csv do |csv, options|
  csv = csv.respond_to?(:to_csv) ? csv.to_csv() : csv
  self.content_type ||= Mime::CSV
  self.response_body = csv
end
ActionController::Renderers.add :tsv do |tsv, options|
  tsv = tsv.respond_to?(:to_tsv) ? tsv.to_tsv() : tsv
  self.content_type ||= Mime::TSV
  self.response_body = tsv
end

Load the lib

In `config/initializers/load_libs.rb`

require 'csv_and_tsv_renderers'

In your models

Cherry pick the attributes you like to be exported

def as_csv(options = {})
  csv = {
    attribute_1: attribute_1,
    ...
  }
end

In your controllers

respond_to do |format|
  ...
  format.csv  { send_data @objects.to_csv(), :filename => "nice_filename.csv" }
  format.tsv  { send_data @objects.to_tsv(), :filename => "nice_filename.tsv" }
end

References

http://blog.plataformatec.com.br/2009/09/exporting-data-to-csv-and-excel-in-your-rails-app/

https://github.com/ilmotta/to-csv/issues/3

http://blog.grayproductions.net/articles/ruby_19s_string

http://stackoverflow.com/questions/539294/how-do-i-determine-file-encoding-in-osx

Update 2012-01-28

Use UTF-8 everywhere instead of the initial UTF-8 BOM then forcing to UTF-16LE. Seems to work fine under Windows' Office 2010.

If you could confirm that it works properly under an other version, opens fine and shows diacritics characters, please leave a comment bellow.