Downloading Spreadsheet from Rails
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.