money 6.9.0 → 6.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +131 -3
  3. data/LICENSE +17 -17
  4. data/README.md +181 -71
  5. data/config/currency_backwards_compatible.json +65 -0
  6. data/config/currency_iso.json +119 -56
  7. data/config/currency_non_iso.json +35 -2
  8. data/lib/money/bank/variable_exchange.rb +22 -12
  9. data/lib/money/currency/loader.rb +15 -13
  10. data/lib/money/currency.rb +38 -39
  11. data/lib/money/locale_backend/base.rb +7 -0
  12. data/lib/money/locale_backend/currency.rb +11 -0
  13. data/lib/money/locale_backend/errors.rb +6 -0
  14. data/lib/money/locale_backend/i18n.rb +25 -0
  15. data/lib/money/locale_backend/legacy.rb +28 -0
  16. data/lib/money/money/allocation.rb +46 -0
  17. data/lib/money/money/arithmetic.rb +33 -15
  18. data/lib/money/money/constructors.rb +1 -2
  19. data/lib/money/money/formatter.rb +399 -0
  20. data/lib/money/money/formatting_rules.rb +142 -0
  21. data/lib/money/money/locale_backend.rb +22 -0
  22. data/lib/money/money.rb +235 -187
  23. data/lib/money/rates_store/memory.rb +24 -24
  24. data/lib/money/version.rb +1 -1
  25. data/money.gemspec +14 -8
  26. metadata +36 -56
  27. data/.coveralls.yml +0 -1
  28. data/.gitignore +0 -23
  29. data/.rspec +0 -1
  30. data/.travis.yml +0 -26
  31. data/AUTHORS +0 -126
  32. data/CONTRIBUTING.md +0 -17
  33. data/Gemfile +0 -16
  34. data/Rakefile +0 -17
  35. data/lib/money/money/formatting.rb +0 -426
  36. data/spec/bank/base_spec.rb +0 -79
  37. data/spec/bank/single_currency_spec.rb +0 -13
  38. data/spec/bank/variable_exchange_spec.rb +0 -265
  39. data/spec/currency/heuristics_spec.rb +0 -11
  40. data/spec/currency/loader_spec.rb +0 -19
  41. data/spec/currency_spec.rb +0 -359
  42. data/spec/money/arithmetic_spec.rb +0 -693
  43. data/spec/money/constructors_spec.rb +0 -103
  44. data/spec/money/formatting_spec.rb +0 -757
  45. data/spec/money_spec.rb +0 -778
  46. data/spec/rates_store/memory_spec.rb +0 -69
  47. data/spec/spec_helper.rb +0 -28
