Rails 3 Table with Fields Using Calculated Data

I have a table in my app, called entries. Entries belongs_to :users and has a field (decimal) called hours. There is another decimal field called cost. Cost is not entered by the user, but is calculated by multiplying the value in hours with the value in users named rate. To get this to work properly, I had to do the following.

First, in the entry form, make sure there is no text_field for cost. Cost is only found using a calculation.

In entries_controller.rb, edit create and update as follows:

  def create
    @entry = Entry.new(params[:entry])
    @entry.cost = @entry.user.rate * @entry.hours
    respond_to do |format|
      if @entry.save
        format.html { redirect_to(@entry, :notice => 'Entry was successfully created.') }
        format.html { render :action => "new" }

  def update
    @entry = Entry.find(params[:id])
    total_cost = @entry.user.rate * @entry.hours
    @entry.update_attribute(:cost, total_cost)
    respond_to do |format|

      if @entry.update_attributes(params[:entry])
        format.html { redirect_to(@entry, :notice => 'Entry was successfully updated.') }
        format.html { render :action => "edit" }

There should be a way to put a method in the model to calculate the total_cost, but I haven't successfully figured that out yet.

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.


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

    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}/
    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, 

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}/
    csvfile = csv_settings['csvfile']
    send_file "#{csvfile}", :type => 'text/csv'

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

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.