money 6.7.1 → 6.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -1
- data/.travis.yml +15 -6
- data/AUTHORS +5 -0
- data/CHANGELOG.md +98 -3
- data/Gemfile +13 -4
- data/LICENSE +2 -0
- data/README.md +64 -44
- data/config/currency_backwards_compatible.json +30 -0
- data/config/currency_iso.json +135 -59
- data/config/currency_non_iso.json +66 -2
- data/lib/money.rb +0 -13
- data/lib/money/bank/variable_exchange.rb +9 -22
- data/lib/money/currency.rb +33 -39
- data/lib/money/currency/heuristics.rb +1 -144
- data/lib/money/currency/loader.rb +1 -1
- data/lib/money/locale_backend/base.rb +7 -0
- data/lib/money/locale_backend/errors.rb +6 -0
- data/lib/money/locale_backend/i18n.rb +24 -0
- data/lib/money/locale_backend/legacy.rb +28 -0
- data/lib/money/money.rb +106 -139
- data/lib/money/money/allocation.rb +37 -0
- data/lib/money/money/arithmetic.rb +31 -28
- data/lib/money/money/constructors.rb +1 -2
- data/lib/money/money/formatter.rb +397 -0
- data/lib/money/money/formatting_rules.rb +120 -0
- data/lib/money/money/locale_backend.rb +20 -0
- data/lib/money/rates_store/memory.rb +1 -2
- data/lib/money/version.rb +1 -1
- data/money.gemspec +10 -16
- data/spec/bank/variable_exchange_spec.rb +7 -3
- data/spec/currency/heuristics_spec.rb +2 -153
- data/spec/currency_spec.rb +44 -3
- data/spec/locale_backend/i18n_spec.rb +62 -0
- data/spec/locale_backend/legacy_spec.rb +74 -0
- data/spec/money/allocation_spec.rb +130 -0
- data/spec/money/arithmetic_spec.rb +184 -90
- data/spec/money/constructors_spec.rb +0 -12
- data/spec/money/formatting_spec.rb +296 -179
- data/spec/money/locale_backend_spec.rb +14 -0
- data/spec/money_spec.rb +159 -26
- data/spec/rates_store/memory_spec.rb +13 -2
- data/spec/spec_helper.rb +2 -0
- data/spec/support/shared_examples/money_examples.rb +14 -0
- metadata +32 -40
- 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
|
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
|
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
|
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
|
248
|
+
it "multiplies Money by Integer and returns Money" do
|
211
249
|
ts = [
|
212
|
-
{:
|
213
|
-
{:
|
214
|
-
{:
|
215
|
-
{:
|
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
|
281
|
+
it "divides Money by Integer and returns Money" do
|
242
282
|
ts = [
|
243
|
-
{:
|
244
|
-
{:
|
245
|
-
{:
|
246
|
-
{:
|
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
|
-
{:
|
299
|
-
{:
|
300
|
-
{:
|
301
|
-
{:
|
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
|
-
{:
|
311
|
-
{:
|
312
|
-
{:
|
313
|
-
{:
|
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
|
-
{:
|
325
|
-
{:
|
326
|
-
{:
|
327
|
-
{:
|
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
|
379
|
+
it "divides Money by Integer and returns Money" do
|
338
380
|
ts = [
|
339
|
-
{:
|
340
|
-
{:
|
341
|
-
{:
|
342
|
-
{:
|
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
|
-
{:
|
352
|
-
{:
|
353
|
-
{:
|
354
|
-
{:
|
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
|
-
{:
|
364
|
-
{:
|
365
|
-
{:
|
366
|
-
{:
|
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
|
-
{:
|
378
|
-
{:
|
379
|
-
{:
|
380
|
-
{:
|
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
|
432
|
+
it "calculates division and modulo with Integer" do
|
391
433
|
ts = [
|
392
|
-
{:
|
393
|
-
{:
|
394
|
-
{:
|
395
|
-
{:
|
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
|
-
{:
|
405
|
-
{:
|
406
|
-
{:
|
407
|
-
{:
|
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
|
-
{:
|
417
|
-
{:
|
418
|
-
{:
|
419
|
-
{:
|
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
|
-
{:
|
431
|
-
{:
|
432
|
-
{:
|
433
|
-
{:
|
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
|
498
|
+
it "calculates modulo with Integer" do
|
454
499
|
ts = [
|
455
|
-
{:
|
456
|
-
{:
|
457
|
-
{:
|
458
|
-
{:
|
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
|
-
{:
|
468
|
-
{:
|
469
|
-
{:
|
470
|
-
{:
|
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
|
-
{:
|
480
|
-
{:
|
481
|
-
{:
|
482
|
-
{:
|
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
|
537
|
+
it "calculates modulo with Integer" do
|
493
538
|
ts = [
|
494
|
-
{:
|
495
|
-
{:
|
496
|
-
{:
|
497
|
-
{:
|
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
|
-
{:
|
507
|
-
{:
|
508
|
-
{:
|
509
|
-
{:
|
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
|
-
{:
|
519
|
-
{:
|
520
|
-
{:
|
521
|
-
{:
|
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
|
576
|
+
it "calculates remainder with Integer" do
|
532
577
|
ts = [
|
533
|
-
{:
|
534
|
-
{:
|
535
|
-
{:
|
536
|
-
{:
|
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
|