data/spec/money_spec.rb DELETED
@@ -1,778 +0,0 @@
1
- # encoding: utf-8
2
-
3
- describe Money do
4
- describe ".new" do
5
- let(:initializing_value) { 1 }
6
- subject(:money) { Money.new(initializing_value) }
7
-
8
- it "should be an instance of `Money::Bank::VariableExchange`" do
9
- expect(money.bank).to be Money::Bank::VariableExchange.instance
10
- end
11
-
12
- context 'given the initializing value is an integer' do
13
- let(:initializing_value) { Integer(1) }
14
- it 'stores the integer as the number of cents' do
15
- expect(money.cents).to eq initializing_value
16
- end
17
- end
18
-
19
- context 'given the initializing value is a float' do
20
- context 'and the value is 1.00' do
21
- let(:initializing_value) { 1.00 }
22
- it { is_expected.to eq Money.new(1) }
23
- end
24
-
25
- context 'and the value is 1.01' do
26
- let(:initializing_value) { 1.01 }
27
- it { is_expected.to eq Money.new(1) }
28
- end
29
-
30
- context 'and the value is 1.50' do
31
- let(:initializing_value) { 1.50 }
32
- it { is_expected.to eq Money.new(2) }
33
- end
34
- end
35
-
36
- context 'given the initializing value is a rational' do
37
- let(:initializing_value) { Rational(1) }
38
- it { is_expected.to eq Money.new(1) }
39
- end
40
-
41
- context 'given the initializing value is money' do
42
- let(:initializing_value) { Money.new(1_00, Money::Currency.new('NZD')) }
43
- it { is_expected.to eq initializing_value }
44
- end
45
-
46
- context "given the initializing value doesn't respond to .to_d" do
47
- let(:initializing_value) { :"1" }
48
- it { is_expected.to eq Money.new(1) }
49
- end
50
-
51
- context 'given a currency is not provided' do
52
- subject(:money) { Money.new(initializing_value) }
53
-
54
- it "should have the default currency" do
55
- expect(money.currency).to eq Money.default_currency
56
- end
57
- end
58
-
59
- context 'given a currency is provided' do
60
- subject(:money) { Money.new(initializing_value, currency) }
61
-
62
- context 'and the currency is NZD' do
63
- let(:currency) { Money::Currency.new('NZD') }
64
-
65
- it "should have NZD currency" do
66
- expect(money.currency).to eq Money::Currency.new('NZD')
67
- end
68
- end
69
-
70
- context 'and the currency is nil' do
71
- let(:currency) { nil }
72
-
73
- it "should have the default currency" do
74
- expect(money.currency).to eq Money.default_currency
75
- end
76
- end
77
- end
78
-
79
- context "with infinite_precision", :infinite_precision do
80
- context 'given the initializing value is 1.50' do
81
- let(:initializing_value) { 1.50 }
82
-
83
- it "should have the correct cents" do
84
- expect(money.cents).to eq BigDecimal('1.50')
85
- end
86
- end
87
- end
88
- end
89
-
90
- describe ".add_rate" do
91
- before do
92
- @default_bank = Money.default_bank
93
- Money.default_bank = Money::Bank::VariableExchange.new
94
- end
95
-
96
- after do
97
- Money.default_bank = @default_bank
98
- end
99
-
100
- it "saves rate into current bank" do
101
- Money.add_rate("EUR", "USD", 10)
102
- expect(Money.new(10_00, "EUR").exchange_to("USD")).to eq Money.new(100_00, "USD")
103
- end
104
- end
105
-
106
- describe ".disallow_currency_conversions!" do
107
- before do
108
- @default_bank = Money.default_bank
109
- end
110
-
111
- after do
112
- Money.default_bank = @default_bank
113
- end
114
-
115
- it "disallows conversions when doing money arithmetic" do
116
- Money.disallow_currency_conversion!
117
- expect { Money.new(100, "USD") + Money.new(100, "EUR") }.to raise_exception(Money::Bank::DifferentCurrencyError)
118
- end
119
- end
120
-
121
- describe ".from_amount" do
122
- it "accepts numeric values" do
123
- expect(Money.from_amount(1, "USD")).to eq Money.new(1_00, "USD")
124
- expect(Money.from_amount(1.0, "USD")).to eq Money.new(1_00, "USD")
125
- expect(Money.from_amount("1".to_d, "USD")).to eq Money.new(1_00, "USD")
126
- end
127
-
128
- it "raises ArgumentError with unsupported argument" do
129
- expect { Money.from_amount("1") }.to raise_error(ArgumentError)
130
- expect { Money.from_amount(Object.new) }.to raise_error(ArgumentError)
131
- end
132
-
133
- it "converts given amount to subunits according to currency" do
134
- expect(Money.from_amount(1, "USD")).to eq Money.new(1_00, "USD")
135
- expect(Money.from_amount(1, "TND")).to eq Money.new(1_000, "TND")
136
- expect(Money.from_amount(1, "JPY")).to eq Money.new(1, "JPY")
137
- end
138
-
139
- it "rounds the given amount to subunits" do
140
- expect(Money.from_amount(4.444, "USD").amount).to eq "4.44".to_d
141
- expect(Money.from_amount(5.555, "USD").amount).to eq "5.56".to_d
142
- expect(Money.from_amount(444.4, "JPY").amount).to eq "444".to_d
143
- expect(Money.from_amount(555.5, "JPY").amount).to eq "556".to_d
144
- end
145
-
146
- it "does not round the given amount when infinite_precision is set", :infinite_precision do
147
- expect(Money.from_amount(4.444, "USD").amount).to eq "4.444".to_d
148
- expect(Money.from_amount(5.555, "USD").amount).to eq "5.555".to_d
149
- expect(Money.from_amount(444.4, "JPY").amount).to eq "444.4".to_d
150
- expect(Money.from_amount(555.5, "JPY").amount).to eq "555.5".to_d
151
- end
152
-
153
- it "accepts an optional currency" do
154
- expect(Money.from_amount(1).currency).to eq Money.default_currency
155
- jpy = Money::Currency.wrap("JPY")
156
- expect(Money.from_amount(1, jpy).currency).to eq jpy
157
- expect(Money.from_amount(1, "JPY").currency).to eq jpy
158
- end
159
-
160
- it "accepts an optional bank" do
161
- expect(Money.from_amount(1).bank).to eq Money.default_bank
162
- bank = double "bank"
163
- expect(Money.from_amount(1, "USD", bank).bank).to eq bank
164
- end
165
-
166
- it 'rounds using rounding_mode' do
167
- expect(Money.from_amount(1.999).to_d).to eq 2
168
- expect(Money.rounding_mode(BigDecimal::ROUND_DOWN) do
169
- Money.from_amount(1.999).to_d
170
- end).to eq 1.99
171
- end
172
-
173
- context 'given a currency is provided' do
174
- context 'and the currency is nil' do
175
- let(:currency) { nil }
176
-
177
- it "should have the default currency" do
178
- expect(Money.from_amount(1, currency).currency).to eq Money.default_currency
179
- end
180
- end
181
- end
182
- end
183
-
184
- %w[cents pence].each do |units|
185
- describe "##{units}" do
186
- it "is a synonym of #fractional" do
187
- expectation = Money.new(0)
188
- def expectation.fractional
189
- "expectation"
190
- end
191
- expect(expectation.cents).to eq "expectation"
192
- end
193
- end
194
- end
195
-
196
- describe "#fractional" do
197
- it "returns the amount in fractional unit" do
198
- expect(Money.new(1_00).fractional).to eq 1_00
199
- end
200
-
201
- it "stores fractional as an integer regardless of what is passed into the constructor" do
202
- m = Money.new(100)
203
- expect(m.fractional).to eq 100
204
- expect(m.fractional).to be_a(Integer)
205
- end
206
-
207
- context "loading a serialized Money via YAML" do
208
-
209
- let(:serialized) { <<YAML
210
- !ruby/object:Money
211
- fractional: 249.5
212
- currency: !ruby/object:Money::Currency
213
- id: :eur
214
- priority: 2
215
- iso_code: EUR
216
- name: Euro
217
- symbol: €
218
- alternate_symbols: []
219
- subunit: Cent
220
- subunit_to_unit: 100
221
- symbol_first: true
222
- html_entity: ! '&#x20AC;'
223
- decimal_mark: ! ','
224
- thousands_separator: .
225
- iso_numeric: '978'
226
- mutex: !ruby/object:Mutex {}
227
- last_updated: 2012-11-23 20:41:47.454438399 +02:00
228
- YAML
229
- }
230
-
231
- it "uses BigDecimal when rounding" do
232
- m = YAML::load serialized
233
- expect(m).to be_a(Money)
234
- expect(m.class.infinite_precision).to be false
235
- expect(m.fractional).to eq 250 # 249.5 rounded up
236
- expect(m.fractional).to be_a(Integer)
237
- end
238
-
239
- it "is a BigDecimal when using infinite_precision", :infinite_precision do
240
- money = YAML::load serialized
241
- expect(money.fractional).to be_a BigDecimal
242
- end
243
- end
244
-
245
- context "user changes rounding_mode" do
246
- after do
247
- Money.rounding_mode = BigDecimal::ROUND_HALF_EVEN
248
- end
249
-
250
- context "with the setter" do
251
- it "respects the rounding_mode" do
252
- Money.rounding_mode = BigDecimal::ROUND_DOWN
253
- expect(Money.new(1.9).fractional).to eq 1
254
-
255
- Money.rounding_mode = BigDecimal::ROUND_UP
256
- expect(Money.new(1.1).fractional).to eq 2
257
- end
258
- end
259
-
260
- context "with a block" do
261
- it "respects the rounding_mode" do
262
- expect(Money.rounding_mode(BigDecimal::ROUND_DOWN) do
263
- Money.new(1.9).fractional
264
- end).to eq 1
265
-
266
- expect(Money.rounding_mode(BigDecimal::ROUND_UP) do
267
- Money.new(1.1).fractional
268
- end).to eq 2
269
-
270
- expect(Money.rounding_mode).to eq BigDecimal::ROUND_HALF_EVEN
271
- end
272
-
273
- it "works for multiplication within a block" do
274
- Money.rounding_mode(BigDecimal::ROUND_DOWN) do
275
- expect((Money.new(1_00) * "0.019".to_d).fractional).to eq 1
276
- end
277
-
278
- Money.rounding_mode(BigDecimal::ROUND_UP) do
279
- expect((Money.new(1_00) * "0.011".to_d).fractional).to eq 2
280
- end
281
-
282
- expect(Money.rounding_mode).to eq BigDecimal::ROUND_HALF_EVEN
283
- end
284
- end
285
- end
286
-
287
- context "with infinite_precision", :infinite_precision do
288
- it "returns the amount in fractional unit" do
289
- expect(Money.new(1_00).fractional).to eq BigDecimal("100")
290
- end
291
-
292
- it "stores in fractional unit as an integer regardless of what is passed into the constructor" do
293
- m = Money.new(100)
294
- expect(m.fractional).to eq BigDecimal("100")
295
- expect(m.fractional).to be_a(BigDecimal)
296
- end
297
- end
298
- end
299
-
300
- describe "#round_to_nearest_cash_value" do
301
- it "rounds to the nearest possible cash value" do
302
- money = Money.new(2350, "AED")
303
- expect(money.round_to_nearest_cash_value).to eq 2350
304
-
305
- money = Money.new(-2350, "AED")
306
- expect(money.round_to_nearest_cash_value).to eq(-2350)
307
-
308
- money = Money.new(2213, "AED")
309
- expect(money.round_to_nearest_cash_value).to eq 2225
310
-
311
- money = Money.new(-2213, "AED")
312
- expect(money.round_to_nearest_cash_value).to eq(-2225)
313
-
314
- money = Money.new(2212, "AED")
315
- expect(money.round_to_nearest_cash_value).to eq 2200
316
-
317
- money = Money.new(-2212, "AED")
318
- expect(money.round_to_nearest_cash_value).to eq(-2200)
319
-
320
- money = Money.new(178, "CHF")
321
- expect(money.round_to_nearest_cash_value).to eq 180
322
-
323
- money = Money.new(-178, "CHF")
324
- expect(money.round_to_nearest_cash_value).to eq(-180)
325
-
326
- money = Money.new(177, "CHF")
327
- expect(money.round_to_nearest_cash_value).to eq 175
328
-
329
- money = Money.new(-177, "CHF")
330
- expect(money.round_to_nearest_cash_value).to eq(-175)
331
-
332
- money = Money.new(175, "CHF")
333
- expect(money.round_to_nearest_cash_value).to eq 175
334
-
335
- money = Money.new(-175, "CHF")
336
- expect(money.round_to_nearest_cash_value).to eq(-175)
337
-
338
- money = Money.new(299, "USD")
339
- expect(money.round_to_nearest_cash_value).to eq 299
340
-
341
- money = Money.new(-299, "USD")
342
- expect(money.round_to_nearest_cash_value).to eq(-299)
343
-
344
- money = Money.new(300, "USD")
345
- expect(money.round_to_nearest_cash_value).to eq 300
346
-
347
- money = Money.new(-300, "USD")
348
- expect(money.round_to_nearest_cash_value).to eq(-300)
349
-
350
- money = Money.new(301, "USD")
351
- expect(money.round_to_nearest_cash_value).to eq 301
352
-
353
- money = Money.new(-301, "USD")
354
- expect(money.round_to_nearest_cash_value).to eq(-301)
355
- end
356
-
357
- it "raises an exception if smallest denomination is not defined" do
358
- money = Money.new(100, "XAG")
359
- expect {money.round_to_nearest_cash_value}.to raise_error(Money::UndefinedSmallestDenomination)
360
- end
361
-
362
- it "returns a Integer when infinite_precision is not set" do
363
- money = Money.new(100, "USD")
364
- expect(money.round_to_nearest_cash_value).to be_a Integer
365
- end
366
-
367
- it "returns a BigDecimal when infinite_precision is set", :infinite_precision do
368
- money = Money.new(100, "EUR")
369
- expect(money.round_to_nearest_cash_value).to be_a BigDecimal
370
- end
371
- end
372
-
373
- describe "#amount" do
374
- it "returns the amount of cents as dollars" do
375
- expect(Money.new(1_00).amount).to eq 1
376
- end
377
-
378
- it "respects :subunit_to_unit currency property" do
379
- expect(Money.new(1_00, "USD").amount).to eq 1
380
- expect(Money.new(1_000, "TND").amount).to eq 1
381
- expect(Money.new(1, "VUV").amount).to eq 1
382
- expect(Money.new(1, "CLP").amount).to eq 1
383
- end
384
-
385
- it "does not lose precision" do
386
- expect(Money.new(100_37).amount).to eq 100.37
387
- end
388
-
389
- it 'produces a BigDecimal' do
390
- expect(Money.new(1_00).amount).to be_a BigDecimal
391
- end
392
- end
393
-
394
- describe "#dollars" do
395
- it "is synonym of #amount" do
396
- m = Money.new(0)
397
-
398
- # Make a small expectation
399
- def m.amount
400
- 5
401
- end
402
-
403
- expect(m.dollars).to eq 5
404
- end
405
- end
406
-
407
- describe "#currency" do
408
- it "returns the currency object" do
409
- expect(Money.new(1_00, "USD").currency).to eq Money::Currency.new("USD")
410
- end
411
- end
412
-
413
- describe "#currency_as_string" do
414
- it "returns the iso_code of the currency object" do
415
- expect(Money.new(1_00, "USD").currency_as_string).to eq "USD"
416
- expect(Money.new(1_00, "EUR").currency_as_string).to eq "EUR"
417
- end
418
- end
419
-
420
- describe "#currency_as_string=" do
421
- it "sets the currency object using the provided string leaving cents intact" do
422
- money = Money.new(100_00, "USD")
423
-
424
- money.currency_as_string = "EUR"
425
- expect(money.currency).to eq Money::Currency.new("EUR")
426
- expect(money.cents).to eq 100_00
427
-
428
- money.currency_as_string = "YEN"
429
- expect(money.currency).to eq Money::Currency.new("YEN")
430
- expect(money.cents).to eq 100_00
431
- end
432
- end
433
-
434
- describe "#hash=" do
435
- it "returns the same value for equal objects" do
436
- expect(Money.new(1_00, "EUR").hash).to eq Money.new(1_00, "EUR").hash
437
- expect(Money.new(2_00, "USD").hash).to eq Money.new(2_00, "USD").hash
438
- expect(Money.new(1_00, "EUR").hash).not_to eq Money.new(2_00, "EUR").hash
439
- expect(Money.new(1_00, "EUR").hash).not_to eq Money.new(1_00, "USD").hash
440
- expect(Money.new(1_00, "EUR").hash).not_to eq Money.new(2_00, "USD").hash
441
- end
442
-
443
- it "can be used to return the intersection of Money object arrays" do
444
- intersection = [Money.new(1_00, "EUR"), Money.new(1_00, "USD")] & [Money.new(1_00, "EUR")]
445
- expect(intersection).to eq [Money.new(1_00, "EUR")]
446
- end
447
- end
448
-
449
- describe "#symbol" do
450
- it "works as documented" do
451
- currency = Money::Currency.new("EUR")
452
- expect(currency).to receive(:symbol).and_return("€")
453
- expect(Money.new(0, currency).symbol).to eq "€"
454
-
455
- currency = Money::Currency.new("EUR")
456
- expect(currency).to receive(:symbol).and_return(nil)
457
- expect(Money.new(0, currency).symbol).to eq "¤"
458
- end
459
- end
460
-
461
- describe "#to_s" do
462
- it "works as documented" do
463
- expect(Money.new(10_00).to_s).to eq "10.00"
464
- expect(Money.new(400_08).to_s).to eq "400.08"
465
- expect(Money.new(-237_43).to_s).to eq "-237.43"
466
- end
467
-
468
- it "respects :subunit_to_unit currency property" do
469
- expect(Money.new(10_00, "BHD").to_s).to eq "1.000"
470
- expect(Money.new(10_00, "CNY").to_s).to eq "10.00"
471
- end
472
-
473
- it "does not have decimal when :subunit_to_unit == 1" do
474
- expect(Money.new(10_00, "VUV").to_s).to eq "1000"
475
- end
476
-
477
- it "does not work when :subunit_to_unit == 5" do
478
- expect(Money.new(10_00, "MGA").to_s).to eq "200.0"
479
- end
480
-
481
- it "respects :decimal_mark" do
482
- expect(Money.new(10_00, "BRL").to_s).to eq "10,00"
483
- end
484
-
485
- context "with infinite_precision", :infinite_precision do
486
- it "shows fractional cents" do
487
- expect(Money.new(1.05, "USD").to_s).to eq "0.0105"
488
- end
489
-
490
- it "suppresses fractional cents when there is none" do
491
- expect(Money.new(1.0, "USD").to_s).to eq "0.01"
492
- end
493
-
494
- it "shows fractional if needed when :subunut_to_unit == 1" do
495
- expect(Money.new(10_00.1, "VUV").to_s).to eq "1000.1"
496
- end
497
- end
498
- end
499
-
500
- describe "#to_d" do
501
- it "works as documented" do
502
- decimal = Money.new(10_00).to_d
503
- expect(decimal).to be_a(BigDecimal)
504
- expect(decimal).to eq 10.0
505
- end
506
-
507
- it "respects :subunit_to_unit currency property" do
508
- decimal = Money.new(10_00, "BHD").to_d
509
- expect(decimal).to be_a(BigDecimal)
510
- expect(decimal).to eq 1.0
511
- end
512
-
513
- it "works with float :subunit_to_unit currency property" do
514
- money = Money.new(10_00, "BHD")
515
- allow(money.currency).to receive(:subunit_to_unit).and_return(1000.0)
516
-
517
- decimal = money.to_d
518
- expect(decimal).to be_a(BigDecimal)
519
- expect(decimal).to eq 1.0
520
- end
521
- end
522
-
523
- describe "#to_f" do
524
- it "works as documented" do
525
- expect(Money.new(10_00).to_f).to eq 10.0
526
- end
527
-
528
- it "respects :subunit_to_unit currency property" do
529
- expect(Money.new(10_00, "BHD").to_f).to eq 1.0
530
- end
531
- end
532
-
533
- describe "#to_i" do
534
- it "works as documented" do
535
- expect(Money.new(10_00).to_i).to eq 10
536
- end
537
-
538
- it "respects :subunit_to_unit currency property" do
539
- expect(Money.new(10_00, "BHD").to_i).to eq 1
540
- end
541
- end
542
-
543
- describe "#to_money" do
544
- it "works as documented" do
545
- money = Money.new(10_00, "DKK")
546
- expect(money).to eq money.to_money
547
- expect(money).to eq money.to_money("DKK")
548
- expect(money.bank).to receive(:exchange_with).with(Money.new(10_00, Money::Currency.new("DKK")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
549
- expect(money.to_money("EUR")).to eq Money.new(200_00, "EUR")
550
- end
551
- end
552
-
553
- describe "#exchange_to" do
554
- it "exchanges the amount via its exchange bank" do
555
- money = Money.new(100_00, "USD")
556
- expect(money.bank).to receive(:exchange_with).with(Money.new(100_00, Money::Currency.new("USD")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
557
- money.exchange_to("EUR")
558
- end
559
-
560
- it "exchanges the amount properly" do
561
- money = Money.new(100_00, "USD")
562
- expect(money.bank).to receive(:exchange_with).with(Money.new(100_00, Money::Currency.new("USD")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
563
- expect(money.exchange_to("EUR")).to eq Money.new(200_00, "EUR")
564
- end
565
-
566
- it 'uses the block given as rounding method' do
567
- money = Money.new(100_00, 'USD')
568
- expect(money.bank).to receive(:exchange_with).and_yield(300_00)
569
- expect { |block| money.exchange_to(Money::Currency.new('EUR'), &block) }.to yield_successive_args(300_00)
570
- end
571
-
572
- it "does no exchange when the currencies are the same" do
573
- money = Money.new(100_00, "USD")
574
- expect(money.bank).to_not receive(:exchange_with)
575
- expect(money.exchange_to("USD")).to eq money
576
- end
577
- end
578
-
579
- describe "#allocate" do
580
- it "takes no action when one gets all" do
581
- expect(Money.us_dollar(005).allocate([1.0])).to eq [Money.us_dollar(5)]
582
- end
583
-
584
- it "keeps currencies intact" do
585
- expect(Money.ca_dollar(005).allocate([1])).to eq [Money.ca_dollar(5)]
586
- end
587
-
588
- it "does not lose pennies" do
589
- moneys = Money.us_dollar(5).allocate([0.3, 0.7])
590
- expect(moneys[0]).to eq Money.us_dollar(2)
591
- expect(moneys[1]).to eq Money.us_dollar(3)
592
- end
593
-
594
- it "does not lose pennies" do
595
- moneys = Money.us_dollar(100).allocate([0.333, 0.333, 0.333])
596
- expect(moneys[0].cents).to eq 34
597
- expect(moneys[1].cents).to eq 33
598
- expect(moneys[2].cents).to eq 33
599
- end
600
-
601
- context "negative amount" do
602
- it "does not lose pennies" do
603
- moneys = Money.us_dollar(-100).allocate([0.333, 0.333, 0.333])
604
-
605
- expect(moneys[0].cents).to eq -34
606
- expect(moneys[1].cents).to eq -33
607
- expect(moneys[2].cents).to eq -33
608
- end
609
-
610
- it "allocates the same way as positive amounts" do
611
- ratios = [0.6667, 0.3333]
612
-
613
- expect(Money.us_dollar(10_00).allocate(ratios).map(&:fractional)).to eq([6_67, 3_33])
614
- expect(Money.us_dollar(-10_00).allocate(ratios).map(&:fractional)).to eq([-6_67, -3_33])
615
- end
616
- end
617
-
618
- it "requires total to be less then 1" do
619
- expect { Money.us_dollar(0.05).allocate([0.5, 0.6]) }.to raise_error(ArgumentError)
620
- end
621
-
622
- it "keeps subclasses intact" do
623
- special_money_class = Class.new(Money)
624
- expect(special_money_class.new(005).allocate([1]).first).to be_a special_money_class
625
- end
626
-
627
- context "with infinite_precision", :infinite_precision do
628
- it "allows for fractional cents allocation" do
629
- one_third = BigDecimal("1") / BigDecimal("3")
630
-
631
- moneys = Money.new(100).allocate([one_third, one_third, one_third])
632
- expect(moneys[0].cents).to eq one_third * BigDecimal("100")
633
- expect(moneys[1].cents).to eq one_third * BigDecimal("100")
634
- expect(moneys[2].cents).to eq one_third * BigDecimal("100")
635
- end
636
- end
637
- end
638
-
639
- describe "#split" do
640
- it "needs at least one party" do
641
- expect { Money.us_dollar(1).split(0) }.to raise_error(ArgumentError)
642
- expect { Money.us_dollar(1).split(-1) }.to raise_error(ArgumentError)
643
- end
644
-
645
- it "gives 1 cent to both people if we start with 2" do
646
- expect(Money.us_dollar(2).split(2)).to eq [Money.us_dollar(1), Money.us_dollar(1)]
647
- end
648
-
649
- it "may distribute no money to some parties if there isnt enough to go around" do
650
- expect(Money.us_dollar(2).split(3)).to eq [Money.us_dollar(1), Money.us_dollar(1), Money.us_dollar(0)]
651
- end
652
-
653
- it "does not lose pennies" do
654
- expect(Money.us_dollar(5).split(2)).to eq [Money.us_dollar(3), Money.us_dollar(2)]
655
- end
656
-
657
- it "splits a dollar" do
658
- moneys = Money.us_dollar(100).split(3)
659
- expect(moneys[0].cents).to eq 34
660
- expect(moneys[1].cents).to eq 33
661
- expect(moneys[2].cents).to eq 33
662
- end
663
-
664
- it "preserves the class in the result when using a subclass of Money" do
665
- special_money_class = Class.new(Money)
666
- expect(special_money_class.new(10_00).split(1).first).to be_a special_money_class
667
- end
668
-
669
- context "with infinite_precision", :infinite_precision do
670
- it "allows for splitting by fractional cents" do
671
- thirty_three_and_one_third = BigDecimal("100") / BigDecimal("3")
672
-
673
- moneys = Money.new(100).split(3)
674
- expect(moneys[0].cents).to eq thirty_three_and_one_third
675
- expect(moneys[1].cents).to eq thirty_three_and_one_third
676
- expect(moneys[2].cents).to eq thirty_three_and_one_third
677
- end
678
- end
679
- end
680
-
681
- describe "#round" do
682
-
683
- let(:money) { Money.new(15.75, 'NZD') }
684
- subject(:rounded) { money.round }
685
-
686
- context "without infinite_precision" do
687
- it "returns self (as it is already rounded)" do
688
- rounded = money.round
689
- expect(rounded).to be money
690
- expect(rounded.cents).to eq 16
691
- end
692
- end
693
-
694
- context "with infinite_precision", :infinite_precision do
695
- it "returns a different money" do
696
- expect(rounded).not_to be money
697
- end
698
-
699
- it "rounds the cents" do
700
- expect(rounded.cents).to eq 16
701
- end
702
-
703
- it "maintains the currency" do
704
- expect(rounded.currency).to eq Money::Currency.new('NZD')
705
- end
706
-
707
- it "uses a provided rounding strategy" do
708
- rounded = money.round(BigDecimal::ROUND_DOWN)
709
- expect(rounded.cents).to eq 15
710
- end
711
-
712
- context "when using a subclass of Money" do
713
- let(:special_money_class) { Class.new(Money) }
714
- let(:money) { special_money_class.new(15.75, 'NZD') }
715
-
716
- it "preserves the class in the result" do
717
- expect(rounded).to be_a special_money_class
718
- end
719
- end
720
- end
721
- end
722
-
723
- describe "#inspect" do
724
- it "reports the class name properly when using inheritance" do
725
- expect(Money.new(1).inspect).to start_with '#<Money'
726
- Subclass = Class.new(Money)
727
- expect(Subclass.new(1).inspect).to start_with '#<Subclass'
728
- end
729
- end
730
-
731
- describe "#as_*" do
732
- before do
733
- Money.default_bank = Money::Bank::VariableExchange.new
734
- Money.add_rate("EUR", "USD", 1)
735
- Money.add_rate("EUR", "CAD", 1)
736
- Money.add_rate("USD", "EUR", 1)
737
- end
738
-
739
- after do
740
- Money.default_bank = Money::Bank::VariableExchange.instance
741
- end
742
-
743
- specify "as_us_dollar converts Money object to USD" do
744
- obj = Money.new(1, "EUR")
745
- expect(obj.as_us_dollar).to eq Money.new(1, "USD")
746
- end
747
-
748
- specify "as_ca_dollar converts Money object to CAD" do
749
- obj = Money.new(1, "EUR")
750
- expect(obj.as_ca_dollar).to eq Money.new(1, "CAD")
751
- end
752
-
753
- specify "as_euro converts Money object to EUR" do
754
- obj = Money.new(1, "USD")
755
- expect(obj.as_euro).to eq Money.new(1, "EUR")
756
- end
757
- end
758
-
759
- describe ".default_currency" do
760
- before do
761
- @default_currency = Money.default_currency
762
- end
763
-
764
- after do
765
- Money.default_currency = @default_currency
766
- end
767
-
768
- it "accepts a lambda" do
769
- Money.default_currency = lambda { :eur }
770
- expect(Money.default_currency).to eq Money::Currency.new(:eur)
771
- end
772
-
773
- it "accepts a symbol" do
774
- Money.default_currency = :eur
775
- expect(Money.default_currency).to eq Money::Currency.new(:eur)
776
- end
777
- end
778
- end