money 6.7.1 → 6.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -1
  3. data/.travis.yml +15 -6
  4. data/AUTHORS +5 -0
  5. data/CHANGELOG.md +98 -3
  6. data/Gemfile +13 -4
  7. data/LICENSE +2 -0
  8. data/README.md +64 -44
  9. data/config/currency_backwards_compatible.json +30 -0
  10. data/config/currency_iso.json +135 -59
  11. data/config/currency_non_iso.json +66 -2
  12. data/lib/money.rb +0 -13
  13. data/lib/money/bank/variable_exchange.rb +9 -22
  14. data/lib/money/currency.rb +33 -39
  15. data/lib/money/currency/heuristics.rb +1 -144
  16. data/lib/money/currency/loader.rb +1 -1
  17. data/lib/money/locale_backend/base.rb +7 -0
  18. data/lib/money/locale_backend/errors.rb +6 -0
  19. data/lib/money/locale_backend/i18n.rb +24 -0
  20. data/lib/money/locale_backend/legacy.rb +28 -0
  21. data/lib/money/money.rb +106 -139
  22. data/lib/money/money/allocation.rb +37 -0
  23. data/lib/money/money/arithmetic.rb +31 -28
  24. data/lib/money/money/constructors.rb +1 -2
  25. data/lib/money/money/formatter.rb +397 -0
  26. data/lib/money/money/formatting_rules.rb +120 -0
  27. data/lib/money/money/locale_backend.rb +20 -0
  28. data/lib/money/rates_store/memory.rb +1 -2
  29. data/lib/money/version.rb +1 -1
  30. data/money.gemspec +10 -16
  31. data/spec/bank/variable_exchange_spec.rb +7 -3
  32. data/spec/currency/heuristics_spec.rb +2 -153
  33. data/spec/currency_spec.rb +44 -3
  34. data/spec/locale_backend/i18n_spec.rb +62 -0
  35. data/spec/locale_backend/legacy_spec.rb +74 -0
  36. data/spec/money/allocation_spec.rb +130 -0
  37. data/spec/money/arithmetic_spec.rb +184 -90
  38. data/spec/money/constructors_spec.rb +0 -12
  39. data/spec/money/formatting_spec.rb +296 -179
  40. data/spec/money/locale_backend_spec.rb +14 -0
  41. data/spec/money_spec.rb +159 -26
  42. data/spec/rates_store/memory_spec.rb +13 -2
  43. data/spec/spec_helper.rb +2 -0
  44. data/spec/support/shared_examples/money_examples.rb +14 -0
  45. metadata +32 -40
  46. data/lib/money/money/formatting.rb +0 -418
