Slurping up and Spitting out CSV Files in Ruby with FasterCSV and Ruport 6

Posted by unixmonkey on May 01, 2008

I’ve got some data in an excel file that I need to put in the database and its far too much to do by hand, what will I do?

Lets throw some ruby at the problem!

First, excel it too darn complicated and proprietary a format to even mess with unless you are creating something really worth it, so lets open that .xls with Excel or OpenOffice and do a File -> Save As -> .csv (comma separated values) to get a file that is easier to work with.

Now, we could write our own CSV parser since its such a simple format, but why futz with it when someone else has already put out a good library for that will likely be more error tolerant? Lets use FasterCSV, as its pretty well-known.

Install by issuing:

sudo gem install fastercsv

Now you can just fire up script/console of your Rails app and type in the below, or just put this in a database migration to slurp up all that good spreadsheet data.

The below assumes you have a ‘users’ table with fields name, address, and email that are also rows in your excel file. Adjust as necessary.

require 'fastercsv'
FasterCSV.foreach("#{RAILS_ROOT}/myfile.csv") do |row|
  record = User.new(
    :name    => row[0], # first column of csv file
    :address => row[1], # second column
    :email   => row[2]  # third
  )
  record.save
end

That’s pretty awesome; now how can I export that stuff in the database back out to Excel again?

Lets use Ruport, the Ruby report gem!

sudo gem install acts_as_reportable

Toss the require statement somewhere obvious (like environment.rb or above the model you want to export), and put ‘acts_as_reportable’ in your model declaration.

require 'ruport'
class User < ActiveRecord::Base
  acts_as_reportable
end

Now I can do this kind of stuff to export to a csv file (again with script/console, but a migration should work equally well):

content = User.report_table.as(:csv) # convert your model table to CSV

or

content = User.report_table_by_sql("SELECT name, address, email FROM users").as(:csv)

Then write that to a file like so:

file = File.open("#{RAILS_ROOT}/report.csv", "w") # open file
file.print(content) # print that csv content to the open file
file.close          # close the file

Open that CSV file with Excel and amaze the sales team, your boss, or whoever.

This is a narrow view of what we can do with FasterCSV and Ruport, but I’m sure you can see how you could build out a format.csv in a respond_to block in a Rails controller, or have a setter in your model that sucks in an uploaded CSV to create some records.

These are some pretty great libraries, and I’m very glad they were able to help me load, combine, query and output some data I’d been working with in a pinch.

I hope this post serves to help someone else in a similar situation.

Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. David Baldwin Thu, 17 Jul 2008 17:59:12 EDT

    Great article Dave. I just recently started looking into Ruport and am very impressed with it’s power and flexibility.

  2. Gregory Brown Thu, 31 Jul 2008 15:31:58 EDT

    Hi, thanks for writing this up about Ruport

    If you put the names of the headers in the first line of your csv file, like:

    name,address,email

    You can just just Ruport (which uses FasterCSV under the hood):

    Table("my.csv", :records =&gt; true) { |t,r| User.create(r.to_hash) }

    Of course, using FasterCSV is fine too, and probably a little more efficient.

    With acts_as_reportable, you can use any of your find options, so you can write:

    content = User.report_table_by_sql("SELECT name, address, email FROM users").as(:csv)

    as:

    content = User.report_table(:all, :only =&gt; [:name, :address, :email]).as(:csv)

    Also, you can get rid of the file code:

    table = User.report_table(:all, :only =&gt; [:name, :address, :email])
    table.save_as("#{RAILS_ROOT}/report.csv")

    Those are just a few tips, nice article :)

    -greg

  3. Gregory Brown Thu, 31 Jul 2008 15:33:09 EDT

    Oh, also… by installing ruport-util, Ruport can read/write xls, though I agree, CSV is usually better!

  4. Unixmonkey Fri, 01 Aug 2008 13:25:48 EDT

    Thanks Greg, I wasn’t aware Ruport did xls files directly… I’ll definitely have to look at that for exporting for customers, but I do like CSV for internal import/export.

    The Table() method could have saved me a ton of work a few days ago If I’d only known about it. Definitely better than shifting ordinally.

  5. Joel Chippindale Tue, 05 Aug 2008 00:44:06 EDT

    Thanks Dave.

    If you want to customise exports beyond just selecting which columns are output then CSV Builder plugin allows you build templates with fastercsv.

  6. Nikhil Sat, 12 Jun 2010 02:41:12 EDT

    Very enlightening post !!
    Thanks :)

Comments