← Back to Upcase

Calculated Attributes in Models

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…

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

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!

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

Pretty awesome. If you are using rails as a web api and you render to json, I learned today, that you must also add the override the as_json method.