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