Fixed a typo in code.
[fin.git] / account.rb
blob63a3425929cc20ffc7535c70ef902f0d61d4df1b
1 ################################################################################
2 # A financial account from which and to which transactions are made.
4 # _Account_ objects are both flyweights and a flyweight factory.  Since there
5 # is only one kind of flyweight, there is no need to increase complexity with
6 # an extra class.
7 ################################################################################
8 class Account
9   attr_reader :balance, :parent, :children
11   # Initializes a new _Account_ object given the account's full path in the
12   # accounts hierarchy, from the top-most parent to the account's name.
13   #
14   # +auto_repair_account = Account.new *['Expenses', 'Auto', 'Repair']
15   # groceries_account = Account.new(*'Expenses.Groceries'.split('.'))+
16   def initialize(name, parent)
17     @name = name
18     @parent = parent
19     @children = []
20     @balance = 0
21   end
23   # Returns the terminal name of this account.
24   #
25   # +account = Account.new(*'Income.Bursaries.Government'.split('.'))
26   # account.name  => 'Government'+
27   def name
28     @name
29   end
31   # The string representation of the account.
32   #
33   # +account = AccountRegister.new.open('Income', 'Bursaries', 'Uncle Joe')
34   # account.to_s  => 'Income.Bursaries.Uncle Joe'
35   def to_s
36     return @name if @parent.nil?
37     @parent.to_s + '.' + @name
38   end
40   def to_sym
41     to_s.to_sym
42   end
44   # Returns this account's depth in the accounts' hierarchy.  A depth of 0 means
45   # this account is a root account.
46   def depth
47     return 0 if @parent.nil?
48     1 + @parent.depth
49   end
51   # Invoke block on each of the account's parents.
52   #
53   # account = Account.new(*['Expenses', 'Groceries'])
54   # account.each_parent { |parent| puts parent }                => Expenses
55   # 
56   # account.each_parent { |parent| puts parent }  => Expenses
57   #                                                                Groceries
58   def each_parent(&block)
59     return if @parent.nil?
60     yield(parent)
61     @parent.each_parent(&block)
62   end
64   # Test for equal id of this _Transaction_ against another.
65   def ==(other)
66     to_s == other.to_s
67   end
69   # Check whether this object's class is the same as that of the given parameter
70   # and that this transaction's id is the same as that of the given parameter.
71   def eql?(other)
72     self.class.eql?(other.class) and to_s.eql?(other.to_s)
73   end
75   def hash
76     # the hash method is called by functions such as Array.uniq.  We consider
77     # a Transaction's identity by looking at its id.
78     to_s.hash
79   end
81   # Comparison operator
82   def <=>(other)
83     to_s <=> other.to_s
84   end
86   # Set a new value for this account's balance, and update this accont's parents'
87   # balance as well.
88   def balance=(new_balance)
89     difference = new_balance - @balance
90     @balance += difference
91     @parent.balance += difference unless @parent.nil?
92   end
94   # Get the path to the account, from the root parent to the account's literal
95   # name.
96   #
97   # The path is an array.  For example,
98   #
99   # +account = AccountRegister.open(*%w[Income Salary])
100   # account.path => ["Income", "Salary"]+
101   def path
102     to_s.split('.')
103   end