samlown-money 2.3.3.2

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.
File without changes
@@ -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.
@@ -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
@@ -0,0 +1,137 @@
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/samlown/money
16
+ - Previous fork: http://github.com/ShadowBelmolve/money
17
+ - Website: http://money.rubyforge.org
18
+ - RDoc API: http://money.rubyforge.org
19
+ - Git repository: http://github.com/FooBarWidget/money/tree/master
20
+
21
+
22
+ == Download
23
+
24
+ Install stable releases with the following command:
25
+
26
+ gem install money
27
+
28
+ The development version (hosted on Github) can be installed with:
29
+
30
+ gem sources -a http://gems.github.com
31
+ gem install samlown-money
32
+
33
+
34
+ == Usage
35
+
36
+ === Synopsis
37
+
38
+ require 'money'
39
+
40
+ # 10.00 USD
41
+ money = Money.new(1000, "USD")
42
+ money.cents # => 1000
43
+ money.currency # => "USD"
44
+ money.format # => "$10.00"
45
+
46
+ Money.new(880088, "EUR").format # => €8,800.88
47
+ Money.new(-8000).format(:no_cents => true) # => $-80
48
+
49
+ Money.new(1000, "USD") == Money.new(1000, "USD") # => true
50
+ Money.new(1000, "USD") == Money.new( 100, "USD") # => false
51
+ Money.new(1000, "USD") == Money.new(1000, "EUR") # => false
52
+
53
+ # Comparisons with other numbers
54
+ Money.new(10.10) > 10.0 # => true
55
+ Money.new(1001) > 10 # => true
56
+ Money.new(999) > 10 # => false
57
+
58
+
59
+ === Currency Exchange
60
+
61
+ Exchanging money is performed through an exchange bank object. The default
62
+ exchange bank object requires one to manually specify the exchange rate. Here's
63
+ an example of how it works:
64
+
65
+ Money.add_rate("CAD", 0.803115)
66
+ Money.add_rate("USD", 1.24515)
67
+
68
+ Money.us_dollar(100_00).exchange_to("CAD") # => Money.new(15504, "CAD")
69
+ Money.ca_dollar(100_00).exchange_to("USD") # => Money.new(6450, "USD")
70
+
71
+ or
72
+
73
+ Money.us_dollar(100).as_cad # => Money.new(155, "CAD")
74
+ Money.ca_dollar(100).as_usd # => Money.new(64, "USD")
75
+
76
+ Comparison and arithmetic operations work as expected:
77
+
78
+ Money.new(1000, "USD") <=> Money.new(900, "USD") # => 1; 9.00 USD is smaller
79
+ Money.new(1000, "EUR") + Money.new(10, "EUR") == Money.new(1010, "EUR")
80
+
81
+ Money.add_rate("EUR", 0.5)
82
+ Money.new(1000, "EUR") + Money.new(1000, "USD") == Money.new(1500, "EUR")
83
+
84
+ Fetch the exchange rates published by the European Bank
85
+
86
+ Money.default_bank.fetch_rates # Fetch the rates
87
+ Money.default_bank.auto_fetch 3600 # Fetch the rates every hour
88
+ Money.default_bank.stop_fetch # Stop auto-fetch
89
+
90
+ There is nothing stopping you from creating bank objects which scrapes
91
+ www.xe.com for the current rates or just returns <tt>rand(2)</tt>:
92
+
93
+ Money.default_bank = ExchangeBankWhichScrapesXeDotCom.new
94
+
95
+
96
+ === Ruby on Rails
97
+
98
+ Use the +has_money+ method to embed the money object in your models.
99
+ The following example requires a +price_cents+ and a +price_currency+
100
+ fields on the database.
101
+
102
+ config/enviroment.rb
103
+
104
+ require.gem 'ShadowBelmolve-money', :lib => 'money'
105
+
106
+ app/models/product.rb
107
+
108
+ class Product < ActiveRecord::Base
109
+ belongs_to :product
110
+ has_money :price
111
+
112
+ validates_numericality_of :price_cents, :greater_than => 0
113
+ end
114
+
115
+ migration:
116
+
117
+ create_table :products do |t|
118
+ t.integer :price_cents
119
+ t.string :price_currency
120
+ end
121
+
122
+
123
+ === Default Currency
124
+
125
+ By default Money defaults to USD as its currency. This can be overwritten using:
126
+
127
+ Money.default_currency = "CAD"
128
+
129
+ If you use Rails, then environment.rb is a very good place to put this.
130
+
131
+
132
+ == TODO
133
+
134
+ * Better validation (fix composed_of allow_nil)
135
+ * Interest (almost there..)
136
+ * Remote rate fetching
137
+
@@ -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,29 @@
1
+ # Copyright (c) 2005 Tobias Luetke
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.
22
+
23
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
24
+ require 'money/money'
25
+ require 'money/core_extensions'
26
+
27
+ class Money
28
+ VERSION = "2.3.3"
29
+ end
@@ -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 => false }.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,319 @@
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
+ self.default_bank = VariableExchangeBank.instance
32
+ self.default_currency = "USD"
33
+
34
+ CURRENCIES = {
35
+ "USD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
36
+ "CAD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
37
+ "HKD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
38
+ "SGD" => { :delimiter => ",", :separator => ".", :symbol => "$" },
39
+ "BRL" => { :delimiter => ".", :separator => ",", :symbol => "R$" },
40
+ "EUR" => { :delimiter => ",", :separator => ".", :symbol => '€', :html => '&euro;' },
41
+ "GBP" => { :delimiter => ",", :separator => ".", :symbol => '£', :html => '&pound;' },
42
+ "JPY" => { :delimiter => ".", :separator => ".", :symbol => '¥', :html => '&yen;' },
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(currency, rate)
71
+ Money.default_bank.add_rate(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 = nil, bank = nil)
80
+ @cents = cents.to_i
81
+ @currency = currency || Money.default_currency
82
+ @bank = bank || Money.default_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
+ case other_money
92
+ when Money
93
+ if bank.same_currency?(currency, other_money.currency)
94
+ cents <=> other_money.cents
95
+ else
96
+ cents <=> other_money.exchange_to(currency).cents
97
+ end
98
+ when Numeric
99
+ cents <=> (other_money * 100).to_i
100
+ else
101
+ raise "Comparison attempted with incompatible Money type"
102
+ end
103
+ end
104
+
105
+ def +(other_money)
106
+ other_money = Money.new(other_money) unless other_money.is_a? 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
+ def -(other_money)
115
+ other_money = Money.new(other_money) unless other_money.is_a? Money
116
+ if currency == other_money.currency
117
+ Money.new(cents - other_money.cents, other_money.currency)
118
+ else
119
+ Money.new(cents - other_money.exchange_to(currency).cents, currency)
120
+ end
121
+ end
122
+
123
+ # get the cents value of the object
124
+ def cents
125
+ @cents
126
+ end
127
+
128
+ # multiply money by fixnum
129
+ def *(fixnum)
130
+ Money.new(cents * fixnum, currency)
131
+ end
132
+
133
+ # divide money by fixnum
134
+ # check out split_in_installments method too
135
+ def /(fixnum)
136
+ Money.new(cents / fixnum, currency)
137
+ end
138
+
139
+ def %(fixnum)
140
+ Money.new(cents % fixnum, currency)
141
+ end
142
+
143
+ # Test if the money amount is zero
144
+ def zero?
145
+ cents == 0
146
+ end
147
+
148
+ # Calculates compound interest
149
+ # Returns a money object with the sum of self + it
150
+ def compound_interest(rate, count = 1, period = 12)
151
+ Money.new(cents * ((1 + rate / 100.0 / period) ** count - 1))
152
+ end
153
+
154
+ # Calculate self + simple interest
155
+ def simple_interest(rate, count = 1, period = 12)
156
+ Money.new(rate / 100 / period * cents * count)
157
+ end
158
+
159
+ # Split money in number of installments
160
+ #
161
+ # Money.new(10_00).split_in_installments(3)
162
+ # => [ 3.34, 3.33, 3.33 ] (All Money instances)
163
+ #
164
+ def split_in_installments(fixnum, order=false)
165
+ wallet = Wallet.new(fixnum, Money.new(cents/fixnum,currency))
166
+ to_add = cents % fixnum
167
+ to_add.times { |m| wallet[m] += Money.new(1) }
168
+ wallet.reverse! if order
169
+ wallet
170
+ end
171
+
172
+ # Split money in installments based on payment value
173
+ #
174
+ # Money.new(1000_00).split_in_installments(Money.new(300_00))
175
+ # => [ 334_00, 333_00, 333_00 ] (All Money instances)
176
+ #
177
+ def in_installments_of(other_money, order=false)
178
+ split_in_installments(cents/other_money.cents, order)
179
+ end
180
+
181
+ # Just a helper if you got tax inputs in percentage.
182
+ # Ie. add_tax(20) => cents * 1.20
183
+ def add_tax(tax)
184
+ Money.new(cents + cents / 100 * tax)
185
+ end
186
+
187
+ # Format the price according to several rules
188
+ # Currently supported are :with_currency, :no_cents, :symbol and :html
189
+ #
190
+ # with_currency:
191
+ #
192
+ # Money.ca_dollar(0).format => "free"
193
+ # Money.ca_dollar(100).format => "$1.00"
194
+ # Money.ca_dollar(100).format(:with_currency => true) => "$1.00 CAD"
195
+ # Money.us_dollar(85).format(:with_currency => true) => "$0.85 USD"
196
+ #
197
+ # no_cents:
198
+ #
199
+ # Money.ca_dollar(100).format(:no_cents) => "$1"
200
+ # Money.ca_dollar(599).format(:no_cents) => "$5"
201
+ #
202
+ # Money.ca_dollar(570).format(:no_cents, :with_currency) => "$5 CAD"
203
+ # Money.ca_dollar(39000).format(:no_cents) => "$390"
204
+ #
205
+ # symbol:
206
+ #
207
+ # Money.new(100, :currency => "GBP").format(:symbol => "£") => "£1.00"
208
+ #
209
+ # html:
210
+ #
211
+ # Money.ca_dollar(570).format(:html => true, :with_currency => true) => "$5.70 <span class=\"currency\">CAD</span>"
212
+ def format(*rules)
213
+ # support for old format parameters
214
+ rules = normalize_formatting_rules(rules)
215
+
216
+ if cents == 0
217
+ if rules[:display_free].respond_to?(:to_str)
218
+ return rules[:display_free]
219
+ elsif rules[:display_free]
220
+ return "free"
221
+ end
222
+ end
223
+
224
+ if rules.has_key?(:symbol)
225
+ if rules[:symbol]
226
+ symbol = rules[:symbol]
227
+ else
228
+ symbol = ""
229
+ end
230
+ else
231
+ symbol = CURRENCIES[currency][:symbol]
232
+ end
233
+ self.currency
234
+
235
+ if rules[:no_cents]
236
+ formatted = sprintf("#{symbol}%d", cents.to_f / 100)
237
+ formatted.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{CURRENCIES[currency][:delimiter]}")
238
+ else
239
+ formatted = sprintf("#{symbol}%.2f", cents.to_f / 100).split('.')
240
+ formatted[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{CURRENCIES[currency][:delimiter]}")
241
+ formatted = formatted.join(CURRENCIES[currency][:separator])
242
+ end
243
+
244
+ # Commify ("10000" => "10,000")
245
+ formatted.gsub!(/(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/,'\1,\2')
246
+
247
+ if rules[:with_currency]
248
+ formatted << " "
249
+ formatted << '<span class="currency">' if rules[:html]
250
+ formatted << currency
251
+ formatted << '</span>' if rules[:html]
252
+ end
253
+ formatted.gsub!(CURRENCIES[currency][:symbol],CURRENCIES[currency][:html]) if rules[:html]
254
+ formatted
255
+ end
256
+
257
+ def normalize_formatting_rules(rules)
258
+ if rules.size == 1
259
+ rules = rules.pop
260
+ rules = { rules => true } if rules.is_a?(Symbol)
261
+ else
262
+ rules = rules.inject({}) do |h,s|
263
+ h[s] = true
264
+ h
265
+ end
266
+ end
267
+ rules
268
+ end
269
+
270
+
271
+ # Money.ca_dollar(100).to_s => "1.00"
272
+ def to_s
273
+ sprintf("%.2f", cents / 100.0)
274
+ end
275
+
276
+ # Money.new(123).to_i => "1"
277
+ def to_i
278
+ (cents / 100.0).round
279
+ end
280
+
281
+ # Money.ca_dollar(100).to_f => "1.0"
282
+ def to_f
283
+ cents / 100.0
284
+ end
285
+
286
+ # Recieve the amount of this money object in another currency.
287
+ def exchange_to(other_currency)
288
+ Money.new(@bank.exchange(self.cents, currency, other_currency), other_currency)
289
+ end
290
+
291
+ # Conversation to self
292
+ def to_money
293
+ self
294
+ end
295
+
296
+ def method_missing(m,*x)
297
+ if m.to_s =~ /^as/
298
+ exchange_to(m.to_s.split("_").last.upcase)
299
+ else
300
+ super
301
+ end
302
+ end
303
+ end
304
+
305
+ #
306
+ # Represent a financial array.
307
+ # Investment/Time/Installments...
308
+ #
309
+ class Wallet < Array
310
+
311
+ def to_s
312
+ map &:to_s
313
+ end
314
+
315
+ def sum
316
+ Money.new(inject(0){ |sum,m| sum + m.cents })
317
+ end
318
+
319
+ end