CSV Download of any given model

I found sample code from the archives of this list to make CSV
download of a model. It works fine. I am wondering if it is possible
to generalize the code posted by François on his blog to handle any
given model.

class ReportController < ApplicationController
def report
@models = Model.find(:all, :conditions => [’…’])
report = StringIO.new
CSV::Writer.generate(report, ‘,’) do |csv|
csv << %w(Title Total)
@models.each do |model|
csv << [model.title, model.total]
end
end

report.rewind
send_data(report.read,
:type => ‘text/csv; charset=iso-8859-1; header=present’,
:filename => ‘report.csv’)
end
end

TIA.

I’m curious on this as well… my code is a little different then that
of the blog post referenced:

class SignaturesController < ApplicationController
def export
require ‘csv’
content_type = if request.user_agent =~ /windows/i
‘application/vnd.ms-excel’
else
‘text/csv’
end

CSV::Writer.generate(output = "") do |csv|
  csv << Signature.column_names
  Signature.find(:all).each do |signature|
    csv << [signature.id, signature.firstname, signature.lastname]
  end
end
send_data(output, :type => content_type, :filename =>

“#{controller_name}-export.csv”)
end
end

I’m already using Model.column_names to get the header of the CSV
file. Is there a way to loop through Model.content_columns and grab
the name variable to include within the block rather then specifying
the column individually?

Thoughts from anyone? Thanks…

Tim

I think you’ll find this simpler and more flexible. First, you’ll
need to download the FasterCSV gem (‘gem install fastercsv’). Then
you can use:

class FasterCSVExport
require ‘fastercsv’

def create_report( field_names = [], data_rows = [] )
FasterCSV.generate do |csv|
csv << field_names.map {|fn| fn.humanize.titleize }
data_rows.each {|row| csv << row }
end
end
end

I’ve given it to you as a stand-alone class but the create_report
method can also be added to each model directly or as part of a module
that gets mixed in (which is how I do it). Then in your controller
you can just have:

field_names = [‘id’, ‘firstname’, ‘lastname’]
table_rows = YourModel.find(:all).collect {|item| [item.id,
item.firstname, item.lastname]
export_data = FasterCSVExport.create_report(field_names, table_rows)
send_data(export_data, :type => “text/csv; charset=utf-8;
header=present”,
:disposition => ‘attachment’, :filename => “exported_data.csv”)

Even easier, you can pass your field_names to the “select” option when
performing the find to quickly have both the table header and the
table rows contain the same attributes. Something like:

field_names = [‘id’, ‘firstname’, ‘lastname’]
table_rows = YourModel.find(:all, :select => field_names)
export_data = FasterCSVExport.create_report(field_names, table_rows)
send_data(export_data, :type => “text/csv; charset=utf-8;
header=present”,
:disposition => ‘attachment’, :filename => “exported_data.csv”)

Or if you opt to put the ‘create_report’ method into your model
directly, you can push the find to the model too and let it handle
both steps at once.

export_data = YourModel.create_report([‘id’, ‘firstname’, ‘lastname’])
send_data(export_data, :type => “text/csv; charset=utf-8;
header=present”,
:disposition => ‘attachment’, :filename => “exported_data.csv”)

From there you can start to add bells and whistles. Tim, something
like:
field_names = YourModel.column_names if field_names.blank?
might be what you want to grab all the column names.

(I’m typing this from scratch without testing it so it’s quite
possible I’ve typo’d something…)

HTH,
Kevin S.

http://www.nullislove.com

Thank you Kevin - that makes sense, I’ll check it out when I get back
to the office tomorrow.

Regards,

Tim