money 4.0.1 → 4.0.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.
@@ -1,19 +1,21 @@
1
- require 'pathname'
2
-
3
- module CurrencyLoader
4
- extend self
5
-
6
- DATA_PATH = Pathname.new(__FILE__).dirname + "../../config/"
7
-
8
- # Loads and returns the currencies stored in JSON files in the config directory.
9
- #
10
- # @return [Hash]
11
- def load_currencies
12
- json = File.read(DATA_PATH + 'currency.json')
13
- currencies = JSON.parse(json, :symbolize_names => true)
14
-
15
- # merge the currencies kept for backwards compatibility
16
- json = File.read(DATA_PATH + 'currency_bc.json')
17
- currencies.merge!(JSON.parse(json, :symbolize_names => true))
18
- end
19
- end
1
+ module CurrencyLoader
2
+ extend self
3
+
4
+ DATA_PATH = File.expand_path("../../../config", __FILE__)
5
+
6
+ # Loads and returns the currencies stored in JSON files in the config directory.
7
+ #
8
+ # @return [Hash]
9
+ def load_currencies
10
+ currencies = parse_currency_file("currency.json")
11
+ currencies.merge! parse_currency_file("currency_bc.json")
12
+ end
13
+
14
+ private
15
+
16
+ def parse_currency_file(filename)
17
+ json = File.read("#{DATA_PATH}/#{filename}")
18
+ json.force_encoding(::Encoding::UTF_8) if defined?(::Encoding)
19
+ JSON.parse(json, :symbolize_names => true)
20
+ end
21
+ end
data/lib/money/money.rb CHANGED
@@ -1,405 +1,411 @@
1
- # encoding: utf-8
2
- require 'money/bank/variable_exchange'
3
- require 'money/money/arithmetic'
4
- require 'money/money/parsing'
5
- require 'money/money/formatting'
6
-
7
- # Represents an amount of money in a given currency.
8
- class Money
9
- include Comparable
10
- include Arithmetic
11
- include Formatting
12
- include Parsing
13
-
14
- # The value of the money in cents.
15
- #
16
- # @return [Integer]
17
- attr_reader :cents
18
-
19
- # The currency the money is in.
20
- #
21
- # @return [Currency]
22
- attr_reader :currency
23
-
24
- # The +Money::Bank+ based object used to perform currency exchanges with.
25
- #
26
- # @return [Money::Bank::*]
27
- attr_reader :bank
28
-
29
- # Class Methods
30
- class << self
31
- # Each Money object is associated to a bank object, which is responsible
32
- # for currency exchange. This property allows you to specify the default
33
- # bank object. The default value for this property is an instance if
34
- # +Bank::VariableExchange.+ It allows one to specify custom exchange rates.
35
- #
36
- # @return [Money::Bank::*]
37
- attr_accessor :default_bank
38
-
39
- # The default currency, which is used when +Money.new+ is called without an
40
- # explicit currency argument. The default value is Currency.new("USD"). The
41
- # value must be a valid +Money::Currency+ instance.
42
- #
43
- # @return [Money::Currency]
44
- attr_accessor :default_currency
45
-
46
- # Use this to disable i18n even if it's used by other objects in your app.
47
- #
48
- # @return [true,false]
49
- attr_accessor :use_i18n
50
- end
51
-
52
- # Set the default bank for creating new +Money+ objects.
53
- self.default_bank = Bank::VariableExchange.instance
54
-
55
- # Set the default currency for creating new +Money+ object.
56
- self.default_currency = Currency.new("USD")
57
-
58
- # Default to using i18n
59
- self.use_i18n = true
60
-
61
- # Create a new money object with value 0.
62
- #
63
- # @param [Currency, String, Symbol] currency The currency to use.
64
- #
65
- # @return [Money]
66
- #
67
- # @example
68
- # Money.empty #=> #<Money @cents=0>
69
- def self.empty(currency = default_currency)
70
- Money.new(0, currency)
71
- end
72
-
73
- # Creates a new Money object of the given value, using the Canadian
74
- # dollar currency.
75
- #
76
- # @param [Integer] cents The cents value.
77
- #
78
- # @return [Money]
79
- #
80
- # @example
81
- # n = Money.ca_dollar(100)
82
- # n.cents #=> 100
83
- # n.currency #=> #<Money::Currency id: cad>
84
- def self.ca_dollar(cents)
85
- Money.new(cents, "CAD")
86
- end
87
-
88
- # Creates a new Money object of the given value, using the American dollar
89
- # currency.
90
- #
91
- # @param [Integer] cents The cents value.
92
- #
93
- # @return [Money]
94
- #
95
- # @example
96
- # n = Money.us_dollar(100)
97
- # n.cents #=> 100
98
- # n.currency #=> #<Money::Currency id: usd>
99
- def self.us_dollar(cents)
100
- Money.new(cents, "USD")
101
- end
102
-
103
- # Creates a new Money object of the given value, using the Euro currency.
104
- #
105
- # @param [Integer] cents The cents value.
106
- #
107
- # @return [Money]
108
- #
109
- # @example
110
- # n = Money.euro(100)
111
- # n.cents #=> 100
112
- # n.currency #=> #<Money::Currency id: eur>
113
- def self.euro(cents)
114
- Money.new(cents, "EUR")
115
- end
116
-
117
-
118
- # Creates a new Money object of +amount+ value in dollars,
119
- # with given +currency+.
120
- #
121
- # The amount value is expressed in +dollars+
122
- # where the +dollar+ is the main monetary unit,
123
- # opposite to the subunit-based representation
124
- # used internally by this library called +cents+.
125
- #
126
- # @param [Numeric] amount The money amount, in dollars.
127
- # @param [Currency, String, Symbol] currency The currency format.
128
- # @param [Money::Bank::*] bank The exchange bank to use.
129
- #
130
- # @return [Money]
131
- #
132
- # @example
133
- # Money.new_with_dollars(100)
134
- # #=> #<Money @cents=10000 @currency="USD">
135
- # Money.new_with_dollars(100, "USD")
136
- # #=> #<Money @cents=10000 @currency="USD">
137
- # Money.new_with_dollars(100, "EUR")
138
- # #=> #<Money @cents=10000 @currency="EUR">
139
- #
140
- # @see Money.new
141
- #
142
- def self.new_with_dollars(amount, currency = Money.default_currency, bank = Money.default_bank)
143
- money = from_numeric(amount, currency)
144
- # Hack! You can't change a bank
145
- money.instance_variable_set("@bank", bank)
146
- money
147
- end
148
- # Adds a new exchange rate to the default bank and return the rate.
149
- #
150
- # @param [Currency, String, Symbol] from_currency Currency to exchange from.
151
- # @param [Currency, String, Symbol] to_currency Currency to exchange to.
152
- # @param [Numeric] rate Rate to exchange with.
153
- #
154
- # @return [Numeric]
155
- #
156
- # @example
157
- # Money.add_rate("USD", "CAD", 1.25) #=> 1.25
158
- def self.add_rate(from_currency, to_currency, rate)
159
- Money.default_bank.add_rate(from_currency, to_currency, rate)
160
- end
161
-
162
-
163
- # Creates a new Money object of +cents+ value in cents,
164
- # with given +currency+.
165
- #
166
- # Alternatively you can use the convenience
167
- # methods like {Money.ca_dollar} and {Money.us_dollar}.
168
- #
169
- # @param [Integer] cents The money amount, in cents.
170
- # @param [Currency, String, Symbol] currency The currency format.
171
- # @param [Money::Bank::*] bank The exchange bank to use.
172
- #
173
- # @return [Money]
174
- #
175
- # @example
176
- # Money.new(100)
177
- # #=> #<Money @cents=100 @currency="USD">
178
- # Money.new(100, "USD")
179
- # #=> #<Money @cents=100 @currency="USD">
180
- # Money.new(100, "EUR")
181
- # #=> #<Money @cents=100 @currency="EUR">
182
- #
183
- # @see Money.new_with_dollars
184
- #
185
- def initialize(cents, currency = Money.default_currency, bank = Money.default_bank)
186
- @cents = cents.round.to_i
187
- @currency = Currency.wrap(currency)
188
- @bank = bank
189
- end
190
-
191
- # Returns the value of the money in dollars,
192
- # instead of in cents.
193
- #
194
- # @return [Float]
195
- #
196
- # @example
197
- # Money.new(100).dollars # => 1.0
198
- # Money.new_with_dollars(1).dollar # => 1.0
199
- #
200
- # @see #to_f
201
- # @see #cents
202
- #
203
- def dollars
204
- to_f
205
- end
206
-
207
- # Return string representation of currency object
208
- #
209
- # @return [String]
210
- #
211
- # @example
212
- # Money.new(100, :USD).currency_as_string #=> "USD"
213
- def currency_as_string
214
- self.currency.to_s
215
- end
216
-
217
- # Set currency object using a string
218
- #
219
- # @param [String] val The currency string.
220
- #
221
- # @return [Money::Currency]
222
- #
223
- # @example
224
- # Money.new(100).currency_as_string("CAD") #=> #<Money::Currency id: cad>
225
- def currency_as_string=(val)
226
- @currency = Currency.wrap(val)
227
- end
228
-
229
- # Returns a Fixnum hash value based on the +cents+ and +currency+ attributes
230
- # in order to use functions like & (intersection), group_by, etc.
231
- #
232
- # @return [Fixnum]
233
- #
234
- # @example
235
- # Money.new(100).hash #=> 908351
236
- def hash
237
- [cents.hash, currency.hash].hash
238
- end
239
-
240
- # Uses +Currency#symbol+. If +nil+ is returned, defaults to "¤".
241
- #
242
- # @return [String]
243
- #
244
- # @example
245
- # Money.new(100, "USD").symbol #=> "$"
246
- def symbol
247
- currency.symbol || "¤"
248
- end
249
-
250
- # Returns the amount of money as a string.
251
- #
252
- # @return [String]
253
- #
254
- # @example
255
- # Money.ca_dollar(100).to_s #=> "1.00"
256
- def to_s
257
- unit, subunit = cents.abs.divmod(currency.subunit_to_unit).map{|o| o.to_s}
258
- if currency.decimal_places == 0
259
- return "-#{unit}" if cents < 0
260
- return unit
261
- end
262
- subunit = (("0" * currency.decimal_places) + subunit)[(-1*currency.decimal_places)..-1]
263
- return "-#{unit}#{decimal_mark}#{subunit}" if cents < 0
264
- "#{unit}#{decimal_mark}#{subunit}"
265
- end
266
-
267
- # Return the amount of money as a BigDecimal.
268
- #
269
- # @return [BigDecimal]
270
- #
271
- # @example
272
- # Money.us_dollar(100).to_d => BigDecimal.new("1.0")
273
- def to_d
274
- BigDecimal.new(cents.to_s) / BigDecimal.new(currency.subunit_to_unit.to_s)
275
- end
276
-
277
- # Return the amount of money as a float. Floating points cannot guarantee
278
- # precision. Therefore, this function should only be used when you no longer
279
- # need to represent currency or working with another system that requires
280
- # decimals.
281
- #
282
- # @return [Float]
283
- #
284
- # @example
285
- # Money.us_dollar(100).to_f => 1.0
286
- def to_f
287
- to_d.to_f
288
- end
289
-
290
- # Receive the amount of this money object in another Currency.
291
- #
292
- # @param [Currency, String, Symbol] other_currency Currency to exchange to.
293
- #
294
- # @return [Money]
295
- #
296
- # @example
297
- # Money.new(2000, "USD").exchange_to("EUR")
298
- # Money.new(2000, "USD").exchange_to(Currency.new("EUR"))
299
- def exchange_to(other_currency)
300
- other_currency = Currency.wrap(other_currency)
301
- @bank.exchange_with(self, other_currency)
302
- end
303
-
304
- # Receive a money object with the same amount as the current Money object
305
- # in american dollars.
306
- #
307
- # @return [Money]
308
- #
309
- # @example
310
- # n = Money.new(100, "CAD").as_us_dollar
311
- # n.currency #=> #<Money::Currency id: usd>
312
- def as_us_dollar
313
- exchange_to("USD")
314
- end
315
-
316
- # Receive a money object with the same amount as the current Money object
317
- # in canadian dollar.
318
- #
319
- # @return [Money]
320
- #
321
- # @example
322
- # n = Money.new(100, "USD").as_ca_dollar
323
- # n.currency #=> #<Money::Currency id: cad>
324
- def as_ca_dollar
325
- exchange_to("CAD")
326
- end
327
-
328
- # Receive a money object with the same amount as the current Money object
329
- # in euro.
330
- #
331
- # @return [Money]
332
- #
333
- # @example
334
- # n = Money.new(100, "USD").as_euro
335
- # n.currency #=> #<Money::Currency id: eur>
336
- def as_euro
337
- exchange_to("EUR")
338
- end
339
-
340
- # Conversation to +self+.
341
- #
342
- # @return [self]
343
- def to_money
344
- self
345
- end
346
-
347
- # Common inspect function
348
- #
349
- # @return [String]
350
- def inspect
351
- "#<Money cents:#{cents} currency:#{currency}>"
352
- end
353
-
354
- # Allocates money between different parties without loosing pennies.
355
- # After the mathmatically split has been performed, left over pennies will
356
- # be distributed round-robin amongst the parties. This means that parties
357
- # listed first will likely recieve more pennies then ones that are listed later
358
- #
359
- # @param [0.50, 0.25, 0.25] to give 50% of the cash to party1, 25% ot party2, and 25% to party3.
360
- #
361
- # @return [Array<Money, Money, Money>]
362
- #
363
- # @example
364
- # Money.new(5, "USD").allocate([0.3,0.7)) #=> [Money.new(2), Money.new(3)]
365
- # Money.new(100, "USD").allocate([0.33,0.33,0.33]) #=> [Money.new(34), Money.new(33), Money.new(33)]
366
- def allocate(splits)
367
- allocations = splits.inject(0.0) {|sum, i| sum += i }
368
- raise ArgumentError, "splits add to more then 100%" if (allocations - 1.0) > Float::EPSILON
369
-
370
- left_over = cents
371
-
372
- amounts = splits.collect do |ratio|
373
- fraction = (cents * ratio / allocations).floor
374
- left_over -= fraction
375
- fraction
376
- end
377
-
378
- left_over.times { |i| amounts[i % amounts.length] += 1 }
379
-
380
- return amounts.collect { |cents| Money.new(cents, currency) }
381
- end
382
-
383
- # Split money amongst parties evenly without loosing pennies.
384
- #
385
- # @param [2] number of parties.
386
- #
387
- # @return [Array<Money, Money, Money>]
388
- #
389
- # @example
390
- # Money.new(100, "USD").split(3) #=> [Money.new(34), Money.new(33), Money.new(33)]
391
- def split(num)
392
- raise ArgumentError, "need at least one party" if num < 1
393
- low = Money.new(cents / num)
394
- high = Money.new(low.cents + 1)
395
-
396
- remainder = cents % num
397
- result = []
398
-
399
- num.times do |index|
400
- result[index] = index < remainder ? high : low
401
- end
402
-
403
- return result
404
- end
405
- end
1
+ # encoding: utf-8
2
+ require 'money/bank/variable_exchange'
3
+ require 'money/money/arithmetic'
4
+ require 'money/money/parsing'
5
+ require 'money/money/formatting'
6
+
7
+ # Represents an amount of money in a given currency.
8
+ class Money
9
+ include Comparable
10
+ include Arithmetic
11
+ include Formatting
12
+ include Parsing
13
+
14
+ # The value of the money in cents.
15
+ #
16
+ # @return [Integer]
17
+ attr_reader :cents
18
+
19
+ # The currency the money is in.
20
+ #
21
+ # @return [Currency]
22
+ attr_reader :currency
23
+
24
+ # The +Money::Bank+ based object used to perform currency exchanges with.
25
+ #
26
+ # @return [Money::Bank::*]
27
+ attr_reader :bank
28
+
29
+ # Class Methods
30
+ class << self
31
+ # Each Money object is associated to a bank object, which is responsible
32
+ # for currency exchange. This property allows you to specify the default
33
+ # bank object. The default value for this property is an instance if
34
+ # +Bank::VariableExchange.+ It allows one to specify custom exchange rates.
35
+ #
36
+ # @return [Money::Bank::*]
37
+ attr_accessor :default_bank
38
+
39
+ # The default currency, which is used when +Money.new+ is called without an
40
+ # explicit currency argument. The default value is Currency.new("USD"). The
41
+ # value must be a valid +Money::Currency+ instance.
42
+ #
43
+ # @return [Money::Currency]
44
+ attr_accessor :default_currency
45
+
46
+ # Use this to disable i18n even if it's used by other objects in your app.
47
+ #
48
+ # @return [true,false]
49
+ attr_accessor :use_i18n
50
+ end
51
+
52
+ # Set the default bank for creating new +Money+ objects.
53
+ self.default_bank = Bank::VariableExchange.instance
54
+
55
+ # Set the default currency for creating new +Money+ object.
56
+ self.default_currency = Currency.new("USD")
57
+
58
+ # Default to using i18n
59
+ self.use_i18n = true
60
+
61
+ # Create a new money object with value 0.
62
+ #
63
+ # @param [Currency, String, Symbol] currency The currency to use.
64
+ #
65
+ # @return [Money]
66
+ #
67
+ # @example
68
+ # Money.empty #=> #<Money @cents=0>
69
+ def self.empty(currency = default_currency)
70
+ Money.new(0, currency)
71
+ end
72
+
73
+ # Creates a new Money object of the given value, using the Canadian
74
+ # dollar currency.
75
+ #
76
+ # @param [Integer] cents The cents value.
77
+ #
78
+ # @return [Money]
79
+ #
80
+ # @example
81
+ # n = Money.ca_dollar(100)
82
+ # n.cents #=> 100
83
+ # n.currency #=> #<Money::Currency id: cad>
84
+ def self.ca_dollar(cents)
85
+ Money.new(cents, "CAD")
86
+ end
87
+
88
+ # Creates a new Money object of the given value, using the American dollar
89
+ # currency.
90
+ #
91
+ # @param [Integer] cents The cents value.
92
+ #
93
+ # @return [Money]
94
+ #
95
+ # @example
96
+ # n = Money.us_dollar(100)
97
+ # n.cents #=> 100
98
+ # n.currency #=> #<Money::Currency id: usd>
99
+ def self.us_dollar(cents)
100
+ Money.new(cents, "USD")
101
+ end
102
+
103
+ # Creates a new Money object of the given value, using the Euro currency.
104
+ #
105
+ # @param [Integer] cents The cents value.
106
+ #
107
+ # @return [Money]
108
+ #
109
+ # @example
110
+ # n = Money.euro(100)
111
+ # n.cents #=> 100
112
+ # n.currency #=> #<Money::Currency id: eur>
113
+ def self.euro(cents)
114
+ Money.new(cents, "EUR")
115
+ end
116
+
117
+ # Creates a new Money object of +amount+ value in dollars,
118
+ # with given +currency+.
119
+ #
120
+ # The amount value is expressed in +dollars+
121
+ # where the +dollar+ is the main monetary unit,
122
+ # opposite to the subunit-based representation
123
+ # used internally by this library called +cents+.
124
+ #
125
+ # @param [Numeric] amount The money amount, in dollars.
126
+ # @param [Currency, String, Symbol] currency The currency format.
127
+ # @param [Money::Bank::*] bank The exchange bank to use.
128
+ #
129
+ # @return [Money]
130
+ #
131
+ # @example
132
+ # Money.new_with_dollars(100)
133
+ # #=> #<Money @cents=10000 @currency="USD">
134
+ # Money.new_with_dollars(100, "USD")
135
+ # #=> #<Money @cents=10000 @currency="USD">
136
+ # Money.new_with_dollars(100, "EUR")
137
+ # #=> #<Money @cents=10000 @currency="EUR">
138
+ #
139
+ # @see Money.new
140
+ #
141
+ def self.new_with_dollars(amount, currency = Money.default_currency, bank = Money.default_bank)
142
+ money = from_numeric(amount, currency)
143
+ # Hack! You can't change a bank
144
+ money.instance_variable_set("@bank", bank)
145
+ money
146
+ end
147
+
148
+ # Adds a new exchange rate to the default bank and return the rate.
149
+ #
150
+ # @param [Currency, String, Symbol] from_currency Currency to exchange from.
151
+ # @param [Currency, String, Symbol] to_currency Currency to exchange to.
152
+ # @param [Numeric] rate Rate to exchange with.
153
+ #
154
+ # @return [Numeric]
155
+ #
156
+ # @example
157
+ # Money.add_rate("USD", "CAD", 1.25) #=> 1.25
158
+ def self.add_rate(from_currency, to_currency, rate)
159
+ Money.default_bank.add_rate(from_currency, to_currency, rate)
160
+ end
161
+
162
+
163
+ # Creates a new Money object of +cents+ value in cents,
164
+ # with given +currency+.
165
+ #
166
+ # Alternatively you can use the convenience
167
+ # methods like {Money.ca_dollar} and {Money.us_dollar}.
168
+ #
169
+ # @param [Integer] cents The money amount, in cents.
170
+ # @param [Currency, String, Symbol] currency The currency format.
171
+ # @param [Money::Bank::*] bank The exchange bank to use.
172
+ #
173
+ # @return [Money]
174
+ #
175
+ # @example
176
+ # Money.new(100)
177
+ # #=> #<Money @cents=100 @currency="USD">
178
+ # Money.new(100, "USD")
179
+ # #=> #<Money @cents=100 @currency="USD">
180
+ # Money.new(100, "EUR")
181
+ # #=> #<Money @cents=100 @currency="EUR">
182
+ #
183
+ # @see Money.new_with_dollars
184
+ #
185
+ def initialize(cents, currency = Money.default_currency, bank = Money.default_bank)
186
+ @cents = cents.round.to_i
187
+ @currency = Currency.wrap(currency)
188
+ @bank = bank
189
+ end
190
+
191
+ # Returns the value of the money in dollars,
192
+ # instead of in cents.
193
+ #
194
+ # @return [Float]
195
+ #
196
+ # @example
197
+ # Money.new(100).dollars # => 1.0
198
+ # Money.new_with_dollars(1).dollar # => 1.0
199
+ #
200
+ # @see #to_f
201
+ # @see #cents
202
+ #
203
+ def dollars
204
+ to_f
205
+ end
206
+
207
+ # Return string representation of currency object
208
+ #
209
+ # @return [String]
210
+ #
211
+ # @example
212
+ # Money.new(100, :USD).currency_as_string #=> "USD"
213
+ def currency_as_string
214
+ currency.to_s
215
+ end
216
+
217
+ # Set currency object using a string
218
+ #
219
+ # @param [String] val The currency string.
220
+ #
221
+ # @return [Money::Currency]
222
+ #
223
+ # @example
224
+ # Money.new(100).currency_as_string("CAD") #=> #<Money::Currency id: cad>
225
+ def currency_as_string=(val)
226
+ @currency = Currency.wrap(val)
227
+ end
228
+
229
+ # Returns a Fixnum hash value based on the +cents+ and +currency+ attributes
230
+ # in order to use functions like & (intersection), group_by, etc.
231
+ #
232
+ # @return [Fixnum]
233
+ #
234
+ # @example
235
+ # Money.new(100).hash #=> 908351
236
+ def hash
237
+ [cents.hash, currency.hash].hash
238
+ end
239
+
240
+ # Uses +Currency#symbol+. If +nil+ is returned, defaults to "¤".
241
+ #
242
+ # @return [String]
243
+ #
244
+ # @example
245
+ # Money.new(100, "USD").symbol #=> "$"
246
+ def symbol
247
+ currency.symbol || "¤"
248
+ end
249
+
250
+ # Common inspect function
251
+ #
252
+ # @return [String]
253
+ def inspect
254
+ "#<Money cents:#{cents} currency:#{currency}>"
255
+ end
256
+
257
+ # Returns the amount of money as a string.
258
+ #
259
+ # @return [String]
260
+ #
261
+ # @example
262
+ # Money.ca_dollar(100).to_s #=> "1.00"
263
+ def to_s
264
+ unit, subunit = cents.abs.divmod(currency.subunit_to_unit).map{|o| o.to_s}
265
+ if currency.decimal_places == 0
266
+ return "-#{unit}" if cents < 0
267
+ return unit
268
+ end
269
+ subunit = (("0" * currency.decimal_places) + subunit)[(-1*currency.decimal_places)..-1]
270
+ return "-#{unit}#{decimal_mark}#{subunit}" if cents < 0
271
+ "#{unit}#{decimal_mark}#{subunit}"
272
+ end
273
+
274
+ # Return the amount of money as a BigDecimal.
275
+ #
276
+ # @return [BigDecimal]
277
+ #
278
+ # @example
279
+ # Money.us_dollar(100).to_d => BigDecimal.new("1.0")
280
+ def to_d
281
+ BigDecimal.new(cents.to_s) / BigDecimal.new(currency.subunit_to_unit.to_s)
282
+ end
283
+
284
+ # Return the amount of money as a float. Floating points cannot guarantee
285
+ # precision. Therefore, this function should only be used when you no longer
286
+ # need to represent currency or working with another system that requires
287
+ # decimals.
288
+ #
289
+ # @return [Float]
290
+ #
291
+ # @example
292
+ # Money.us_dollar(100).to_f => 1.0
293
+ def to_f
294
+ to_d.to_f
295
+ end
296
+
297
+ # Conversation to +self+.
298
+ #
299
+ # @return [self]
300
+ def to_money(given_currency = nil)
301
+ given_currency = Currency.wrap(given_currency) if given_currency
302
+ if given_currency.nil? || self.currency == given_currency
303
+ self
304
+ else
305
+ exchange_to(given_currency)
306
+ end
307
+ end
308
+
309
+ # Receive the amount of this money object in another Currency.
310
+ #
311
+ # @param [Currency, String, Symbol] other_currency Currency to exchange to.
312
+ #
313
+ # @return [Money]
314
+ #
315
+ # @example
316
+ # Money.new(2000, "USD").exchange_to("EUR")
317
+ # Money.new(2000, "USD").exchange_to(Currency.new("EUR"))
318
+ def exchange_to(other_currency)
319
+ other_currency = Currency.wrap(other_currency)
320
+ @bank.exchange_with(self, other_currency)
321
+ end
322
+
323
+ # Receive a money object with the same amount as the current Money object
324
+ # in american dollars.
325
+ #
326
+ # @return [Money]
327
+ #
328
+ # @example
329
+ # n = Money.new(100, "CAD").as_us_dollar
330
+ # n.currency #=> #<Money::Currency id: usd>
331
+ def as_us_dollar
332
+ exchange_to("USD")
333
+ end
334
+
335
+ # Receive a money object with the same amount as the current Money object
336
+ # in canadian dollar.
337
+ #
338
+ # @return [Money]
339
+ #
340
+ # @example
341
+ # n = Money.new(100, "USD").as_ca_dollar
342
+ # n.currency #=> #<Money::Currency id: cad>
343
+ def as_ca_dollar
344
+ exchange_to("CAD")
345
+ end
346
+
347
+ # Receive a money object with the same amount as the current Money object
348
+ # in euro.
349
+ #
350
+ # @return [Money]
351
+ #
352
+ # @example
353
+ # n = Money.new(100, "USD").as_euro
354
+ # n.currency #=> #<Money::Currency id: eur>
355
+ def as_euro
356
+ exchange_to("EUR")
357
+ end
358
+
359
+ # Allocates money between different parties without loosing pennies.
360
+ # After the mathmatically split has been performed, left over pennies will
361
+ # be distributed round-robin amongst the parties. This means that parties
362
+ # listed first will likely recieve more pennies then ones that are listed later
363
+ #
364
+ # @param [0.50, 0.25, 0.25] to give 50% of the cash to party1, 25% ot party2, and 25% to party3.
365
+ #
366
+ # @return [Array<Money, Money, Money>]
367
+ #
368
+ # @example
369
+ # Money.new(5, "USD").allocate([0.3,0.7)) #=> [Money.new(2), Money.new(3)]
370
+ # Money.new(100, "USD").allocate([0.33,0.33,0.33]) #=> [Money.new(34), Money.new(33), Money.new(33)]
371
+ def allocate(splits)
372
+ allocations = splits.inject(0.0) {|sum, i| sum += i }
373
+ raise ArgumentError, "splits add to more then 100%" if (allocations - 1.0) > Float::EPSILON
374
+
375
+ left_over = cents
376
+
377
+ amounts = splits.collect do |ratio|
378
+ fraction = (cents * ratio / allocations).floor
379
+ left_over -= fraction
380
+ fraction
381
+ end
382
+
383
+ left_over.times { |i| amounts[i % amounts.length] += 1 }
384
+
385
+ amounts.collect { |cents| Money.new(cents, currency) }
386
+ end
387
+
388
+ # Split money amongst parties evenly without loosing pennies.
389
+ #
390
+ # @param [2] number of parties.
391
+ #
392
+ # @return [Array<Money, Money, Money>]
393
+ #
394
+ # @example
395
+ # Money.new(100, "USD").split(3) #=> [Money.new(34), Money.new(33), Money.new(33)]
396
+ def split(num)
397
+ raise ArgumentError, "need at least one party" if num < 1
398
+ low = Money.new(cents / num)
399
+ high = Money.new(low.cents + 1)
400
+
401
+ remainder = cents % num
402
+ result = []
403
+
404
+ num.times do |index|
405
+ result[index] = index < remainder ? high : low
406
+ end
407
+
408
+ result
409
+ end
410
+
411
+ end