Created
January 14, 2010 09:59
-
-
Save tatey/277023 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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