ShadowBelmolve-money 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt ADDED
File without changes
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2005 Tobias Lutke
2
+ Copyright (c) 2008 Phusion
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,25 @@
1
+ History.txt
2
+ MIT-LICENSE
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/money.rb
7
+ lib/money/acts_as_money.rb
8
+ lib/money/core_extensions.rb
9
+ lib/money/errors.rb
10
+ lib/money/money.rb
11
+ lib/money/variable_exchange_bank.rb
12
+ money.gemspec
13
+ rails/init.rb
14
+ script/console
15
+ script/destroy
16
+ script/generate
17
+ spec/db/database.yml
18
+ spec/db/schema.rb
19
+ spec/money/acts_as_money_spec.rb
20
+ spec/money/core_extensions_spec.rb
21
+ spec/money/exchange_bank_spec.rb
22
+ spec/money/money_spec.rb
23
+ spec/spec.opts
24
+ spec/spec_helper.rb
25
+ tmp/acts_as_money.sqlite3
data/README.rdoc ADDED
@@ -0,0 +1,122 @@
1
+ = Money $
2
+
3
+ This library aids one in handling money and different currencies. Features:
4
+
5
+ - Provides a Money class which encapsulates all information about an certain
6
+ amount of money, such as its value and its currency.
7
+ - Represents monetary values as integers, in cents. This avoids floating point
8
+ rounding errors.
9
+ - Provides APIs for exchanging money from one currency to another.
10
+ - Has the ability to parse a money string into a Money object.
11
+ - Provides ActiveRecord "has_money" method.
12
+
13
+ Resources:
14
+
15
+ - This fork: http://github.com/nofxx/money
16
+ - Website: http://money.rubyforge.org
17
+ - RDoc API: http://money.rubyforge.org
18
+ - Git repository: http://github.com/FooBarWidget/money/tree/master
19
+
20
+
21
+ == Download
22
+
23
+ Install stable releases with the following command:
24
+
25
+ gem install money
26
+
27
+ The development version (hosted on Github) can be installed with:
28
+
29
+ gem sources -a http://gems.github.com
30
+ gem install ShadowBelmolve-money
31
+
32
+
33
+ == Usage
34
+
35
+ === Synopsis
36
+
37
+ require 'money'
38
+
39
+ # 10.00 USD
40
+ money = Money.new(1000, "USD")
41
+ money.cents # => 1000
42
+ money.currency # => "USD"
43
+ money.format # => "$10.00"
44
+
45
+ Money.new(1000, "USD") == Money.new(1000, "USD") # => true
46
+ Money.new(1000, "USD") == Money.new(100, "USD") # => false
47
+ Money.new(1000, "USD") == Money.new(1000, "EUR") # => false
48
+
49
+
50
+ === Currency Exchange
51
+
52
+ Exchanging money is performed through an exchange bank object. The default
53
+ exchange bank object requires one to manually specify the exchange rate. Here's
54
+ an example of how it works:
55
+
56
+ Money.add_rate("USD", "CAD", 1.24515)
57
+ Money.add_rate("CAD", "USD", 0.803115)
58
+
59
+ Money.us_dollar(100).exchange_to("CAD") # => Money.new(124, "CAD")
60
+ Money.ca_dollar(100).exchange_to("USD") # => Money.new(80, "USD")
61
+
62
+ or
63
+
64
+ Money.us_dollar(100).to_cad # => Money.new(124, "CAD")
65
+ Money.ca_dollar(100).to_usd # => Money.new(80, "USD")
66
+
67
+ Comparison and arithmetic operations work as expected:
68
+
69
+ Money.new(1000, "USD") <=> Money.new(900, "USD") # => 1; 9.00 USD is smaller
70
+ Money.new(1000, "EUR") + Money.new(10, "EUR") == Money.new(1010, "EUR")
71
+
72
+ Money.add_rate("USD", "EUR", 0.5)
73
+ Money.new(1000, "EUR") + Money.new(1000, "USD") == Money.new(1500, "EUR")
74
+
75
+ There is nothing stopping you from creating bank objects which scrapes
76
+ www.xe.com for the current rates or just returns <tt>rand(2)</tt>:
77
+
78
+ Money.default_bank = ExchangeBankWhichScrapesXeDotCom.new
79
+
80
+
81
+ === Ruby on Rails
82
+
83
+ Use the +has_many+ helper to embed the money object in your models.
84
+ The following example requires a +price_in_cents+ and a +price_currency+
85
+ fields.
86
+
87
+ config/enviroment.rb
88
+
89
+ require.gem 'ShadowBelmolve-money', :lib => 'money'
90
+
91
+ app/models/product.rb
92
+
93
+ class Product < ActiveRecord::Base
94
+ belongs_to :product
95
+ has_money :price
96
+
97
+ validates_numericality_of :price_cents, :greater_than => 0
98
+ end
99
+
100
+ migration:
101
+
102
+ create_table :products do |t|
103
+ t.integer :price_cents
104
+ t.string :price_currency
105
+ end
106
+
107
+
108
+ === Default Currency
109
+
110
+ By default Money defaults to USD as its currency. This can be overwritten using
111
+
112
+ Money.default_currency = "CAD"
113
+
114
+ If you use Rails, then environment.rb is a very good place to put this.
115
+
116
+
117
+ == TODO
118
+
119
+ * Better validation (fix composed_of allow_nil)
120
+ * Interest (almost there..)
121
+ * Remote rate fetching
122
+
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/money'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('money', Money::VERSION) do |p|
7
+ p.developer('Money Team', 'see@readme')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ p.rubyforge_name = p.name
10
+ p.summary = "This library aids one in handling money and different currencies."
11
+ p.description = "This library aids one in handling money and different currencies."
12
+ p.url = "http://github.com/nofxx/money"
13
+
14
+
15
+ p.extra_dev_deps = [
16
+ ['newgem', ">= #{::Newgem::VERSION}"]
17
+ ]
18
+
19
+ p.clean_globs |= %w[**/.DS_Store *.log]
20
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
21
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
22
+ p.rsync_args = '-av --delete --ignore-errors'
23
+ end
24
+
25
+ require 'newgem/tasks' # load /tasks/*.rake
26
+ Dir['tasks/**/*.rake'].each { |t| load t }
27
+
@@ -0,0 +1,41 @@
1
+ # Money require 'money'
2
+ # based on github.com/collectiveidea/acts_as_money
3
+ module ActsAsMoney #:nodoc:
4
+ def self.included(base) #:nodoc:
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ #
10
+ # class Product
11
+ # has_money :value, :tax, :opts => opts
12
+ # end
13
+ #
14
+ # @product.value.class #=> "Money"
15
+ # @product.value_cents #=> "1000"
16
+ # @product.tax_currency #=> "USD"
17
+ #
18
+ # Opts:
19
+ # :cents => "pennys" #=> @product.pennys
20
+ # :currency => "currency" #=> @product.currency
21
+ # :allow_nil => true
22
+ # :with_currency => false
23
+ # :with_cents => true #=> 1000.to_money #=> #<Money @cents=1000>
24
+ #
25
+ def has_money(*attributes)
26
+ config = {:with_currency => true, :with_cents => false,
27
+ :allow_nil => true }.update(attributes.extract_options!)
28
+
29
+ for attribute in attributes do
30
+ mapping = [[config[:cents] || "#{attribute}_cents", 'cents']]
31
+ mapping << [config[:currency] || "#{attribute}_currency", 'currency'] if config[:with_currency]
32
+
33
+ composed_of attribute, :class_name => 'Money', :allow_nil => config[:allow_nil],
34
+ :mapping => mapping, :converter => lambda { |m| (m||0).to_money(config[:with_cents]) }
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,139 @@
1
+ class Numeric
2
+ # Converts this numeric to a Money object in the default currency. It
3
+ # multiplies the numeric value by 100 and treats that as cents if receive false.
4
+ #
5
+ # 100.to_money => #<Money @cents=100>
6
+ # 100.37.to_money => #<Money @cents=10037>
7
+ # 100.to_money(false) => #<Money @cents=10000>
8
+ def to_money(cents = false)
9
+ if cents
10
+ if self.is_a? Integer
11
+ Money.new(self)
12
+ else
13
+ Money.new(self.to_s.gsub(/\./,'').to_i)
14
+ end
15
+ else
16
+ Money.new(self * 100)
17
+ end
18
+ end
19
+ end
20
+
21
+ class String
22
+ # Parses the current string and converts it to a Money object.
23
+ # Excess characters will be discarded.
24
+ #
25
+ # '100'.to_money # => #<Money @cents=100>
26
+ # '100.37'.to_money # => #<Money @cents=10037>
27
+ # '100 USD'.to_money # => #<Money @cents=100, @currency="USD">
28
+ # 'USD 100'.to_money # => #<Money @cents=100, @currency="USD">
29
+ # '$100 USD'.to_money # => #<Money @cents=100, @currency="USD">
30
+ # '$100 USD'.to_money(false) # => #<Money @cents=10000, @currency="USD">
31
+ def to_money(with_cents = false)
32
+ # Get the currency.
33
+ matches = scan /([A-Z]{2,3})/
34
+ currency = matches[0] ? matches[0][0] : Money.default_currency
35
+ cents = calculate_cents(self, with_cents)
36
+ Money.new(cents, currency)
37
+ end
38
+
39
+ private
40
+
41
+ def calculate_cents(number, with_cents = true)
42
+ # remove anything that's not a number, potential delimiter, or minus sign
43
+ num = number.gsub(/[^\d|\.|,|\'|\s|\-]/, '').strip
44
+
45
+ # set a boolean flag for if the number is negative or not
46
+ negative = num.split(//).first == "-"
47
+
48
+ # if negative, remove the minus sign from the number
49
+ num = num.gsub(/^-/, '') if negative
50
+
51
+ # gather all separators within the result number
52
+ used_separators = num.scan /[^\d]/
53
+
54
+ # determine the number of unique separators within the number
55
+ #
56
+ # e.g.
57
+ # $1,234,567.89 would return 2 (, and .)
58
+ # $125,00 would return 1
59
+ # $199 would return 0
60
+ # $1 234,567.89 would raise an error (separators are space, comma, and period)
61
+ case used_separators.uniq.length
62
+ # no separator or delimiter; major (dollars) is the number, and minor (cents) is 0
63
+ when 0 then major, minor = num, 0
64
+
65
+ # two separators, so we know the last item in this array is the
66
+ # major/minor delimiter and the rest are separators
67
+ when 2
68
+ separator, delimiter = used_separators.uniq
69
+ # remove all separators, split on the delimiter
70
+ major, minor = num.gsub(separator, '').split(delimiter)
71
+ min = 0 unless min
72
+ when 1
73
+ # we can't determine if the comma or period is supposed to be a separator or a delimiter
74
+ # e.g.
75
+ # 1,00 - comma is a delimiter
76
+ # 1.000 - period is a delimiter
77
+ # 1,000 - comma is a separator
78
+ # 1,000,000 - comma is a separator
79
+ # 10000,00 - comma is a delimiter
80
+ # 1000,000 - comma is a delimiter
81
+
82
+ # assign first separator for reusability
83
+ separator = used_separators.first
84
+
85
+ # separator is used as a separator when there are multiple instances, always
86
+ if num.scan(separator).length > 1 # multiple matches; treat as separator
87
+ major, minor = num.gsub(separator, ''), 0
88
+ else
89
+ # ex: 1,000 - 1.0000 - 10001.000
90
+ # split number into possible major (dollars) and minor (cents) values
91
+ possible_major, possible_minor = num.split(separator)
92
+
93
+ # if the minor (cents) length isn't 3, assign major/minor from the possibles
94
+ # e.g.
95
+ # 1,00 => 1.00
96
+ # 1.0000 => 1.00
97
+ # 1.2 => 1.20
98
+ if possible_minor.length != 3 # delimiter
99
+ major, minor = possible_major, possible_minor
100
+ else
101
+ # minor length is three
102
+ # let's try to figure out intent of the delimiter
103
+
104
+ # the major length is greater than three, which means
105
+ # the comma or period is used as a delimiter
106
+ # e.g.
107
+ # 1000,000
108
+ # 100000,000
109
+ if possible_major.length > 3
110
+ major, minor = possible_major, possible_minor
111
+ else
112
+ # number is in format ###{sep}### or ##{sep}### or #{sep}###
113
+ # handle as , is sep, . is delimiter
114
+ if separator == '.'
115
+ major, minor = possible_major, possible_minor
116
+ else
117
+ major, minor = "#{possible_major}#{possible_minor}", 0
118
+ end
119
+ end
120
+ end
121
+ end
122
+ else
123
+ raise ArgumentError, "Invalid currency amount"
124
+ end
125
+
126
+ # build the string based on major/minor since separator/delimiters have been removed
127
+ # transform to a float, multiply by 100 to convert to cents
128
+ if with_cents and minor == 0
129
+ cents = "#{major}.#{minor}".to_f
130
+ else
131
+ cents = "#{major}.#{minor}".to_f * 100
132
+ end
133
+
134
+
135
+ # if negative, multiply by -1; otherwise, return positive cents
136
+ negative ? cents * -1 : cents
137
+ end
138
+
139
+ end
@@ -0,0 +1,4 @@
1
+ class Money
2
+ class UnknownRate < StandardError
3
+ end
4
+ end
@@ -0,0 +1,302 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'money/variable_exchange_bank'
3
+
4
+ # Represents an amount of money in a certain currency.
5
+ class Money
6
+ include Comparable
7
+
8
+ attr_reader :cents, :currency, :bank
9
+
10
+ class << self
11
+ # Each Money object is associated to a bank object, which is responsible
12
+ # for currency exchange. This property allows one to specify the default
13
+ # bank object.
14
+ #
15
+ # bank1 = MyBank.new
16
+ # bank2 = MyOtherBank.new
17
+ #e if VariableExchangeBank.
18
+ # It allows one to specify custom exchange rates:
19
+ #
20
+ # Money.default_bank.add_rate("USD", "CAD", 1.24515)
21
+ # Money.default_bank.add_rate("CAD", "USD", 0.803115)
22
+ # Money.us_dollar(100).exchange_to("CAD") # => MONEY.ca_dollar(124)
23
+ # Money.ca_dollar(100).exchange_to("USD") # => Money.us_dollar(80)
24
+ attr_accessor :default_bank
25
+
26
+ # the default currency, which is used when <tt>Money.new</tt> is called
27
+ # without an explicit currency argument. The default value is "USD".
28
+ attr_accessor :default_currency
29
+ end
30
+
31
+ CURRENCIES = {
32
+ "USD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
33
+ "CAD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
34
+ "HKD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
35
+ "SGD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
36
+ "BRL" => { :delimiter => ".", :separator => ",", :symbol => "R$" },
37
+ "EUR" => { :delimiter => ",", :separator => ".", :symbol => '€', :html => '&euro;' },
38
+ "GBP" => { :delimiter => ",", :separator => ".", :symbol => '£', :html => '&pound;' },
39
+ "JPY" => { :delimiter => ".", :separator => ".", :symbol => '¥', :html => '&yen;' },
40
+ }
41
+ self.default_bank = VariableExchangeBank.instance
42
+ self.default_currency = "USD"
43
+
44
+
45
+ # Create a new money object with value 0.
46
+ def self.empty(currency = default_currency)
47
+ Money.new(0, currency)
48
+ end
49
+
50
+ # Creates a new Money object of the given value, using the Canadian dollar currency.
51
+ def self.ca_dollar(cents)
52
+ Money.new(cents, "CAD")
53
+ end
54
+
55
+ # Creates a new Money object of the given value, using the American dollar currency.
56
+ def self.us_dollar(cents)
57
+ Money.new(cents, "USD")
58
+ end
59
+
60
+ # Creates a new Money object of the given value, using the Euro currency.
61
+ def self.euro(cents)
62
+ Money.new(cents, "EUR")
63
+ end
64
+
65
+ # Creates a new Money object of the given value, using the Brazilian Real currency.
66
+ def self.real(cents)
67
+ Money.new(cents, "BRL")
68
+ end
69
+
70
+ def self.add_rate(from_currency, to_currency, rate)
71
+ Money.default_bank.add_rate(from_currency, to_currency, rate)
72
+ end
73
+
74
+ # Creates a new money object.
75
+ # Money.new(100)
76
+ #
77
+ # Alternativly you can use the convinience methods like
78
+ # Money.ca_dollar and Money.us_dollar
79
+ def initialize(cents, currency = Money.default_currency, bank = Money.default_bank)
80
+ @cents = cents.round
81
+ @currency = currency
82
+ @bank = bank
83
+ end
84
+
85
+ # Do two money objects equal? Only works if both objects are of the same currency
86
+ def ==(other_money)
87
+ cents == other_money.cents && bank.same_currency?(currency, other_money.currency)
88
+ end
89
+
90
+ def <=>(other_money)
91
+ if bank.same_currency?(currency, other_money.currency)
92
+ cents <=> other_money.cents
93
+ else
94
+ cents <=> other_money.exchange_to(currency).cents
95
+ end
96
+ end
97
+
98
+ def +(other_money)
99
+ if currency == other_money.currency
100
+ Money.new(cents + other_money.cents, other_money.currency)
101
+ else
102
+ Money.new(cents + other_money.exchange_to(currency).cents,currency)
103
+ end
104
+ end
105
+
106
+ def -(other_money)
107
+ if currency == other_money.currency
108
+ Money.new(cents - other_money.cents, other_money.currency)
109
+ else
110
+ Money.new(cents - other_money.exchange_to(currency).cents, currency)
111
+ end
112
+ end
113
+
114
+ # get the cents value of the object
115
+ def cents
116
+ @cents
117
+ end
118
+
119
+ # multiply money by fixnum
120
+ def *(fixnum)
121
+ Money.new(cents * fixnum, currency)
122
+ end
123
+
124
+ # divide money by fixnum
125
+ # check out split_in_installments method too
126
+ def /(fixnum)
127
+ Money.new(cents / fixnum, currency)
128
+ end
129
+
130
+ def %(fixnum)
131
+ Money.new(cents % fixnum, currency)
132
+ end
133
+
134
+ # Test if the money amount is zero
135
+ def zero?
136
+ cents == 0
137
+ end
138
+
139
+ # Calculates compound interest
140
+ # Returns a money object with the sum of self + it
141
+ def compound_interest(rate,count=1)
142
+ Money.new(cents * ((1 + rate / 100.0 / 12) ** count - 1))
143
+ end
144
+
145
+ # Calculate self + simple interest
146
+ def simple_interest(rate,count=1)
147
+ Money.new(rate/100/12*cents*count)
148
+ end
149
+
150
+ # Split money in number of installments
151
+ #
152
+ # Money.new(10_00).split_in_installments(3)
153
+ # => [ 3.34, 3.33, 3.33 ] (All Money instances)
154
+ #
155
+ def split_in_installments(fixnum,extra=nil,*opts)
156
+ wallet = Wallet.new(fixnum, Money.new(cents/fixnum,currency))
157
+ to_add = cents % fixnum
158
+ to_add.times { |m| wallet[m] += Money.new(1) }
159
+ wallet
160
+ end
161
+
162
+ # Split money in installments based on payment value
163
+ #
164
+ # Money.new(1000_00).split_in_installments(Money.new(300_00))
165
+ # => [ 334_00, 333_00, 333_00 ] (All Money instances)
166
+ #
167
+ def in_installments_of(other_money,first=false)
168
+ split_in_installments(cents/other_money.cents,first)
169
+ end
170
+
171
+ # Just a helper if you got tax inputs in percentage.
172
+ # Ie. add_tax(20) => cents * 1.20
173
+ def add_tax(tax)
174
+ Money.new(cents + cents / 100 * tax)
175
+ end
176
+
177
+ # Format the price according to several rules
178
+ # Currently supported are :with_currency, :no_cents, :symbol and :html
179
+ #
180
+ # with_currency:
181
+ #
182
+ # Money.ca_dollar(0).format => "free"
183
+ # Money.ca_dollar(100).format => "$1.00"
184
+ # Money.ca_dollar(100).format(:with_currency => true) => "$1.00 CAD"
185
+ # Money.us_dollar(85).format(:with_currency => true) => "$0.85 USD"
186
+ #
187
+ # no_cents:
188
+ #
189
+ # Money.ca_dollar(100).format(:no_cents) => "$1"
190
+ # Money.ca_dollar(599).format(:no_cents) => "$5"
191
+ #
192
+ # Money.ca_dollar(570).format(:no_cents, :with_currency) => "$5 CAD"
193
+ # Money.ca_dollar(39000).format(:no_cents) => "$390"
194
+ #
195
+ # symbol:
196
+ #
197
+ # Money.new(100, :currency => "GBP").format(:symbol => "£") => "£1.00"
198
+ #
199
+ # html:
200
+ #
201
+ # Money.ca_dollar(570).format(:html => true, :with_currency => true) => "$5.70 <span class=\"currency\">CAD</span>"
202
+ def format(*rules)
203
+ # support for old format parameters
204
+ rules = normalize_formatting_rules(rules)
205
+
206
+ if cents == 0
207
+ if rules[:display_free].respond_to?(:to_str)
208
+ return rules[:display_free]
209
+ elsif rules[:display_free]
210
+ return "free"
211
+ end
212
+ end
213
+
214
+ if rules.has_key?(:symbol)
215
+ if rules[:symbol]
216
+ symbol = rules[:symbol]
217
+ else
218
+ symbol = ""
219
+ end
220
+ else
221
+ symbol = CURRENCIES[currency][:symbol]
222
+ end
223
+
224
+ if rules[:no_cents]
225
+ formatted = sprintf("#{symbol}%d", cents.to_f / 100)
226
+ formatted.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{CURRENCIES[currency][:delimiter]}")
227
+ else
228
+ formatted = sprintf("#{symbol}%.2f", cents.to_f / 100).split('.')
229
+ formatted[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{CURRENCIES[currency][:delimiter]}")
230
+ formatted = formatted.join(CURRENCIES[currency][:separator])
231
+ end
232
+
233
+ # Commify ("10000" => "10,000")
234
+ formatted.gsub!(/(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/,'\1,\2')
235
+
236
+ if rules[:with_currency]
237
+ formatted << " "
238
+ formatted << '<span class="currency">' if rules[:html]
239
+ formatted << currency
240
+ formatted << '</span>' if rules[:html]
241
+ end
242
+ formatted
243
+ end
244
+
245
+ def normalize_formatting_rules(rules)
246
+ if rules.size == 1
247
+ rules = rules.pop
248
+ rules = { rules => true } if rules.is_a?(Symbol)
249
+ else
250
+ rules = rules.inject({}) do |h,s|
251
+ h[s] = true
252
+ h
253
+ end
254
+ end
255
+ rules
256
+ end
257
+
258
+
259
+ # Money.ca_dollar(100).to_s => "1.00"
260
+ def to_s
261
+ sprintf("%.2f", cents / 100.0)
262
+ end
263
+
264
+ # Money.ca_dollar(100).to_f => "1.0"
265
+ def to_f
266
+ cents / 100.0
267
+ end
268
+
269
+ # Recieve the amount of this money object in another currency.
270
+ def exchange_to(other_currency)
271
+ Money.new(@bank.exchange(self.cents, currency, other_currency), other_currency)
272
+ end
273
+
274
+ # Conversation to self
275
+ def to_money
276
+ self
277
+ end
278
+
279
+ def method_missing(m,*x)
280
+ if m.to_s =~ /^as/
281
+ exchange_to(m.to_s.split("_").last.upcase)
282
+ else
283
+ super
284
+ end
285
+ end
286
+ end
287
+
288
+ #
289
+ # Represent a financial array.
290
+ # Investment/Time/Installments...
291
+ #
292
+ class Wallet < Array
293
+
294
+ def to_s
295
+ map &:to_s
296
+ end
297
+
298
+ def sum
299
+ Money.new(inject(0){ |sum,m| sum + m.cents })
300
+ end
301
+
302
+ end