@@ -0,0 +1,130 @@
1
+ # encoding: utf-8
2
+
3
+ describe Money::Allocation do
4
+ describe 'given number as argument' do
5
+ it 'raises an error when invalid argument is given' do
6
+ expect { described_class.generate(100, 0) }.to raise_error(ArgumentError)
7
+ expect { described_class.generate(100, -1) }.to raise_error(ArgumentError)
8
+ end
9
+
10
+ context 'whole amounts' do
11
+ it 'returns the amount when 1 is given' do
12
+ expect(described_class.generate(100, 1)).to eq([100])
13
+ end
14
+
15
+ it 'splits the amount into equal parts' do
16
+ expect(described_class.generate(100, 2)).to eq([50, 50])
17
+ expect(described_class.generate(100, 4)).to eq([25, 25, 25, 25])
18
+ expect(described_class.generate(100, 5)).to eq([20, 20, 20, 20, 20])
19
+ end
20
+
21
+ it 'does not loose pennies' do
22
+ expect(described_class.generate(5, 2)).to eq([3, 2])
23
+ expect(described_class.generate(2, 3)).to eq([1, 1, 0])
24
+ expect(described_class.generate(100, 3)).to eq([34, 33, 33])
25
+ expect(described_class.generate(100, 6)).to eq([17, 17, 17, 17, 16, 16])
26
+ end
27
+ end
28
+
29
+ context 'fractional amounts' do
30
+ it 'returns the amount when 1 is given' do
31
+ expect(described_class.generate(BigDecimal(100), 1, false)).to eq([BigDecimal(100)])
32
+ end
33
+
34
+ it 'splits the amount into equal parts' do
35
+ expect(described_class.generate(BigDecimal(100), 2, false)).to eq([50, 50])
36
+ expect(described_class.generate(BigDecimal(100), 4, false)).to eq([25, 25, 25, 25])
37
+ expect(described_class.generate(BigDecimal(100), 5, false)).to eq([20, 20, 20, 20, 20])
38
+ end
39
+
40
+ it 'splits the amount into equal fractions' do
41
+ expect(described_class.generate(BigDecimal(5), 2, false)).to eq([2.5, 2.5])
42
+ expect(described_class.generate(BigDecimal(5), 4, false)).to eq([1.25, 1.25, 1.25, 1.25])
43
+ end
44
+
45
+ it 'handles splits into repeating decimals' do
46
+ amount = BigDecimal(100)
47
+ parts = described_class.generate(amount, 3, false)
48
+
49
+ # Rounding due to inconsistent BigDecimal size in ruby compared to jruby. In reality the
50
+ # first 2 elements will look like the last one with a '5' at the end, compensating for a
51
+ # missing fraction
52
+ expect(parts.map { |x| x.round(10) }).to eq([
53
+ BigDecimal('33.3333333333'),
54
+ BigDecimal('33.3333333333'),
55
+ BigDecimal('33.3333333333')
56
+ ])
57
+ expect(parts.inject(0, :+)).to eq(amount)
58
+ end
59
+ end
60
+ end
61
+
62
+ describe 'given array as argument' do
63
+ it 'raises an error when invalid argument is given' do
64
+ expect { described_class.generate(100, []) }.to raise_error(ArgumentError)
65
+ end
66
+
67
+ context 'whole amounts' do
68
+ it 'returns the amount when array contains only one element' do
69
+ expect(described_class.generate(100, [1])).to eq([100])
70
+ expect(described_class.generate(100, [5])).to eq([100])
71
+ end
72
+
73
+ it 'splits the amount into whole parts respecting the order' do
74
+ expect(described_class.generate(100, [1, 1])).to eq([50, 50])
75
+ expect(described_class.generate(100, [1, 1, 2])).to eq([25, 25, 50])
76
+ expect(described_class.generate(100, [7, 3])).to eq([70, 30])
77
+ end
78
+
79
+ it 'accepts floats as arguments' do
80
+ expect(described_class.generate(100, [1.0, 1.0])).to eq([50, 50])
81
+ expect(described_class.generate(100, [0.1, 0.1, 0.2])).to eq([25, 25, 50])
82
+ expect(described_class.generate(100, [0.07, 0.03])).to eq([70, 30])
83
+ expect(described_class.generate(10, [0.1, 0.2, 0.1])).to eq([3, 5, 2])
84
+ end
85
+
86
+ it 'does not loose pennies' do
87
+ expect(described_class.generate(10, [1, 1, 2])).to eq([3, 2, 5])
88
+ expect(described_class.generate(100, [1, 1, 1])).to eq([34, 33, 33])
89
+ end
90
+ end
91
+
92
+ context 'fractional amounts' do
93
+ it 'returns the amount when array contains only one element' do
94
+ expect(described_class.generate(BigDecimal(100), [1], false)).to eq([100])
95
+ expect(described_class.generate(BigDecimal(100), [5], false)).to eq([100])
96
+ end
97
+
98
+ it 'splits the amount into whole parts respecting the order' do
99
+ expect(described_class.generate(BigDecimal(100), [1, 1], false)).to eq([50, 50])
100
+ expect(described_class.generate(BigDecimal(100), [1, 1, 2], false)).to eq([25, 25, 50])
101
+ expect(described_class.generate(BigDecimal(100), [7, 3], false)).to eq([70, 30])
102
+ end
103
+
104
+ it 'splits the amount proportionally to the given parts' do
105
+ expect(described_class.generate(BigDecimal(10), [1, 1, 2], false)).to eq([2.5, 2.5, 5])
106
+ expect(described_class.generate(BigDecimal(7), [1, 1], false)).to eq([3.5, 3.5])
107
+ end
108
+
109
+ it 'keeps the class of the splits the same as given amount' do
110
+ # Note that whole_amount is false but result is whole values
111
+ expect(described_class.generate(10, [1, 1, 2], false)).to eq([3, 2, 5])
112
+ end
113
+
114
+ it 'handles splits into repeating decimals' do
115
+ amount = BigDecimal(100)
116
+ parts = described_class.generate(amount, [1, 1, 1], false)
117
+
118
+ # Rounding due to inconsistent BigDecimal size in ruby compared to jruby. In reality the
119
+ # first 2 elements will look like the last one with a '5' at the end, compensating for a
120
+ # missing fraction
121
+ expect(parts.map { |x| x.round(10) }).to eq([
122
+ BigDecimal('33.3333333333'),
123
+ BigDecimal('33.3333333333'),
124
+ BigDecimal('33.3333333333')
125
+ ])
126
+ expect(parts.inject(0, :+)).to eq(amount)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -12,6 +12,8 @@ describe Money do
12
12
  special_money_class = Class.new(Money)
