0

For the following RubyOnRails code, is there a way to move the "profit" calculation out of the View and into the Model.. so there's maybe an attribute called total_income and total_expense?

Model - transaction.rb

class Transaction < ActiveRecord::Base
  attr_accessible :name, :amount, :category
  scope :incomes,  :conditions => { :category => 'Income'  }
  scope :expenses, :conditions => { :category => 'Expense' }
end

Controller - transactions_controller.rb

class TransactionsController < ApplicationController

  def index
    @incomes  = Transaction.incomes
    @expenses = Transaction.expenses
    @transaction = Transaction.new
  end

View - index.html.erb

<pre>
<strong>Income</strong>
  <% @incomes.each do |income| %>
  <%= income.name %>  -  <%= number_to_currency((income.amount.nil? ? 0 : income.amount)) %>
  <% end %>
  <strong>Subtotal:</strong> <%= number_to_currency(@income_total = @incomes.sum(:amount)) %>

<strong>Expenses</strong>
  <% @expenses.each do |expense| %>
  <%= expense.name %>  -  <%= number_to_currency((expense.amount.nil? ? 0 : expense.amount)) %>
  <% end %>
  <strong>Subtotal:</strong> <%= number_to_currency(@expenses_total = @expenses.sum(:amount)) %>

<strong>Profit: <%= number_to_currency(@income_total - @expenses_total) %></strong>
</pre>
2
  • 1
    unrelated to your problem: watch out, Transaction is a reserved word in many programming environments and could lead to problems. I make it a habit to always tweak the name a bit to avoid problems. Commented Feb 25, 2012 at 1:35
  • 1
    @DGM thanks for pointing this out. Some RoR reserved words (which I've found does include 'transaction') are listed on the RoR old wiki. Commented Feb 25, 2012 at 6:14

2 Answers 2

2

For the most basic change you could just add class methods to Transaction

class Transaction < ActiveRecord::Base
  attr_accessible :name, :amount, :category
  scope :incomes,  :conditions => { :category => 'Income'  }
  scope :expenses, :conditions => { :category => 'Expense' }

  def self.income_total
    incomes.sum :amount
  end

  def self.expenses_total
    expenses.sum :amount
  end

end

class TransactionsController < ApplicationController

  def index
    @incomes  = Transaction.incomes
    @expenses = Transaction.expenses
    @income_total = Transaction.income_total
    @expenses_total = Transaction.expenses_total
    @transaction = Transaction.new
  end
Sign up to request clarification or add additional context in comments.

1 Comment

Just the type of thing I was looking for. Thanks.
1

These days I prefer to wrap these multiple, interdependent instance variables up into a new presenter class, something like this (untested):

class TransactionPresenter

  attr_reader :income_total, :expenses_total

  def initialize
    @incomes = Transaction.incomes
    @expenses = Transaction.expenses
  end

  def each_income(&block)
    @incomes.each(&block)
  end

  def each_expense(&block)
    @incomes.each(&block)
  end

  def income_total
    @income_total ||= number_to_currency(@incomes.sum(&:amount))
  end

  def expenses_total
    @expenses_total ||= number_to_currency(@expenses.sum(&:amount))
  end

  def name_and_amount(income_or_expense)
    "#{income_or_expense.name} - #{number_to_currency((income.amount.nil? ? 0 : income.amount))}"
  end

  def profit
    number_to_currency(income_total - expenses_total)
  end

end

# controller

def index
  @presenter = TransactionPresenter.new
end

# view

<pre>
<strong>Income</strong>
  <% @presenter.each_income do |income| %>
    <%= @presenter.name_and_amount %>
  <% end %>
  <strong>Subtotal:</strong> <%= @presenter.income_total %>

<strong>Expenses</strong>
  <% @presenter.each_expense do |expense| %>
    <%= @presenter.name_and_amount %>
  <% end %>
  <strong>Subtotal:</strong> <%= @presenter.expenses_total %>

<strong>Profit: <%= @presenter.profit %></strong>
</pre>

1 Comment

I'll have to read up on the use of a presenter class. I've never seen this type of thing before.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.