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 Skoglund

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