13
13
  expect(- special_money_class.new(10_00)).to be_a special_money_class
14
14
  end
15
+
16
+ it_behaves_like 'instance with custom bank', :-@
15
17
  end
16
18
 
17
19
  describe "#==" do
@@ -49,7 +51,7 @@ describe Money do
49
51
  it 'allows comparison with zero' do
50
52
  expect(Money.new(0, :usd)).to eq 0
51
53
  expect(Money.new(0, :usd)).to eq 0.0
52
- expect(Money.new(0, :usd)).to eq BigDecimal.new(0)
54
+ expect(Money.new(0, :usd)).to eq BigDecimal(0)
53
55
  expect(Money.new(1, :usd)).to_not eq 0
54
56
  end
55
57
 
@@ -68,6 +70,12 @@ describe Money do
68
70
  expect(Money.new(1_00, "USD").eql?(Money.new(99_00, "EUR"))).to be false
69
71
  end
70
72
 
73
+ it "returns true when their amounts are zero and currencies differ" do
74
+ expect(Money.new(0, "USD").eql?(Money.new(0, "EUR"))).to be true
75
+ expect(Money.new(0, "USD").eql?(Money.new(0, "USD"))).to be true
76
+ expect(Money.new(0, "AUD").eql?(Money.new(0, "EUR"))).to be true
77
+ end
78
+
71
79
  it "returns false if used to compare with an object that doesn't inherit from Money" do
72
80
  expect(Money.new(1_00, "USD").eql?(Object.new)).to be false
73
81
  expect(Money.new(1_00, "USD").eql?(Class)).to be false
@@ -129,6 +137,32 @@ describe Money do
129
137
  expect(Money.new(1_00) <=> /foo/).to be_nil
130
138
  end
131
139
 
140
+ context 'when conversions disallowed' do
141
+ around do |example|
142
+ begin
143
+ old_default_bank = Money.default_bank
144
+ Money.disallow_currency_conversion!
145
+ example.run
146
+ ensure
147
+ Money.default_bank = old_default_bank
148
+ end
149
+ end
150
+
151
+ context 'when currencies differ' do
152
+ context 'when both values are 1_00' do
153
+ it 'raises currency error' do
154
+ expect { Money.usd(1_00) <=> Money.gbp(1_00) }.to raise_error Money::Bank::DifferentCurrencyError
155
+ end
156
+ end
157
+
158
+ context 'when both values are 0' do
159
+ it 'considers them equal' do
160
+ expect(Money.usd(0) <=> Money.gbp(0)).to eq(0)
161
+ end
162
+ end
163
+ end
164
+ end
165
+
132
166
  it 'compares with numeric 0' do
133
167
  expect(Money.usd(1) < 0).to eq false
134
168
  expect(Money.usd(1) > 0.0).to eq true
@@ -175,7 +209,7 @@ describe Money do
175
209
  expect(Money.new(10_00, "USD") + other).to eq Money.new(19_00, "USD")
176
210
  end
177
211
 
178
- it "adds Fixnum 0 to money and returns the same ammount" do
212
+ it "adds Integer 0 to money and returns the same ammount" do
179
213
  expect(Money.new(10_00) + 0).to eq Money.new(10_00)
180
214
  end
181
215
 
@@ -183,6 +217,8 @@ describe Money do
183
217
  special_money_class = Class.new(Money)
184
218
  expect(special_money_class.new(10_00, "USD") + Money.new(90, "USD")).to be_a special_money_class
185
219
  end
220
+
221
+ it_behaves_like 'instance with custom bank', :+, Money.new(1)
186
222
  end
187
223
 
188
224
  describe "#-" do
@@ -196,7 +232,7 @@ describe Money do
196
232
  expect(Money.new(10_00, "USD") - other).to eq Money.new(1_00, "USD")
197
233
  end
198
234
 
199
- it "subtract Fixnum 0 to money and returns the same ammount" do
235
+ it "subtract Integer 0 to money and returns the same ammount" do
200
236
  expect(Money.new(10_00) - 0).to eq Money.new(10_00)
201
237
  end
202
238
 
@@ -204,15 +240,17 @@ describe Money do
204
240
  special_money_class = Class.new(Money)
205
241
  expect(special_money_class.new(10_00, "USD") - Money.new(90, "USD")).to be_a special_money_class
206
242
  end
