Reputation: 27
Is it possible to see an updated balance when calling my print_statement
? In my Transaction
class, I am using an @amount
and @timestamp
instance variable to store a transaction into my @transaction
instance variable in my Account
class. I am then calling my print_statement
method which only prints out the @timestamp
and @amount
for each transaction.
In my desired outcome, I need to also print out an updated Balance
with each transaction. I am new to coding, so any advice would be much appreciated!
My desired outcome:
date || credit || debit || balance
14/01/2012 || || 500.00 || 2500.00
13/01/2012 || 2000.00 || || 3000.00
10/01/2012 || 1000.00 || || 1000.00
My current outcome:
2.7.0 :001 > require './lib/account'
=> true
2.7.0 :002 > require './lib/transaction'
=> false
2.7.0 :003 > account = Account.new
2.7.0 :004 > account.deposit(100)
=> [#<Transaction:0x00007f9eff8941a8 @amount=100, @timestamp="09/01/2021">]
2.7.0 :005 > account.withdraw(50)
=> [#<Transaction:0x00007f9eff8941a8 @amount=100, @timestamp="09/01/2021">, #<Transaction:0x00007f9efd9673c0 @amount=-50, @timestamp="09/01/2021">]
2.7.0 :006 > account.balance
=> 50
2.7.0 :007 > account.print_statement
date || credit || debit || balance ||
-------------------------------------------
09/01/2021||100|| || Balance ||
09/01/2021|| ||-50|| Balance ||
=> [nil, nil]
Account class
require_relative 'transaction'
class Account
attr_reader :transactions
def initialize
@transactions = []
end
def balance
@transactions.map(&:amount).sum
end
def deposit(amount)
@transactions << Transaction.new(amount)
end
def withdraw(amount)
@transactions << Transaction.new(-amount)
end
def header
puts ' date || credit || debit || balance || '
puts '-------------------------------------------'
end
def print_statement
header
@transactions.map(&:formate)
end
end
Transaction class
class Transaction
attr_reader :amount, :timestamp
def initialize(amount)
@amount = amount
@timestamp = Time.now.strftime("%d/%m/%Y")
end
def formate
if @amount > 0
puts "#{@timestamp}||#{@amount}|| || Balance ||"
elsif
puts "#{@timestamp}|| ||#{@amount}|| Balance ||"
end
end
end
Upvotes: 2
Views: 93
Reputation: 16728
I think you should have a reference to an account associated with each transaction. You can then use that reference to access the account info you want. Since it also seems like you would just have a list of transactions and not store a fixed balance, I think you will need a method to get the balance after any transaction.
First I added a balance_after
method to Account to calculate the balance after any given transaction. It does this by looking up the index of the passed in transaction and then summing up the amounts of all transactions prior to the one passed in.
require_relative 'transaction'
class Account
attr_reader :transactions
def initialize
@transactions = []
end
def balance
@transactions.map(&:amount).sum
end
# new method to get the balance after a given transaction
def balance_after(transaction)
index = transactions.find_index(transaction)
transactions[0..index].map(&:amount).sum
end
def deposit(amount)
@transactions << Transaction.new(amount, self)
end
def withdraw(amount)
@transactions << Transaction.new(-amount, self)
end
def header
puts ' date || credit || debit || balance || '
puts '-------------------------------------------'
end
def print_statement
header
@transactions.map(&:formate)
end
end
Next I added the account info into the Transaction and a method that calls balance_after()
method on the account reference, passing a reference to current transaction.
class Transaction
attr_reader :amount, :timestamp, :account
def initialize(amount, account)
@amount = amount
@timestamp = Time.now.strftime("%d/%m/%Y")
@account = account
end
def balance()
@account.balance_after(self)
end
def formate
if @amount > 0
puts "#{@timestamp}||#{@amount}|| || #{balance} ||"
elsif
puts "#{@timestamp}|| ||#{@amount}|| #{balance} ||"
end
end
end
Upvotes: 1
Reputation: 857
There could be more than one solution.
I'll give you a simple example. But first let me congrats with you, I like the way you coded so far, very clean and focused on the public methods for each class, a clear and meaningful API.
What you are missing is what data structure you need to rapresent the problem.
In my solution I will use a Struct
, you may use a Hash
, or may be you could build some method in Transaction
where you keep track of the balance.
But I think that a partial balance is a matter of the Account
class, so here it is a suggestion:
class Account
attr_reader :transactions
TransactionPartialBalance = Struct.new(:transaction, :balance_at_transaction)
def initialize
@transactions = []
@transactions_with_balance = []
end
def balance
@transactions.map(&:amount).sum
end
def deposit(amount)
@transactions << Transaction.new(amount)
@transactions_with_balance << TransactionPartialBalance.new(@transactions.last, balance)
end
def withdraw(amount)
@transactions << Transaction.new(-amount)
@transactions_with_balance << TransactionPartialBalance.new(@transactions.last, balance)
end
def header
puts ' date || credit || debit || balance || '
puts '-------------------------------------------'
end
def print_transactions_balance
p @transactions_with_balance
end
def print_statement
header
@transactions.map(&:formate)
end
end
class Transaction
attr_reader :amount, :timestamp
def initialize(amount)
@amount = amount
@timestamp = Time.now.strftime("%d/%m/%Y")
end
def formate
if @amount > 0
puts "#{@timestamp}||#{@amount}|| || Balance ||"
elsif
puts "#{@timestamp}|| ||#{@amount}|| Balance ||"
end
end
end
if __FILE__ == $PROGRAM_NAME
account = Account.new
account.deposit(100)
account.withdraw(50)
account.balance
account.print_transactions_balance
# [#<struct Account::TransactionPartialBalance transaction=#<Transaction:0x0000562a986ecc38 @amount=100, @timestamp="10/01/2021">, balance_at_transaction=100>,
# #<struct Account::TransactionPartialBalance transaction=#<Transaction:0x0000562a986ecad0 @amount=-50, @timestamp="10/01/2021">, balance_at_transaction=50>]
end
Upvotes: 1
Reputation: 2117
Definitely, there are more that one way to call a method of a particular class within another class, the simple and shortest way (since you mentioned you are new coding I will only show you 2 ways to avoid confusions):
Let's say you are within your Account
class, within a method you can create an instance of your Transaction
object and call any function (in a public scope for simplicity of this example):
t = Transaction.new(...)
t.formate
This is similar to what you are doing when you write:
@transactions.map(&:formate)
Basically, that line above said for each instance of transaction in that list call the method format
The other easy way to achieve this is to define the method as a class method (static
function in other languages like C#
for example) something like:
class A
def self.print_foo
p 'Foo'
end
end
With the above definition, you can do something like A.print_foo
.
There are other ways to achieve this but I think these 2 are the basics.
NOTE: IF YOU ARE IN DIFFERENT FILES AND USING PLANE RUBY (NO AUTOMATIC FILE LOADER, YOU MAY NEED TO IMPORT/REQUIRE THE FILES, BUT THAT ALREADY HAS AN ANSWER HERE IN THE SITE 👌)
Hope the above helps to clarify! 👍
Upvotes: 1