Skip to content

Instantly share code, notes, and snippets.

@tatey
Created January 14, 2010 09:59
Show Gist options
  • Save tatey/277023 to your computer and use it in GitHub Desktop.
Save tatey/277023 to your computer and use it in GitHub Desktop.
# Learning Ruby. An example of a bank account implemented in Ruby.
# We're doing everything the "long" way. Much of this code
# could be expressed more consicely.
require 'test/unit'
# Account represents a person's bank account. It has a number, name and
# balance. Money can be deposited and withdrawn from the account. You
# cannot withdraw more money than what's in the account.
class Account
# Tracks number to assign to account, ensuring each
# number is unique. Unlike instance variables, class
# variables are prexied with a double @ (@@). Class
# variables are shared by every instance in the class.
# Where as instance variables are unique to that instance.
#
# There's some quirky behaviour in Ruby 1.8.X with class variables
# and children classes. Don't worry about that for now.
@@number = 0
# Initializer invoked when object (instance) is instantiated
#
# Returns nothing
def initialize(name)
@@number = @@number + 1
@number = @@number
@name = name
@balance = 0
end
# Get number
#
# Returns Integer
def number
return @number
end
# Get name
#
# Returns String
def name
return @name
end
# Get balance
#
# Returns Integer or Float
def balance
return @balance
end
# Deposit amount by increasing the balance
#
# Returns nothing
def deposit(amount)
@balance = @balance + amount
end
# Withdraw amount by decreasing balance. Will
# not withrdaw when there is an insufficient balance
#
# Returns Boolean
def withdraw?(amount)
if amount <= @balance
@balance = @balance - amount
return true
else
return false
end
end
# Calculate compounded interest for an entire year
#
# Returns Integer or Float
def calculate_interest(rate)
monthly_balance = @balance
interest = 0
12.times do
monthly_interest = monthly_balance * rate
interest = interest + monthly_interest
monthly_balance = monthly_balance + monthly_interest
end
return interest
end
end
# TransactionAccount inherits from Account. Unlike Account, TransactionAccount
# charges a transaction fee on each widthdrawal. It's possible for the balance
# to become negative if the fee is greater than the balance.
#
# Inheritance is when a child class inherits the state and behaviour of its
# parent (or super class) class. You can think of children classes as
# specialisations (Especially when the parent is relatively generic).
# All the methods and instance variables (@example_instance_variable) defined
# in the parent are available to the child object.
#
# Remember, instance and object can be used interchangablely. When an object
# is being created with the Class.new notation, the object is said to be
# instantiated.
#
# While inheritance sounds like a great idea, it's not always as useful as
# it seems. Ruby has a special way of dealing with mixing behaviour from other class-like
# definitions, known as Modules. Don't worry about this for now.
class TransactionAccount < Account
# Fee charged on each withdrawal. Notice that this is all in upcase?
# When a label starts with a capital like TransactionAccount or TRANSACTION_FEE
# it is said to be a constant. It's a common convention in Ruby to define
# class constants in upcase. Unlike variables, constants should never be changed.
# Other languages enforce this, Ruby just throws you a warning. Don't worry too
# much about this for now.
TRANSACTION_FEE = 0.30
# Charges a transaction fee on each withdrawal
#
# Returns Boolean
def withdraw?(amount)
# When overloading a parent method, you need to explicitly invoke
# super() to get the parent's behaviour before extending it with our
# own speciality.
if super(amount)
@balance = @balance - TRANSACTION_FEE
return true
else
return false
end
end
end
# TestAccount tests the desired behaviour of an Account object.
class TestAccount < Test::Unit::TestCase
# Setup is invoked before each test case is run. It creates
# two accounts for testing. This ensures that each test
# starts with a fresh object.
def setup
@account1 = Account.new('Monica')
@account2 = Account.new('Tate')
end
def test_number_should_be_incremented_at_initialization
# This test will pass if account1 has a number less than
# account 2. The assert() method takes an argument, which
# when evaulated is expected to be true or false (Boolean).
# Test passes when the argument is evaulated to be true.
assert @account1.number < @account2.number
end
def test_name_should_be_defined_at_initialization
# This test will pass if the name for accont1 is "Monica"
# and the name for account2 is "Tate". The assert_equal(arg1, arg2)
# takes two arguments and compares them against each other. This is
# equivalent to the comparrison operate, which you know as ==.
# Test passes when the evaulated arguments are equal to each other.
assert_equal 'Monica', @account1.name
assert_equal 'Tate', @account2.name
end
def test_balance_should_be_zero_at_initialization
assert @account1.balance.zero?
end
def test_deposit_should_increase_balance
@account1.deposit(100)
assert_equal 100, @account1.balance
end
def test_withdraw_should_be_false_when_insufficient_balance
assert ! @account1.withdraw?(150)
end
def test_withdraw_should_be_true_when_sufficient_balance
@account1.deposit(100)
# We can do this because withdraw?() returns a Boolean
assert @account1.withdraw?(25)
assert_equal 75, @account1.balance
end
def test_calculate_interest
@account1.deposit(100)
# You can even invoke functions in arguments and have their
# returned value evaulated
assert_equal 79.5856326022129, @account1.calculate_interest(0.05)
end
end
# TestTransactionAccount tests the desired behaviour of an Account object.
class TestTransactionAccount < Test::Unit::TestCase
def setup
@account1 = TransactionAccount.new('Monica')
end
def test_withdraw_should_charge_transaction_fee
@account1.deposit(100)
@account1.withdraw?(100)
assert_equal -0.30, @account1.balance
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment