money 6.7.1 → 6.13.0

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 +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