I have a rails app that needs to provide a spreadsheet of all the data in one of the tables. Specifically, we have a simple signup form for an event. I need to be able to let a subset of users download a spreadsheet of the current people signed up with the information given on the signup form. Here’s what I did.

First, use fasterCSV. Add to the Gemfile:

gem 'fastercsv', :require => 'faster_csv'

Basically, all that will happen is that there will be a link on the attendees index page that only logged in users will see (and be allowed to access). This is done in two steps. First the csv file is generated and then it is downloaded.

So, where shall I put the file? Since my development and production workstations have different operating systems, I’m going to have different paths on both of them. I created an yaml file to hold this information.

config/download_csv.yml

development:
    csvfile: /Users/me/Desktop/output.csv

production:
    csvfile: /local/code/web/app/myapp/shared/system/output.csv

In my attendee model, I created a method to generate the csv file.

def self.generate_csv
    csv_settings = YAML.load_file("#{Rails.root.to_s}/
config/download_csv.yml")[Rails.env]
    csvfile = csv_settings['csvfile']
    @attendees = Attendee.order('lastname ASC')
    FasterCSV.open("#{csvfile}",'w') do |csv|
      csv << ['Firstname','Lastname','Institution','Address',
'Email','Number attending']
      @attendees.each do |attendee|
        csv << [attendee.firstname, attendee.lastname, 
attendee.institution, attendee.address, attendee.email, 
attendee.fest_count]
      end
    end
  end

In my attendee controller, I created a method called download_csv. In this method, I actually call the generate_csv method above. (Side note: I originally had it set up so that the generate_csv method was called with each new create method. But then decided that fewer people would be downloading the csv file than would be registering. And I changed things so that the csv is only generated when someone wants to download it.)

def download_csv
    csv_settings = YAML.load_file("#{Rails.root.to_s}/
config/download_csv.yml")[Rails.env]
    csvfile = csv_settings['csvfile']
    Attendee.generate_csv
    send_file "#{csvfile}", :type => 'text/csv'
  end

Since I’m using authlogic and declarative_authorization in this app, I also needed to edit my filter_resource_access line in the method to:

filter_resource_access :additional_collection 
=> [[:download_csv, :edit], :edit]

(I.E. People who are allowed to edit attendees are also allowed to download the file.)

On my view I have the link:

<% if current_user %>
	<%= link_to 'Download Spreadsheet of Attendees', 
:controller => :attendees, :action => :download_csv %>
<% end %>

Lastly, add the route

resources :attendees do
    collection do
      get 'download_csv', :as => :download_csv
    end
  end

This all worked perfectly on my laptop while I tested it. But when I uploaded it to the server, it kept giving me an empty file, even though the file on the server was not empty. Apparently, this is a common problem. All I needed to do was edit my environments/production.rb file to match this:

# Specifies the header that your server uses for sending files
  #config.action_dispatch.x_sendfile_header = "X-Sendfile"

  # For nginx:
  config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

My problem now, as always, is I have no idea how to write a test for this.