carlosbrando-money 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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/README.rdoc ADDED
@@ -0,0 +1,97 @@
1
+ = Introduction
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
+
12
+ Resources:
13
+
14
+ - Website: http://money.rubyforge.org
15
+ - RDoc API: http://money.rubyforge.org
16
+ - Git repository: http://github.com/FooBarWidget/money/tree/master
17
+
18
+ == Download
19
+
20
+ Install stable releases with the following command:
21
+
22
+ gem install money
23
+
24
+ The development version (hosted on Github) can be installed with:
25
+
26
+ gem sources -a http://gems.github.com
27
+ gem install FooBarWidget-money
28
+
29
+ == Usage
30
+
31
+ === Synopsis
32
+
33
+ require 'money'
34
+
35
+ # 10.00 USD
36
+ money = Money.new(1000, "USD")
37
+ money.cents # => 1000
38
+ money.currency # => "USD"
39
+
40
+ Money.new(1000, "USD") == Money.new(1000, "USD") # => true
41
+ Money.new(1000, "USD") == Money.new(100, "USD") # => false
42
+ Money.new(1000, "USD") == Money.new(1000, "EUR") # => false
43
+
44
+ === Currency Exchange
45
+
46
+ Exchanging money is performed through an exchange bank object. The default
47
+ exchange bank object requires one to manually specify the exchange rate. Here's
48
+ an example of how it works:
49
+
50
+ Money.add_rate("USD", "CAD", 1.24515)
51
+ Money.add_rate("CAD", "USD", 0.803115)
52
+
53
+ Money.us_dollar(100).exchange_to("CAD") # => Money.new(124, "CAD")
54
+ Money.ca_dollar(100).exchange_to("USD") # => Money.new(80, "USD")
55
+
56
+ Comparison and arithmetic operations work as expected:
57
+
58
+ Money.new(1000, "USD") <=> Money.new(900, "USD") # => 1; 9.00 USD is smaller
59
+ Money.new(1000, "EUR") + Money.new(10, "EUR") == Money.new(1010, "EUR")
60
+
61
+ Money.add_rate("USD", "EUR", 0.5)
62
+ Money.new(1000, "EUR") + Money.new(1000, "USD") == Money.new(1500, "EUR")
63
+
64
+ There is nothing stopping you from creating bank objects which scrapes
65
+ www.xe.com for the current rates or just returns <tt>rand(2)</tt>:
66
+
67
+ Money.default_bank = ExchangeBankWhichScrapesXeDotCom.new
68
+
69
+ === Ruby on Rails
70
+
71
+ Use the +compose_of+ helper to let Active Record deal with embedding the money
72
+ object in your models. The following example requires a +cents+ and a +currency+
73
+ field.
74
+
75
+ class ProductUnit < ActiveRecord::Base
76
+ belongs_to :product
77
+ composed_of :price, :class_name => "Money", :mapping => [%w(cents cents), %w(currency currency)]
78
+
79
+ private
80
+ validate :cents_not_zero
81
+
82
+ def cents_not_zero
83
+ errors.add("cents", "cannot be zero or less") unless cents > 0
84
+ end
85
+
86
+ validates_presence_of :sku, :currency
87
+ validates_uniqueness_of :sku
88
+ end
89
+
90
+ === Default Currency
91
+
92
+ By default Money defaults to USD as its currency. This can be overwritten using
93
+
94
+ Money.default_currency = "CAD"
95
+
96
+ If you use Rails, then environment.rb is a very good place to put this.
97
+
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ desc "Build a gem"
2
+ task :gem do
3
+ sh "gem build money.gemspec"
4
+ end
5
+
6
+ task "Generate RDoc documentation"
7
+ task :rdoc do
8
+ sh "hanna README.rdoc lib -U"
9
+ end
10
+
11
+ task :upload => :rdoc do
12
+ sh "scp -r doc/* rubyforge.org:/var/www/gforge-projects/money/"
13
+ end
14
+
15
+ desc "Run unit tests"
16
+ task :test do
17
+ ruby "-S spec -f s -c test/*_spec.rb"
18
+ end
data/lib/money.rb ADDED
@@ -0,0 +1,26 @@
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/symbols'
26
+ require 'money/core_extensions'
@@ -0,0 +1,138 @@
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.
4
+ #
5
+ # 100.to_money => #<Money @cents=10000>
6
+ # 100.37.to_money => #<Money @cents=10037>
7
+ def to_money
8
+ Money.new(self * 100)
9
+ end
10
+ end
11
+
12
+ class String
13
+ # Parses the current string and converts it to a Money object.
14
+ # Excess characters will be discarded.
15
+ #
16
+ # '100'.to_money # => #<Money @cents=10000>
17
+ # '100.37'.to_money # => #<Money @cents=10037>
18
+ # '100 USD'.to_money # => #<Money @cents=10000, @currency="USD">
19
+ # 'USD 100'.to_money # => #<Money @cents=10000, @currency="USD">
20
+ # '$100 USD'.to_money # => #<Money @cents=10000, @currency="USD">
21
+ # 'hello 2000 world'.to_money # => #<Money @cents=200000 @currency="USD")>
22
+ def to_money
23
+ # Get the currency.
24
+ matches = scan /([A-Z]{2,3})/
25
+ currency = matches[0] ? matches[0][0] : Money.default_currency
26
+ cents = calculate_cents(self)
27
+ Money.new(cents, currency)
28
+ end
29
+
30
+ private
31
+
32
+ def calculate_cents(number)
33
+ # remove anything that's not a number, potential delimiter, or minus sign
34
+ num = number.gsub(/[^\d|\.|,|\'|\s|\-]/, '').strip
35
+
36
+ # set a boolean flag for if the number is negative or not
37
+ negative = num.split(//).first == "-"
38
+
39
+ # if negative, remove the minus sign from the number
40
+ num = num.gsub(/^-/, '') if negative
41
+
42
+ # gather all separators within the result number
43
+ used_separators = num.scan /[^\d]/
44
+
45
+ # determine the number of unique separators within the number
46
+ #
47
+ # e.g.
48
+ # $1,234,567.89 would return 2 (, and .)
49
+ # $125,00 would return 1
50
+ # $199 would return 0
51
+ # $1 234,567.89 would raise an error (separators are space, comma, and period)
52
+ case used_separators.uniq.length
53
+ # no separator or delimiter; major (dollars) is the number, and minor (cents) is 0
54
+ when 0 then major, minor = num, 0
55
+
56
+ # two separators, so we know the last item in this array is the
57
+ # major/minor delimiter and the rest are separators
58
+ when 2
59
+ separator, delimiter = used_separators.uniq
60
+ # remove all separators, split on the delimiter
61
+ major, minor = num.gsub(separator, '').split(delimiter)
62
+ min = 0 unless min
63
+ when 1
64
+ # we can't determine if the comma or period is supposed to be a separator or a delimiter
65
+ # e.g.
66
+ # 1,00 - comma is a delimiter
67
+ # 1.000 - period is a delimiter
68
+ # 1,000 - comma is a separator
69
+ # 1,000,000 - comma is a separator
70
+ # 10000,00 - comma is a delimiter
71
+ # 1000,000 - comma is a delimiter
72
+
73
+ # assign first separator for reusability
74
+ separator = used_separators.first
75
+
76
+ # separator is used as a separator when there are multiple instances, always
77
+ if num.scan(separator).length > 1 # multiple matches; treat as separator
78
+ major, minor = num.gsub(separator, ''), 0
79
+ else
80
+ # ex: 1,000 - 1.0000 - 10001.000
81
+ # split number into possible major (dollars) and minor (cents) values
82
+ possible_major, possible_minor = num.split(separator)
83
+ possible_major ||= "0"
84
+ possible_minor ||= "00"
85
+
86
+ # if the minor (cents) length isn't 3, assign major/minor from the possibles
87
+ # e.g.
88
+ # 1,00 => 1.00
89
+ # 1.0000 => 1.00
90
+ # 1.2 => 1.20
91
+ if possible_minor.length != 3 # delimiter
92
+ major, minor = possible_major, possible_minor
93
+ else
94
+ # minor length is three
95
+ # let's try to figure out intent of the delimiter
96
+
97
+ # the major length is greater than three, which means
98
+ # the comma or period is used as a delimiter
99
+ # e.g.
100
+ # 1000,000
101
+ # 100000,000
102
+ if possible_major.length > 3
103
+ major, minor = possible_major, possible_minor
104
+ else
105
+ # number is in format ###{sep}### or ##{sep}### or #{sep}###
106
+ # handle as , is sep, . is delimiter
107
+ if separator == '.'
108
+ major, minor = possible_major, possible_minor
109
+ else
110
+ major, minor = "#{possible_major}#{possible_minor}", 0
111
+ end
112
+ end
113
+ end
114
+ end
115
+ else
116
+ raise ArgumentError, "Invalid currency amount"
117
+ end
118
+
119
+ # build the string based on major/minor since separator/delimiters have been removed
120
+ # avoiding floating point arithmetic here to ensure accuracy
121
+ cents = (major.to_i * 100)
122
+ # add the minor number as well. this may have any number of digits,
123
+ # so we treat minor as a string and truncate or right-fill it with zeroes
124
+ # until it becomes a two-digit number string, which we add to cents.
125
+ minor = minor.to_s
126
+ truncated_minor = minor[0..1]
127
+ truncated_minor << "0" * (2 - truncated_minor.size) if truncated_minor.size < 2
128
+ cents += truncated_minor.to_i
129
+ # respect rounding rules
130
+ if minor.size >= 3 && minor[2..2].to_i >= 5
131
+ cents += 1
132
+ end
133
+
134
+ # if negative, multiply by -1; otherwise, return positive cents
135
+ negative ? cents * -1 : cents
136
+ end
137
+
138
+ end
@@ -0,0 +1,4 @@
1
+ class Money
2
+ class UnknownRate < StandardError
3
+ end
4
+ end
@@ -0,0 +1,345 @@
1
+ require 'money/variable_exchange_bank'
2
+
3
+ # Represents an amount of money in a certain currency.
4
+ class Money
5
+ include Comparable
6
+
7
+ attr_reader :cents, :currency, :bank
8
+
9
+ class << self
10
+ # Each Money object is associated to a bank object, which is responsible
11
+ # for currency exchange. This property allows one to specify the default
12
+ # bank object.
13
+ #
14
+ # bank1 = MyBank.new
15
+ # bank2 = MyOtherBank.new
16
+ #
17
+ # Money.default_bank = bank1
18
+ # money1 = Money.new(10)
19
+ # money1.bank # => bank1
20
+ #
21
+ # Money.default_bank = bank2
22
+ # money2 = Money.new(10)
23
+ # money2.bank # => bank2
24
+ # money1.bank # => bank1
25
+ #
26
+ # The default value for this property is an instance if VariableExchangeBank.
27
+ # It allows one to specify custom exchange rates:
28
+ #
29
+ # Money.default_bank.add_rate("USD", "CAD", 1.24515)
30
+ # Money.default_bank.add_rate("CAD", "USD", 0.803115)
31
+ # Money.us_dollar(100).exchange_to("CAD") # => Money.ca_dollar(124)
32
+ # Money.ca_dollar(100).exchange_to("USD") # => Money.us_dollar(80)
33
+ attr_accessor :default_bank
34
+
35
+ # The default currency, which is used when <tt>Money.new</tt> is called
36
+ # without an explicit currency argument. The default value is "USD".
37
+ attr_accessor :default_currency
38
+ end
39
+
40
+ self.default_bank = VariableExchangeBank.instance
41
+ self.default_currency = "USD"
42
+
43
+
44
+ # Create a new money object with value 0.
45
+ def self.empty(currency = default_currency)
46
+ Money.new(0, currency)
47
+ end
48
+
49
+ # Creates a new Money object of the given value, using the Canadian dollar currency.
50
+ def self.ca_dollar(cents)
51
+ Money.new(cents, "CAD")
52
+ end
53
+
54
+ # Creates a new Money object of the given value, using the American dollar currency.
55
+ def self.us_dollar(cents)
56
+ Money.new(cents, "USD")
57
+ end
58
+
59
+ # Creates a new Money object of the given value, using the Euro currency.
60
+ def self.euro(cents)
61
+ Money.new(cents, "EUR")
62
+ end
63
+
64
+ # Creates a new Money object of the given value, using the Brazilian real currency.
65
+ def self.brazilian_real(cents)
66
+ Money.new(cents, "BRL")
67
+ end
68
+
69
+ def self.add_rate(from_currency, to_currency, rate)
70
+ Money.default_bank.add_rate(from_currency, to_currency, rate)
71
+ end
72
+
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
+ if currency.is_a?(Hash)
82
+ # Earlier versions of Money wrongly documented the constructor as being able
83
+ # to accept something like this:
84
+ #
85
+ # Money.new(50, :currency => "USD")
86
+ #
87
+ # We retain compatibility here.
88
+ @currency = currency[:currency] || Money.default_currency
89
+ else
90
+ @currency = currency
91
+ end
92
+ @bank = bank
93
+ end
94
+
95
+ # Checks whether two money objects have the same currency and the same amount.
96
+ # Checks against money objects with a different currency and checks against
97
+ # objects that do not respond to #to_money will always return false.
98
+ def ==(other_money)
99
+ if other_money.respond_to?(:to_money)
100
+ other_money = other_money.to_money
101
+ cents == other_money.cents && bank.same_currency?(currency, other_money.currency)
102
+ else
103
+ false
104
+ end
105
+ end
106
+
107
+ # Compares this money object against another object. +other_money+ must respond
108
+ # to #to_money.
109
+ #
110
+ # If +other_money+ is of a different currency, then +other_money+ will first be
111
+ # converted into this money object's currency by calling +other_money.exchange+.
112
+ #
113
+ # Comparisons against objects that do not respond to #to_money will cause an
114
+ # ArgumentError to be raised.
115
+ def <=>(other_money)
116
+ if other_money.respond_to?(:to_money)
117
+ other_money = other_money.to_money
118
+ if bank.same_currency?(currency, other_money.currency)
119
+ cents <=> other_money.cents
120
+ else
121
+ cents <=> other_money.exchange_to(currency).cents
122
+ end
123
+ else
124
+ raise ArgumentError, "comparison of #{self.class} with #{other_money.inspect} failed"
125
+ end
126
+ end
127
+
128
+ def +(other_money)
129
+ if currency == other_money.currency
130
+ Money.new(cents + other_money.cents, other_money.currency)
131
+ else
132
+ Money.new(cents + other_money.exchange_to(currency).cents, currency)
133
+ end
134
+ end
135
+
136
+ def -(other_money)
137
+ if currency == other_money.currency
138
+ Money.new(cents - other_money.cents, other_money.currency)
139
+ else
140
+ Money.new(cents - other_money.exchange_to(currency).cents, currency)
141
+ end
142
+ end
143
+
144
+ # get the cents value of the object
145
+ def cents
146
+ @cents
147
+ end
148
+
149
+ # multiply money by fixnum
150
+ def *(fixnum)
151
+ Money.new(cents * fixnum, currency)
152
+ end
153
+
154
+ # divide money by money or fixnum
155
+ def /(val)
156
+ if val.is_a?(Money)
157
+ if currency == val.currency
158
+ cents / val.cents.to_f
159
+ else
160
+ cents / val.exchange_to(currency).cents.to_f
161
+ end
162
+ else
163
+ Money.new(cents / val, currency)
164
+ end
165
+ end
166
+
167
+ # Test if the money amount is zero
168
+ def zero?
169
+ cents == 0
170
+ end
171
+
172
+
173
+ # Creates a formatted price string according to several rules. The following
174
+ # options are supported: :display_free, :with_currency, :no_cents, :symbol
175
+ # and :html.
176
+ #
177
+ # === +:display_free+
178
+ #
179
+ # Whether a zero amount of money should be formatted of "free" or as the
180
+ # supplied string.
181
+ #
182
+ # Money.us_dollar(0).format(:display_free => true) => "free"
183
+ # Money.us_dollar(0).format(:display_free => "gratis") => "gratis"
184
+ # Money.us_dollar(0).format => "$0.00"
185
+ #
186
+ # === +:with_currency+
187
+ #
188
+ # Whether the currency name should be appended to the result string.
189
+ #
190
+ # Money.ca_dollar(100).format => "$1.00"
191
+ # Money.ca_dollar(100).format(:with_currency => true) => "$1.00 CAD"
192
+ # Money.us_dollar(85).format(:with_currency => true) => "$0.85 USD"
193
+ #
194
+ # === +:no_cents+
195
+ #
196
+ # Whether cents should be omitted.
197
+ #
198
+ # Money.ca_dollar(100).format(:no_cents => true) => "$1"
199
+ # Money.ca_dollar(599).format(:no_cents => true) => "$5"
200
+ #
201
+ # Money.ca_dollar(570).format(:no_cents => true, :with_currency => true) => "$5 CAD"
202
+ # Money.ca_dollar(39000).format(:no_cents => true) => "$390"
203
+ #
204
+ # === +:symbol+
205
+ #
206
+ # Whether a money symbol should be prepended to the result string. The default is true.
207
+ # This method attempts to pick a symbol that's suitable for the given currency.
208
+ #
209
+ # Money.new(100, "USD") => "$1.00"
210
+ # Money.new(100, "GBP") => "£1.00"
211
+ # Money.new(100, "EUR") => "€1.00"
212
+ #
213
+ # # Same thing.
214
+ # Money.new(100, "USD").format(:symbol => true) => "$1.00"
215
+ # Money.new(100, "GBP").format(:symbol => true) => "£1.00"
216
+ # Money.new(100, "EUR").format(:symbol => true) => "€1.00"
217
+ #
218
+ # You can specify a false expression or an empty string to disable prepending
219
+ # a money symbol:
220
+ #
221
+ # Money.new(100, "USD").format(:symbol => false) => "1.00"
222
+ # Money.new(100, "GBP").format(:symbol => nil) => "1.00"
223
+ # Money.new(100, "EUR").format(:symbol => "") => "1.00"
224
+ #
225
+ #
226
+ # If the symbol for the given currency isn't known, then it will default
227
+ # to "$" as symbol:
228
+ #
229
+ # Money.new(100, "AWG").format(:symbol => true) => "$1.00"
230
+ #
231
+ # You can specify a string as value to enforce using a particular symbol:
232
+ #
233
+ # Money.new(100, "AWG").format(:symbol => "ƒ") => "ƒ1.00"
234
+ #
235
+ # === +:html+
236
+ #
237
+ # Whether the currency should be HTML-formatted. Only useful in combination with +:with_currency+.
238
+ #
239
+ # Money.ca_dollar(570).format(:html => true, :with_currency => true)
240
+ # => "$5.70 <span class=\"currency\">CAD</span>"
241
+ def format(*rules)
242
+ # support for old format parameters
243
+ rules = normalize_formatting_rules(rules)
244
+
245
+ if cents == 0
246
+ if rules[:display_free].respond_to?(:to_str)
247
+ return rules[:display_free]
248
+ elsif rules[:display_free]
249
+ return "free"
250
+ end
251
+ end
252
+
253
+ if rules.has_key?(:symbol)
254
+ if rules[:symbol] === true
255
+ symbol = currency_unit
256
+ elsif rules[:symbol]
257
+ symbol = rules[:symbol]
258
+ else
259
+ symbol = ""
260
+ end
261
+ else
262
+ symbol = currency_unit
263
+ end
264
+
265
+ if rules[:no_cents]
266
+ formatted = sprintf("#{symbol}%d", cents.to_f / 100)
267
+ else
268
+ formatted = sprintf("#{symbol}%.2f", cents.to_f / 100)
269
+ end
270
+
271
+ # Commify ("10000" => "10,000") or ("10000" => "10.000")
272
+ formatted.gsub! /(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/, "\\1#{currency_delimiter}\\2"
273
+
274
+ # Corrects the decimal separator if necessary.
275
+ formatted.sub!(/\.(\d{2})$/, "#{currency_separator}\\1") unless rules[:no_cents] || currency_separator == '.'
276
+
277
+ if rules[:with_currency]
278
+ formatted << " "
279
+ formatted << '<span class="currency">' if rules[:html]
280
+ formatted << currency
281
+ formatted << '</span>' if rules[:html]
282
+ end
283
+ formatted
284
+ end
285
+
286
+ # Returns the amount of money as a string.
287
+ #
288
+ # Money.ca_dollar(100).to_s => "1.00"
289
+ def to_s
290
+ sprintf("%.2f", cents / 100.00)
291
+ end
292
+
293
+ # Return the amount of money as a float. Floating points cannot guarantee
294
+ # precision. Therefore, this function should only be used when you no longer
295
+ # need to represent currency or working with another system that requires
296
+ # decimals.
297
+ #
298
+ # Money.us_dollar(100).to_f => 1.0
299
+ def to_f
300
+ cents / 100.0
301
+ end
302
+
303
+ # Recieve the amount of this money object in another currency.
304
+ def exchange_to(other_currency)
305
+ Money.new(@bank.exchange(self.cents, currency, other_currency), other_currency)
306
+ end
307
+
308
+ # Recieve a money object with the same amount as the current Money object
309
+ # in american dollar
310
+ def as_us_dollar
311
+ exchange_to("USD")
312
+ end
313
+
314
+ # Recieve a money object with the same amount as the current Money object
315
+ # in canadian dollar
316
+ def as_ca_dollar
317
+ exchange_to("CAD")
318
+ end
319
+
320
+ # Recieve a money object with the same amount as the current Money object
321
+ # in euro
322
+ def as_euro
323
+ exchange_to("EUR")
324
+ end
325
+
326
+ # Conversation to self
327
+ def to_money
328
+ self
329
+ end
330
+
331
+ private
332
+
333
+ def normalize_formatting_rules(rules)
334
+ if rules.size == 1
335
+ rules = rules.pop
336
+ rules = { rules => true } if rules.is_a?(Symbol)
337
+ else
338
+ rules = rules.inject({}) do |h,s|
339
+ h[s] = true
340
+ h
341
+ end
342
+ end
343
+ rules
344
+ end
345
+ end