Export Rails model data as CSV

I needed to allow my customer to export a bunch of tables. After some digging, I found a pretty neat solution.

Let's say we have a Customer, Order and OrderItem model. We want to allow the user to download exported data as CSV in a single zip file. To generate CSV from the model we can use following code.

class Customer < ApplicationRecord

  def self.to_csv
    attributes = %w{id name email address}

    CSV.generate(headers: true) do |csv|
      csv << attributes

      all.each do |treatment|
        csv << attributes.map{ |attr| treatment.send(attr) }
      end
    end
  end
end

Then we need only to package up the files in zip file and send it to the user.

We will use a gem called rubyzip. To instal it add this to your Gemfile:

gem 'rubyzip', require: 'zip'

We can write text files directly to an archived file using puts, so all we need to do is to use Tempfile as our backing file.

module Admin
  class ExportsController < Admin::ApplicationController
    def download_export

      tables = [
        Customer,
        Order,
        OrderItem,
      ]

      filename = "shop-export-#{Date.today}.zip"
      temp_file = Tempfile.new(filename)

      begin
        ::Zip::OutputStream.open(temp_file) do |zos|
          tables.each do |table|
            zos.put_next_entry "#{table.name.downcase}.csv"
            zos.puts table.to_csv
          end
        end

        zip_data = File.read(temp_file.path)
        send_data(zip_data, type: 'application/zip', filename: filename)
      ensure
        temp_file.close
        temp_file.unlink
      end
    end
  end
end