acts_as_convertible_to_csv
update: convertible to csv now has a project page
A little ruby magic in the form of meta-programming allows you to define your models as being able to output data in csv format. The definition is a DSL style acts_as_convertible_to_csv with :header and :fields as valid specifications.
Here's how you would use it.
In your model declaration:
class Customer < ActiveRecord::Base acts_as_convertible_to_csv :header => true, :fields => %w(id firstname lastname email_address) end
The first row will contain fields names if you specify :header => true. You can specify which fields and the order in which they appear by using :fields. If you omit :fields, the field list will default to those returned by the human_readable method.
Getting the csv data from a collection of records:
Customer.find(:all).to_csv
You can pass a block:
Customer.find(:all).to_csv do |line| # write the line to a file or something end
You can also convert individual records to csv:
Customer.find(:first).to_csv
The code
class ActiveRecord::Base def self.acts_as_convertible_to_csv(*args) @@use_header_fields = false args.each do |param| if param.is_a? Hash if param.include? :header and param[:header] @@use_header_fields = true end if param.include? :fields and param[:fields] and param[:fields].is_a? Array class_eval <<-CEEND def self.csv_header %w(#{param[:fields].join(' ')}) end CEEND else class_eval <<-CEEND def self.csv_header field_names = Array.new self.content_columns.each do |column| field_names << column.name end field_names end CEEND end end end class_eval <<-CEEND def self.use_header_fields @@use_header_fields end def to_csv line = "" self.class.csv_header.each do |field_name| line = line + "," + self.send(field_name).to_s end # remove first ', ' line.gsub! /^,/, '' end CEEND end end # Allow a collection class to repond to to_csv class Array def to_csv(&block) data = Array.new # check to see if the objects in the array are capable of to_csv if self[0] and self[0].class.method_defined?(:to_csv) if self[0].class.use_header_fields data << self[0].class.csv_header.join(',') yield self[0].class.csv_header.join(',') if block_given? end each do |object| data << object.to_csv yield object.to_csv if block_given? end end data end end