243
+
244
+ it_behaves_like 'instance with custom bank', :-, Money.new(1)
207
245
  end
208
246
 
209
247
  describe "#*" do
210
- it "multiplies Money by Fixnum and returns Money" do
248
+ it "multiplies Money by Integer and returns Money" do
211
249
  ts = [
212
- {:a => Money.new( 10, :USD), :b => 4, :c => Money.new( 40, :USD)},
213
- {:a => Money.new( 10, :USD), :b => -4, :c => Money.new(-40, :USD)},
214
- {:a => Money.new(-10, :USD), :b => 4, :c => Money.new(-40, :USD)},
215
- {:a => Money.new(-10, :USD), :b => -4, :c => Money.new( 40, :USD)},
250
+ {a: Money.new( 10, :USD), b: 4, c: Money.new( 40, :USD)},
251
+ {a: Money.new( 10, :USD), b: -4, c: Money.new(-40, :USD)},
252
+ {a: Money.new(-10, :USD), b: 4, c: Money.new(-40, :USD)},
253
+ {a: Money.new(-10, :USD), b: -4, c: Money.new( 40, :USD)},
216
254
  ]
217
255
  ts.each do |t|
218
256
  expect(t[:a] * t[:b]).to eq t[:c]
@@ -235,15 +273,17 @@ describe Money do
235
273
  special_money_class = Class.new(Money)
236
274
  expect(special_money_class.new(10_00, "USD") * 2).to be_a special_money_class
237
275
  end
276
+
277
+ it_behaves_like 'instance with custom bank', :*, 1
238
278
  end
239
279
 
240
280
  describe "#/" do
241
- it "divides Money by Fixnum and returns Money" do
281
+ it "divides Money by Integer and returns Money" do
242
282
  ts = [
243
- {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3, :USD)},
244
- {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
245
- {:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-3, :USD)},
246
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
283
+ {a: Money.new( 13, :USD), b: 4, c: Money.new( 3, :USD)},
284
+ {a: Money.new( 13, :USD), b: -4, c: Money.new(-3, :USD)},
285
+ {a: Money.new(-13, :USD), b: 4, c: Money.new(-3, :USD)},
286
+ {a: Money.new(-13, :USD), b: -4, c: Money.new( 3, :USD)},
247
287
  ]
248
288
  ts.each do |t|
249
289
  expect(t[:a] / t[:b]).to eq t[:c]
@@ -295,10 +335,10 @@ describe Money do
295
335
 
296
336
  it "divides Money by Money (same currency) and returns Float" do
297
337
  ts = [
298
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => 3.25},
299
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => -3.25},
300
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => -3.25},
301
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
338
+ {a: Money.new( 13, :USD), b: Money.new( 4, :USD), c: 3.25},
339
+ {a: Money.new( 13, :USD), b: Money.new(-4, :USD), c: -3.25},
340
+ {a: Money.new(-13, :USD), b: Money.new( 4, :USD), c: -3.25},
341
+ {a: Money.new(-13, :USD), b: Money.new(-4, :USD), c: 3.25},
302
342
  ]
303
343
  ts.each do |t|
304
344
  expect(t[:a] / t[:b]).to eq t[:c]
@@ -307,10 +347,10 @@ describe Money do
307
347
 
308
348
  it "divides Money by Money (different currency) and returns Float" do
309
349
  ts = [
310
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => 1.625},
311
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => -1.625},
312
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => -1.625},
313
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
350
+ {a: Money.new( 13, :USD), b: Money.new( 4, :EUR), c: 1.625},
351
+ {a: Money.new( 13, :USD), b: Money.new(-4, :EUR), c: -1.625},
352
+ {a: Money.new(-13, :USD), b: Money.new( 4, :EUR), c: -1.625},
353
+ {a: Money.new(-13, :USD), b: Money.new(-4, :EUR), c: 1.625},
314
354
  ]
315
355
  ts.each do |t|
316
356
  expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
@@ -321,25 +361,27 @@ describe Money do
321
361
  context "with infinite_precision", :infinite_precision do
322
362
  it "uses BigDecimal division" do
323
363
  ts = [
324
- {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3.25, :USD)},
325
- {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3.25, :USD)},
326
- {:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-3.25, :USD)},
327
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3.25, :USD)},
364
+ {a: Money.new( 13, :USD), b: 4, c: Money.new( 3.25, :USD)},
365
+ {a: Money.new( 13, :USD), b: -4, c: Money.new(-3.25, :USD)},
366
+ {a: Money.new(-13, :USD), b: 4, c: Money.new(-3.25, :USD)},
367
+ {a: Money.new(-13, :USD), b: -4, c: Money.new( 3.25, :USD)},
328
368
  ]
329
369
  ts.each do |t|
330
370
  expect(t[:a] / t[:b]).to eq t[:c]
331
371
  end
332
372
  end
333
373
  end
374
+
375
+ it_behaves_like 'instance with custom bank', :/, 1
334
376
  end
335
377
 
336
378
  describe "#div" do
337
- it "divides Money by Fixnum and returns Money" do
379
+ it "divides Money by Integer and returns Money" do
338
380
  ts = [
339
- {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3, :USD)},
340
- {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
341
- {:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-3, :USD)},
342
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
381
+ {a: Money.new( 13, :USD), b: 4, c: Money.new( 3, :USD)},
382
+ {a: Money.new( 13, :USD), b: -4, c: Money.new(-3, :USD)},
383
+ {a: Money.new(-13, :USD), b: 4, c: Money.new(-3, :USD)},
384
+ {a: Money.new(-13, :USD), b: -4, c: Money.new( 3, :USD)},
343
385
  ]
344
386
  ts.each do |t|
345
387
  expect(t[:a].div(t[:b])).to eq t[:c]
@@ -348,10 +390,10 @@ describe Money do
348
390
 
349
391
  it "divides Money by Money (same currency) and returns Float" do
350
392
  ts = [
351
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => 3.25},
352
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => -3.25},
353
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => -3.25},
354
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
393
+ {a: Money.new( 13, :USD), b: Money.new( 4, :USD), c: 3.25},
394
+ {a: Money.new( 13, :USD), b: Money.new(-4, :USD), c: -3.25},
395
+ {a: Money.new(-13, :USD), b: Money.new( 4, :USD), c: -3.25},
396
+ {a: Money.new(-13, :USD), b: Money.new(-4, :USD), c: 3.25},
355
397
  ]
356
398
  ts.each do |t|
357
399
  expect(t[:a].div(t[:b])).to eq t[:c]
@@ -360,10 +402,10 @@ describe Money do
360
402
 
361
403
  it "divides Money by Money (different currency) and returns Float" do
362
404
  ts = [
363
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => 1.625},
364
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => -1.625},
365
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => -1.625},
366
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
405
+ {a: Money.new( 13, :USD), b: Money.new( 4, :EUR), c: 1.625},
406
+ {a: Money.new( 13, :USD), b: Money.new(-4, :EUR), c: -1.625},
407
+ {a: Money.new(-13, :USD), b: Money.new( 4, :EUR), c: -1.625},
408
+ {a: Money.new(-13, :USD), b: Money.new(-4, :EUR), c: 1.625},
367
409
  ]
368
410
  ts.each do |t|
369
411
  expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
@@ -374,10 +416,10 @@ describe Money do
374
416
  context "with infinite_precision", :infinite_precision do
375
417
  it "uses BigDecimal division" do
376
418
  ts = [
377
- {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3.25, :USD)},
378
- {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3.25, :USD)},
379
- {:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-3.25, :USD)},
380
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3.25, :USD)},
419
+ {a: Money.new( 13, :USD), b: 4, c: Money.new( 3.25, :USD)},
420
+ {a: Money.new( 13, :USD), b: -4, c: Money.new(-3.25, :USD)},
421
+ {a: Money.new(-13, :USD), b: 4, c: Money.new(-3.25, :USD)},
422
+ {a: Money.new(-13, :USD), b: -4, c: Money.new( 3.25, :USD)},
381
423
  ]
382
424
  ts.each do |t|
383
425
  expect(t[:a].div(t[:b])).to eq t[:c]
@@ -387,12 +429,12 @@ describe Money do
387
429
  end
388
430
 
389
431
  describe "#divmod" do
390
- it "calculates division and modulo with Fixnum" do
432
+ it "calculates division and modulo with Integer" do
391
433
  ts = [
392
- {:a => Money.new( 13, :USD), :b => 4, :c => [Money.new( 3, :USD), Money.new( 1, :USD)]},
393
- {:a => Money.new( 13, :USD), :b => -4, :c => [Money.new(-4, :USD), Money.new(-3, :USD)]},
394
- {:a => Money.new(-13, :USD), :b => 4, :c => [Money.new(-4, :USD), Money.new( 3, :USD)]},
395
- {:a => Money.new(-13, :USD), :b => -4, :c => [Money.new( 3, :USD), Money.new(-1, :USD)]},
434
+ {a: Money.new( 13, :USD), b: 4, c: [Money.new( 3, :USD), Money.new( 1, :USD)]},
435
+ {a: Money.new( 13, :USD), b: -4, c: [Money.new(-4, :USD), Money.new(-3, :USD)]},
436
+ {a: Money.new(-13, :USD), b: 4, c: [Money.new(-4, :USD), Money.new( 3, :USD)]},
437
+ {a: Money.new(-13, :USD), b: -4, c: [Money.new( 3, :USD), Money.new(-1, :USD)]},
396
438
  ]
397
439
  ts.each do |t|
398
440
  expect(t[:a].divmod(t[:b])).to eq t[:c]
@@ -401,10 +443,10 @@ describe Money do
401
443
 
402
444
  it "calculates division and modulo with Money (same currency)" do
403
445
  ts = [
404
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => [ 3, Money.new( 1, :USD)]},
405
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => [-4, Money.new(-3, :USD)]},
406
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => [-4, Money.new( 3, :USD)]},
407
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => [ 3, Money.new(-1, :USD)]},
446
+ {a: Money.new( 13, :USD), b: Money.new( 4, :USD), c: [ 3, Money.new( 1, :USD)]},
447
+ {a: Money.new( 13, :USD), b: Money.new(-4, :USD), c: [-4, Money.new(-3, :USD)]},
448
+ {a: Money.new(-13, :USD), b: Money.new( 4, :USD), c: [-4, Money.new( 3, :USD)]},
449
+ {a: Money.new(-13, :USD), b: Money.new(-4, :USD), c: [ 3, Money.new(-1, :USD)]},
408
450
  ]
409
451
  ts.each do |t|
410
452
  expect(t[:a].divmod(t[:b])).to eq t[:c]
@@ -413,10 +455,10 @@ describe Money do
413
455
 
414
456
  it "calculates division and modulo with Money (different currency)" do
415
457
  ts = [
416
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => [ 1, Money.new( 5, :USD)]},
417
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => [-2, Money.new(-3, :USD)]},
418
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => [-2, Money.new( 3, :USD)]},
419
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => [ 1, Money.new(-5, :USD)]},
458
+ {a: Money.new( 13, :USD), b: Money.new( 4, :EUR), c: [ 1, Money.new( 5, :USD)]},
459
+ {a: Money.new( 13, :USD), b: Money.new(-4, :EUR), c: [-2, Money.new(-3, :USD)]},
460
+ {a: Money.new(-13, :USD), b: Money.new( 4, :EUR), c: [-2, Money.new( 3, :USD)]},
461
+ {a: Money.new(-13, :USD), b: Money.new(-4, :EUR), c: [ 1, Money.new(-5, :USD)]},
420
462
  ]
421
463
  ts.each do |t|
422
464
  expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
@@ -427,10 +469,10 @@ describe Money do
427
469
  context "with infinite_precision", :infinite_precision do
428
470
  it "uses BigDecimal division" do
429
471
  ts = [
430
- {:a => Money.new( 13, :USD), :b => 4, :c => [Money.new( 3, :USD), Money.new( 1, :USD)]},
431
- {:a => Money.new( 13, :USD), :b => -4, :c => [Money.new(-4, :USD), Money.new(-3, :USD)]},
432
- {:a => Money.new(-13, :USD), :b => 4, :c => [Money.new(-4, :USD), Money.new( 3, :USD)]},
433
- {:a => Money.new(-13, :USD), :b => -4, :c => [Money.new( 3, :USD), Money.new(-1, :USD)]},
472
+ {a: Money.new( 13, :USD), b: 4, c: [Money.new( 3, :USD), Money.new( 1, :USD)]},
473
+ {a: Money.new( 13, :USD), b: -4, c: [Money.new(-4, :USD), Money.new(-3, :USD)]},
474
+ {a: Money.new(-13, :USD), b: 4, c: [Money.new(-4, :USD), Money.new( 3, :USD)]},
475
+ {a: Money.new(-13, :USD), b: -4, c: [Money.new( 3, :USD), Money.new(-1, :USD)]},
434
476
  ]
435
477
  ts.each do |t|
436
478
  expect(t[:a].divmod(t[:b])).to eq t[:c]
@@ -447,15 +489,18 @@ describe Money do
447
489
  special_money_class = Class.new(Money)
448
490
  expect(special_money_class.new(10_00, "USD").divmod(special_money_class.new(4_00)).last).to be_a special_money_class
449
491
  end
492
+
493
+ it_behaves_like 'instance with custom bank', :divmod, Money.new(1)
494
+ it_behaves_like 'instance with custom bank', :divmod, 1
450
495
  end
451
496
 
452
497
  describe "#modulo" do
453
- it "calculates modulo with Fixnum" do
498
+ it "calculates modulo with Integer" do
454
499
  ts = [
455
- {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
456
- {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
457
- {:a => Money.new(-13, :USD), :b => 4, :c => Money.new( 3, :USD)},
458
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
500
+ {a: Money.new( 13, :USD), b: 4, c: Money.new( 1, :USD)},
501
+ {a: Money.new( 13, :USD), b: -4, c: Money.new(-3, :USD)},
502
+ {a: Money.new(-13, :USD), b: 4, c: Money.new( 3, :USD)},
503
+ {a: Money.new(-13, :USD), b: -4, c: Money.new(-1, :USD)},
459
504
  ]
460
505
  ts.each do |t|
461
506
  expect(t[:a].modulo(t[:b])).to eq t[:c]
@@ -464,10 +509,10 @@ describe Money do
464
509
 
465
510
  it "calculates modulo with Money (same currency)" do
466
511
  ts = [
467
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
468
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-3, :USD)},
469
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 3, :USD)},
470
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
512
+ {a: Money.new( 13, :USD), b: Money.new( 4, :USD), c: Money.new( 1, :USD)},
513
+ {a: Money.new( 13, :USD), b: Money.new(-4, :USD), c: Money.new(-3, :USD)},
514
+ {a: Money.new(-13, :USD), b: Money.new( 4, :USD), c: Money.new( 3, :USD)},
515
+ {a: Money.new(-13, :USD), b: Money.new(-4, :USD), c: Money.new(-1, :USD)},
471
516
  ]
472
517
  ts.each do |t|
473
518
  expect(t[:a].modulo(t[:b])).to eq t[:c]
@@ -476,10 +521,10 @@ describe Money do
476
521
 
477
522
  it "calculates modulo with Money (different currency)" do
478
523
  ts = [
479
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
480
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-3, :USD)},
481
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 3, :USD)},
482
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
524
+ {a: Money.new( 13, :USD), b: Money.new( 4, :EUR), c: Money.new( 5, :USD)},
525
+ {a: Money.new( 13, :USD), b: Money.new(-4, :EUR), c: Money.new(-3, :USD)},
526
+ {a: Money.new(-13, :USD), b: Money.new( 4, :EUR), c: Money.new( 3, :USD)},
527
+ {a: Money.new(-13, :USD), b: Money.new(-4, :EUR), c: Money.new(-5, :USD)},
483
528
  ]
484
529
  ts.each do |t|
485
530
  expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
@@ -489,12 +534,12 @@ describe Money do
489
534
  end
490
535
 
491
536
  describe "#%" do
492
- it "calculates modulo with Fixnum" do
537
+ it "calculates modulo with Integer" do
493
538
  ts = [
494
- {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
495
- {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
496
- {:a => Money.new(-13, :USD), :b => 4, :c => Money.new( 3, :USD)},
497
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
539
+ {a: Money.new( 13, :USD), b: 4, c: Money.new( 1, :USD)},
540
+ {a: Money.new( 13, :USD), b: -4, c: Money.new(-3, :USD)},
541
+ {a: Money.new(-13, :USD), b: 4, c: Money.new( 3, :USD)},
542
+ {a: Money.new(-13, :USD), b: -4, c: Money.new(-1, :USD)},
498
543
  ]
499
544
  ts.each do |t|
500
545
  expect(t[:a] % t[:b]).to eq t[:c]
@@ -503,10 +548,10 @@ describe Money do
503
548
 
504
549
  it "calculates modulo with Money (same currency)" do
505
550
  ts = [
506
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
507
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-3, :USD)},
508
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 3, :USD)},
509
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
551
+ {a: Money.new( 13, :USD), b: Money.new( 4, :USD), c: Money.new( 1, :USD)},
552
+ {a: Money.new( 13, :USD), b: Money.new(-4, :USD), c: Money.new(-3, :USD)},
553
+ {a: Money.new(-13, :USD), b: Money.new( 4, :USD), c: Money.new( 3, :USD)},
554
+ {a: Money.new(-13, :USD), b: Money.new(-4, :USD), c: Money.new(-1, :USD)},
510
555
  ]
511
556
  ts.each do |t|
512
557
  expect(t[:a] % t[:b]).to eq t[:c]
@@ -515,10 +560,10 @@ describe Money do
515
560
 
516
561
  it "calculates modulo with Money (different currency)" do
