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