money 6.13.0 → 6.13.8

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