← Back to Upcase

Calculated Attributes in Models


(Crispin Cornett) #1

So assume you have the following relationships:

class Account
  has_many :invoices
end

class Invoice
  belongs_to :account
  has_many :line_items
end

def LineItem
  belongs_to :invoice
end

A line_item has a total_cost.

An invoice has a total_due attribute that is the sum of all the line_items total_cost’s.

An account has a balance that is the sum of all its invoices total_due’s.

Would it be better to run these calculations every time to make sure you’re taking everything into account or should you store the sum of the children to the parent?

For instance, on each invoice, should I calculate the sum of the charges everytime via a method like:

class Invoice
  ## ...
  def total_due
    line_items.sum(:total_cost)
  end
end

I like the idea of doing that, but then what would be the best way to calculate an account’s balance since you don’t have an database column storing the invoice total_due (since its a method now)? Something like:

class Account
  ## ...
  def balance
    invoices.map(&:total_due).sum
  end
end

Thanks guys…


(Sean Griffin) #2

Would it be better to run these calculations every time to make sure you’re taking everything into account or should you store the sum of the children to the parent?

Unfortunately there’s no hard and fast rule here. It depends on the application. I think a good rule of thumb is to start with it as a method, and move it to a column when it’s needed, either to be able to query against it, or for performance reasons.

I like the idea of doing that, but then what would be the best way to calculate an account’s balance since you don’t have an database column storing the invoice total_due (since its a method now)?

Like this:

class Account
  def balance
    invoices.map(&:total_due).reduce(0, &:+)
  end
end

(Crispin Cornett) #3

Cool, thanks for the info…

Also, it looks like Rails extends the Ruby core on Enumerable and adds the sum method which is a shortcut for inject. And aren’t inject and reduce the same thing?

From the Rails docs:

payments.sum(&:price) 
# same as
payments.inject(0) { |sum, p| sum + p.price } 

Or is there a reason I should prefer to use reduce(0, &:+) over sum?

Thanks again!


(Sean Griffin) #4

Nope, you’re right the sum method from enumerable is probably much better here.