517
562
  ts = [
518
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
519
- {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-3, :USD)},
520
- {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 3, :USD)},
521
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
563
+ {a: Money.new( 13, :USD), b: Money.new( 4, :EUR), c: Money.new( 5, :USD)},
564
+ {a: Money.new( 13, :USD), b: Money.new(-4, :EUR), c: Money.new(-3, :USD)},
565
+ {a: Money.new(-13, :USD), b: Money.new( 4, :EUR), c: Money.new( 3, :USD)},
566
+ {a: Money.new(-13, :USD), b: Money.new(-4, :EUR), c: Money.new(-5, :USD)},
522
567
  ]
523
568
  ts.each do |t|
524
569
  expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
@@ -528,17 +573,19 @@ describe Money do
528
573
  end
529
574
 
530
575
  describe "#remainder" do
531
- it "calculates remainder with Fixnum" do
576
+ it "calculates remainder with Integer" do
532
577
  ts = [
533
- {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
534
- {:a => Money.new( 13, :USD), :b => -4, :c => Money.new( 1, :USD)},
535
- {:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-1, :USD)},
536
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
578
+ {a: Money.new( 13, :USD), b: 4, c: Money.new( 1, :USD)},
579
+ {a: Money.new( 13, :USD), b: -4, c: Money.new( 1, :USD)},
580
+ {a: Money.new(-13, :USD), b: 4, c: Money.new(-1, :USD)},
581
+ {a: Money.new(-13, :USD), b: -4, c: Money.new(-1, :USD)},
537
582
  ]
538
583
  ts.each do |t|
539
584
  expect(t[:a].remainder(t[:b])).to eq t[:c]
540
585
  end
541
586
  end
587
+
588
+ it_behaves_like 'instance with custom bank', :remainder, -1
542
589
  end
543
590
 
544
591
  describe "#abs" do
@@ -552,6 +599,8 @@ describe Money do
552
599
  special_money_class = Class.new(Money)
553
600
  expect(special_money_class.new(-1).abs).to be_a special_money_class
554
601
  end
602
+
603
+ it_behaves_like 'instance with custom bank', :abs
555
604
  end
556
605
 
557
606
  describe "#zero?" do
@@ -582,6 +631,11 @@ describe Money do
582
631
  end
583
632
 
584
633
  describe "#coerce" do
634
+ it 'allows non-default currency money objects to be summed' do
635
+ result = 0 + Money.new(4, 'EUR') + Money.new(5, 'EUR')
636
+ expect(result).to eq Money.new(9, 'EUR')
637
+ end
638
+
585
639
  it "allows mathematical operations by coercing arguments" do
586
640
  result = 2 * Money.new(4, 'USD')
587
641
  expect(result).to eq Money.new(8, 'USD')
@@ -605,6 +659,16 @@ describe Money do
605
659
  }.to raise_exception(TypeError)
606
660
  end
607
661
 
662
+ it "allows subtraction from numeric zero" do
663
+ result = 0 - Money.new(4, 'USD')
664
+ expect(result).to eq Money.new(-4, 'USD')
665
+ end
666
+
667
+ it "allows addition from numeric zero" do
668
+ result = 0 + Money.new(4, 'USD')
669
+ expect(result).to eq Money.new(4, 'USD')
670
+ end
671
+
608
672
  it "treats multiplication as commutative" do
609
673
  expect {
610
674
  2 * Money.new(2, 'USD')
@@ -659,4 +723,34 @@ describe Money do
659
723
  }.to raise_exception(TypeError)
660
724
  end
661
725
  end
726
+
727
+ %w(+ - / divmod remainder).each do |op|
728
+ describe "##{op}" do
729
+ subject { ->(other = self.other) { instance.send(op, other) } }
730
+ let(:instance) { Money.usd(1) }
731
+
732
+ context 'when conversions disallowed' do
733
+ around do |ex|
734
+ begin
735
+ old = Money.default_bank
736
+ Money.disallow_currency_conversion!
737
+ ex.run
738
+ ensure
739
+ Money.default_bank = old
740
+ end
741
+ end
742
+
743
+ context 'and other is money with different currency' do
744
+ let(:other) { Money.gbp(1) }
745
+ it { should raise_error Money::Bank::DifferentCurrencyError }
746
+
747
+ context 'even for zero' do
748
+ let(:instance) { Money.usd(0) }
749
+ let(:other) { Money.gbp(0) }
750
+ it { should raise_error Money::Bank::DifferentCurrencyError }
751
+ end
752
+ end
753
+ end
754
+ end
755
+ end
662
756
  end