money 6.9.0 → 6.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +131 -3
  3. data/LICENSE +17 -17
  4. data/README.md +181 -71
  5. data/config/currency_backwards_compatible.json +65 -0
  6. data/config/currency_iso.json +119 -56
  7. data/config/currency_non_iso.json +35 -2
  8. data/lib/money/bank/variable_exchange.rb +22 -12
  9. data/lib/money/currency/loader.rb +15 -13
  10. data/lib/money/currency.rb +38 -39
  11. data/lib/money/locale_backend/base.rb +7 -0
  12. data/lib/money/locale_backend/currency.rb +11 -0
  13. data/lib/money/locale_backend/errors.rb +6 -0
  14. data/lib/money/locale_backend/i18n.rb +25 -0
  15. data/lib/money/locale_backend/legacy.rb +28 -0
  16. data/lib/money/money/allocation.rb +46 -0
  17. data/lib/money/money/arithmetic.rb +33 -15
  18. data/lib/money/money/constructors.rb +1 -2
  19. data/lib/money/money/formatter.rb +399 -0
  20. data/lib/money/money/formatting_rules.rb +142 -0
  21. data/lib/money/money/locale_backend.rb +22 -0
  22. data/lib/money/money.rb +235 -187
  23. data/lib/money/rates_store/memory.rb +24 -24
  24. data/lib/money/version.rb +1 -1
  25. data/money.gemspec +14 -8
  26. metadata +36 -56
  27. data/.coveralls.yml +0 -1
  28. data/.gitignore +0 -23
  29. data/.rspec +0 -1
  30. data/.travis.yml +0 -26
  31. data/AUTHORS +0 -126
  32. data/CONTRIBUTING.md +0 -17
  33. data/Gemfile +0 -16
  34. data/Rakefile +0 -17
  35. data/lib/money/money/formatting.rb +0 -426
  36. data/spec/bank/base_spec.rb +0 -79
  37. data/spec/bank/single_currency_spec.rb +0 -13
  38. data/spec/bank/variable_exchange_spec.rb +0 -265
  39. data/spec/currency/heuristics_spec.rb +0 -11
  40. data/spec/currency/loader_spec.rb +0 -19
  41. data/spec/currency_spec.rb +0 -359
  42. data/spec/money/arithmetic_spec.rb +0 -693
  43. data/spec/money/constructors_spec.rb +0 -103
  44. data/spec/money/formatting_spec.rb +0 -757
  45. data/spec/money_spec.rb +0 -778
  46. data/spec/rates_store/memory_spec.rb +0 -69
  47. data/spec/spec_helper.rb +0 -28
@@ -1,693 +0,0 @@
1
- # encoding: utf-8
2
-
3
- describe Money do
4
- describe "-@" do
5
- it "changes the sign of a number" do
6
- expect((- Money.new(0))).to eq Money.new(0)
7
- expect((- Money.new(1))).to eq Money.new(-1)
8
- expect((- Money.new(-1))).to eq Money.new(1)
9
- end
10
-
11
- it "preserves the class in the result when using a subclass of Money" do
12
- special_money_class = Class.new(Money)
13
- expect(- special_money_class.new(10_00)).to be_a special_money_class
14
- end
15
- end
16
-
17
- describe "#==" do
18
- it "returns true if both the amounts and currencies are equal" do
19
- expect(Money.new(1_00, "USD")).to eq Money.new(1_00, "USD")
20
- expect(Money.new(1_00, "USD")).not_to eq Money.new(1_00, "EUR")
21
- expect(Money.new(1_00, "USD")).not_to eq Money.new(2_00, "USD")
22
- expect(Money.new(1_00, "USD")).not_to eq Money.new(99_00, "EUR")
23
- end
24
-
25
- it "returns true if both amounts are zero, even if currency differs" do
26
- allow_any_instance_of(Money).to receive(:exchange_to) { Money.usd(0) }
27
- expect(Money.new(0, "USD")).to eq Money.new(0, "USD")
28
- expect(Money.new(0, "USD")).to eq Money.new(0, "EUR")
29
- expect(Money.new(0, "USD")).to eq Money.new(0, "AUD")
30
- expect(Money.new(0, "USD")).to eq Money.new(0, "JPY")
31
- end
32
-
33
- it "returns false if used to compare with an object that doesn't inherit from Money" do
34
- expect(Money.new(1_00, "USD")).not_to eq Object.new
35
- expect(Money.new(1_00, "USD")).not_to eq Class
36
- expect(Money.new(1_00, "USD")).not_to eq Kernel
37
- expect(Money.new(1_00, "USD")).not_to eq(/foo/)
38
- expect(Money.new(1_00, "USD")).not_to eq nil
39
- end
40
-
41
- it "can be used to compare with an object that inherits from Money" do
42
- klass = Class.new(Money)
43
-
44
- expect(Money.new(1_00, "USD")).to eq klass.new(1_00, "USD")
45
- expect(Money.new(2_50, "USD")).to eq klass.new(2_50, "USD")
46
- expect(Money.new(2_50, "USD")).not_to eq klass.new(3_00, "USD")
47
- expect(Money.new(1_00, "GBP")).not_to eq klass.new(1_00, "USD")
48
- end
49
-
50
- it 'allows comparison with zero' do
51
- expect(Money.new(0, :usd)).to eq 0
52
- expect(Money.new(0, :usd)).to eq 0.0
53
- expect(Money.new(0, :usd)).to eq BigDecimal.new(0)
54
- expect(Money.new(1, :usd)).to_not eq 0
55
- end
56
-
57
- it 'raises error for non-zero numerics' do
58
- expect { Money.new(1_00, :usd) == 1 }.to raise_error ArgumentError
59
- expect { Money.new(1_00, :usd) == -2.0 }.to raise_error ArgumentError
60
- expect { Money.new(1_00, :usd) == Float::INFINITY }.to raise_error ArgumentError
61
- end
62
- end
63
-
64
- describe "#eql?" do
65
- it "returns true if and only if their amount and currency are equal" do
66
- expect(Money.new(1_00, "USD").eql?(Money.new(1_00, "USD"))).to be true
67
- expect(Money.new(1_00, "USD").eql?(Money.new(1_00, "EUR"))).to be false
68
- expect(Money.new(1_00, "USD").eql?(Money.new(2_00, "USD"))).to be false
69
- expect(Money.new(1_00, "USD").eql?(Money.new(99_00, "EUR"))).to be false
70
- end
71
-
72
- it "returns false if used to compare with an object that doesn't inherit from Money" do
73
- expect(Money.new(1_00, "USD").eql?(Object.new)).to be false
74
- expect(Money.new(1_00, "USD").eql?(Class)).to be false
75
- expect(Money.new(1_00, "USD").eql?(Kernel)).to be false
76
- expect(Money.new(1_00, "USD").eql?(/foo/)).to be false
77
- expect(Money.new(1_00, "USD").eql?(nil)).to be false
78
- end
79
-
80
- it "can be used to compare with an object that inherits from Money" do
81
- klass = Class.new(Money)
82
-
83
- expect(Money.new(1_00, "USD").eql?(klass.new(1_00, "USD"))).to be true
84
- expect(Money.new(2_50, "USD").eql?(klass.new(2_50, "USD"))).to be true
85
- expect(Money.new(2_50, "USD").eql?(klass.new(3_00, "USD"))).to be false
86
- expect(Money.new(1_00, "GBP").eql?(klass.new(1_00, "USD"))).to be false
87
- end
88
- end
89
-
90
- describe "#<=>" do
91
- it "compares the two object amounts (same currency)" do
92
- expect((Money.new(1_00, "USD") <=> Money.new(1_00, "USD"))).to eq 0
93
- expect((Money.new(1_00, "USD") <=> Money.new(99, "USD"))).to be > 0
94
- expect((Money.new(1_00, "USD") <=> Money.new(2_00, "USD"))).to be < 0
95
- end
96
-
97
- it "converts other object amount to current currency, then compares the two object amounts (different currency)" do
98
- target = Money.new(200_00, "EUR")
99
- expect(target).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(300_00, "USD"))
100
- expect(Money.new(100_00, "USD") <=> target).to be < 0
101
-
102
- target = Money.new(200_00, "EUR")
103
- expect(target).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(100_00, "USD"))
104
- expect(Money.new(100_00, "USD") <=> target).to eq 0
105
-
106
- target = Money.new(200_00, "EUR")
107
- expect(target).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(99_00, "USD"))
108
- expect(Money.new(100_00, "USD") <=> target).to be > 0
109
- end
110
-
111
- it "returns nil if currency conversion fails, and therefore cannot be compared" do
112
- target = Money.new(200_00, "EUR")
113
- expect(target).to receive(:exchange_to).with(Money::Currency.new("USD")).and_raise(Money::Bank::UnknownRate)
114
- expect(Money.new(100_00, "USD") <=> target).to be_nil
115
- end
116
-
117
- it "can be used to compare with an object that inherits from Money" do
118
- klass = Class.new(Money)
119
-
120
- expect(Money.new(1_00) <=> klass.new(1_00)).to eq 0
121
- expect(Money.new(1_00) <=> klass.new(99)).to be > 0
122
- expect(Money.new(1_00) <=> klass.new(2_00)).to be < 0
123
- end
124
-
125
- it "returns nill when comparing with an object that doesn't inherit from Money" do
126
- expect(Money.new(1_00) <=> 100).to be_nil
127
- expect(Money.new(1_00) <=> Object.new).to be_nil
128
- expect(Money.new(1_00) <=> Class).to be_nil
129
- expect(Money.new(1_00) <=> Kernel).to be_nil
130
- expect(Money.new(1_00) <=> /foo/).to be_nil
131
- end
132
-
133
- it 'compares with numeric 0' do
134
- expect(Money.usd(1) < 0).to eq false
135
- expect(Money.usd(1) > 0.0).to eq true
136
- expect(Money.usd(0) >= 0.0).to eq true
137
- end
138
- end
139
-
140
- describe "#positive?" do
141
- it "returns true if the amount is greater than 0" do
142
- expect(Money.new(1)).to be_positive
143
- end
144
-
145
- it "returns false if the amount is 0" do
146
- expect(Money.new(0)).not_to be_positive
147
- end
148
-
149
- it "returns false if the amount is negative" do
150
- expect(Money.new(-1)).not_to be_positive
151
- end
152
- end
153
-
154
- describe "#negative?" do
155
- it "returns true if the amount is less than 0" do
156
- expect(Money.new(-1)).to be_negative
157
- end
158
-
159
- it "returns false if the amount is 0" do
160
- expect(Money.new(0)).not_to be_negative
161
- end
162
-
163
- it "returns false if the amount is greater than 0" do
164
- expect(Money.new(1)).not_to be_negative
165
- end
166
- end
167
-
168
- describe "#+" do
169
- it "adds other amount to current amount (same currency)" do
170
- expect(Money.new(10_00, "USD") + Money.new(90, "USD")).to eq Money.new(10_90, "USD")
171
- end
172
-
173
- it "converts other object amount to current currency and adds other amount to current amount (different currency)" do
174
- other = Money.new(90, "EUR")
175
- expect(other).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
176
- expect(Money.new(10_00, "USD") + other).to eq Money.new(19_00, "USD")
177
- end
178
-
179
- it "adds Integer 0 to money and returns the same ammount" do
180
- expect(Money.new(10_00) + 0).to eq Money.new(10_00)
181
- end
182
-
183
- it "preserves the class in the result when using a subclass of Money" do
184
- special_money_class = Class.new(Money)
185
- expect(special_money_class.new(10_00, "USD") + Money.new(90, "USD")).to be_a special_money_class
186
- end
187
- end
188
-
189
- describe "#-" do
190
- it "subtracts other amount from current amount (same currency)" do
191
- expect(Money.new(10_00, "USD") - Money.new(90, "USD")).to eq Money.new(9_10, "USD")
192
- end
193
-
194
- it "converts other object amount to current currency and subtracts other amount from current amount (different currency)" do
195
- other = Money.new(90, "EUR")
196
- expect(other).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
197
- expect(Money.new(10_00, "USD") - other).to eq Money.new(1_00, "USD")
198
- end
199
-
200
- it "subtract Integer 0 to money and returns the same ammount" do
201
- expect(Money.new(10_00) - 0).to eq Money.new(10_00)
202
- end
203
-
204
- it "preserves the class in the result when using a subclass of Money" do
205
- special_money_class = Class.new(Money)
206
- expect(special_money_class.new(10_00, "USD") - Money.new(90, "USD")).to be_a special_money_class
207
- end
208
- end
209
-
210
- describe "#*" do
211
- it "multiplies Money by Integer and returns Money" do
212
- ts = [
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)},
216
- {:a => Money.new(-10, :USD), :b => -4, :c => Money.new( 40, :USD)},
217
- ]
218
- ts.each do |t|
219
- expect(t[:a] * t[:b]).to eq t[:c]
220
- end
221
- end
222
-
223
- it "does not multiply Money by Money (same currency)" do
224
- expect { Money.new(10, :USD) * Money.new(4, :USD) }.to raise_error(TypeError)
225
- end
226
-
227
- it "does not multiply Money by Money (different currency)" do
228
- expect { Money.new(10, :USD) * Money.new(4, :EUR) }.to raise_error(TypeError)
229
- end
230
-
231
- it "does not multiply Money by an object which is NOT a number" do
232
- expect { Money.new(10, :USD) * 'abc' }.to raise_error(TypeError)
233
- end
234
-
235
- it "preserves the class in the result when using a subclass of Money" do
236
- special_money_class = Class.new(Money)
237
- expect(special_money_class.new(10_00, "USD") * 2).to be_a special_money_class
238
- end
239
- end
240
-
241
- describe "#/" do
242
- it "divides Money by Integer and returns Money" do
243
- ts = [
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)},
247
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
248
- ]
249
- ts.each do |t|
250
- expect(t[:a] / t[:b]).to eq t[:c]
251
- end
252
- end
253
-
254
- it "preserves the class in the result when using a subclass of Money" do
255
- special_money_class = Class.new(Money)
256
- expect(special_money_class.new(10_00, "USD") / 2).to be_a special_money_class
257
- end
258
-
259
- context 'rounding preference' do
260
- before do
261
- allow(Money).to receive(:rounding_mode).and_return(rounding_mode)
262
- end
263
-
264
- after do
265
- allow(Money).to receive(:rounding_mode).and_call_original
266
- end
267
-
268
- context 'ceiling rounding' do
269
- let(:rounding_mode) { BigDecimal::ROUND_CEILING }
270
- it "obeys the rounding preference" do
271
- expect(Money.new(10) / 3).to eq Money.new(4)
272
- end
273
- end
274
-
275
- context 'floor rounding' do
276
- let(:rounding_mode) { BigDecimal::ROUND_FLOOR }
277
- it "obeys the rounding preference" do
278
- expect(Money.new(10) / 6).to eq Money.new(1)
279
- end
280
- end
281
-
282
- context 'half up rounding' do
283
- let(:rounding_mode) { BigDecimal::ROUND_HALF_UP }
284
- it "obeys the rounding preference" do
285
- expect(Money.new(10) / 4).to eq Money.new(3)
286
- end
287
- end
288
-
289
- context 'half down rounding' do
290
- let(:rounding_mode) { BigDecimal::ROUND_HALF_DOWN }
291
- it "obeys the rounding preference" do
292
- expect(Money.new(10) / 4).to eq Money.new(2)
293
- end
294
- end
295
- end
296
-
297
- it "divides Money by Money (same currency) and returns Float" do
298
- ts = [
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},
302
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
303
- ]
304
- ts.each do |t|
305
- expect(t[:a] / t[:b]).to eq t[:c]
306
- end
307
- end
308
-
309
- it "divides Money by Money (different currency) and returns Float" do
310
- ts = [
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},
314
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
315
- ]
316
- ts.each do |t|
317
- expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
318
- expect(t[:a] / t[:b]).to eq t[:c]
319
- end
320
- end
321
-
322
- context "with infinite_precision", :infinite_precision do
323
- it "uses BigDecimal division" do
324
- ts = [
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)},
328
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3.25, :USD)},
329
- ]
330
- ts.each do |t|
331
- expect(t[:a] / t[:b]).to eq t[:c]
332
- end
333
- end
334
- end
335
- end
336
-
337
- describe "#div" do
338
- it "divides Money by Integer and returns Money" do
339
- ts = [
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)},
343
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
344
- ]
345
- ts.each do |t|
346
- expect(t[:a].div(t[:b])).to eq t[:c]
347
- end
348
- end
349
-
350
- it "divides Money by Money (same currency) and returns Float" do
351
- ts = [
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},
355
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
356
- ]
357
- ts.each do |t|
358
- expect(t[:a].div(t[:b])).to eq t[:c]
359
- end
360
- end
361
-
362
- it "divides Money by Money (different currency) and returns Float" do
363
- ts = [
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},
367
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
368
- ]
369
- ts.each do |t|
370
- expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
371
- expect(t[:a].div(t[:b])).to eq t[:c]
372
- end
373
- end
374
-
375
- context "with infinite_precision", :infinite_precision do
376
- it "uses BigDecimal division" do
377
- ts = [
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)},
381
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3.25, :USD)},
382
- ]
383
- ts.each do |t|
384
- expect(t[:a].div(t[:b])).to eq t[:c]
385
- end
386
- end
387
- end
388
- end
389
-
390
- describe "#divmod" do
391
- it "calculates division and modulo with Integer" do
392
- ts = [
393
- {:a => Money.new( 13, :USD), :b => 4, :c => [Money.new( 3, :USD), Money.new( 1, :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(-4, :USD), Money.new( 3, :USD)]},
396
- {:a => Money.new(-13, :USD), :b => -4, :c => [Money.new( 3, :USD), Money.new(-1, :USD)]},
397
- ]
398
- ts.each do |t|
399
- expect(t[:a].divmod(t[:b])).to eq t[:c]
400
- end
401
- end
402
-
403
- it "calculates division and modulo with Money (same currency)" do
404
- ts = [
405
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => [ 3, Money.new( 1, :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 => [-4, Money.new( 3, :USD)]},
408
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => [ 3, Money.new(-1, :USD)]},
409
- ]
410
- ts.each do |t|
411
- expect(t[:a].divmod(t[:b])).to eq t[:c]
412
- end
413
- end
414
-
415
- it "calculates division and modulo with Money (different currency)" do
416
- ts = [
417
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => [ 1, Money.new( 5, :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 => [-2, Money.new( 3, :USD)]},
420
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => [ 1, Money.new(-5, :USD)]},
421
- ]
422
- ts.each do |t|
423
- expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
424
- expect(t[:a].divmod(t[:b])).to eq t[:c]
425
- end
426
- end
427
-
428
- context "with infinite_precision", :infinite_precision do
429
- it "uses BigDecimal division" do
430
- ts = [
431
- {:a => Money.new( 13, :USD), :b => 4, :c => [Money.new( 3, :USD), Money.new( 1, :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(-4, :USD), Money.new( 3, :USD)]},
434
- {:a => Money.new(-13, :USD), :b => -4, :c => [Money.new( 3, :USD), Money.new(-1, :USD)]},
435
- ]
436
- ts.each do |t|
437
- expect(t[:a].divmod(t[:b])).to eq t[:c]
438
- end
439
- end
440
- end
441
-
442
- it "preserves the class in the result when dividing a subclass of Money by a fixnum" do
443
- special_money_class = Class.new(Money)
444
- expect(special_money_class.new(10_00, "USD").divmod(4).last).to be_a special_money_class
445
- end
446
-
447
- it "preserves the class in the result when using a subclass of Money by a subclass of Money" do
448
- special_money_class = Class.new(Money)
449
- expect(special_money_class.new(10_00, "USD").divmod(special_money_class.new(4_00)).last).to be_a special_money_class
450
- end
451
- end
452
-
453
- describe "#modulo" do
454
- it "calculates modulo with Integer" do
455
- ts = [
456
- {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :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( 3, :USD)},
459
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
460
- ]
461
- ts.each do |t|
462
- expect(t[:a].modulo(t[:b])).to eq t[:c]
463
- end
464
- end
465
-
466
- it "calculates modulo with Money (same currency)" do
467
- ts = [
468
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :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( 3, :USD)},
471
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
472
- ]
473
- ts.each do |t|
474
- expect(t[:a].modulo(t[:b])).to eq t[:c]
475
- end
476
- end
477
-
478
- it "calculates modulo with Money (different currency)" do
479
- ts = [
480
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :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( 3, :USD)},
483
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
484
- ]
485
- ts.each do |t|
486
- expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
487
- expect(t[:a].modulo(t[:b])).to eq t[:c]
488
- end
489
- end
490
- end
491
-
492
- describe "#%" do
493
- it "calculates modulo with Integer" do
494
- ts = [
495
- {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :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( 3, :USD)},
498
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
499
- ]
500
- ts.each do |t|
501
- expect(t[:a] % t[:b]).to eq t[:c]
502
- end
503
- end
504
-
505
- it "calculates modulo with Money (same currency)" do
506
- ts = [
507
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :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( 3, :USD)},
510
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
511
- ]
512
- ts.each do |t|
513
- expect(t[:a] % t[:b]).to eq t[:c]
514
- end
515
- end
516
-
517
- it "calculates modulo with Money (different currency)" do
518
- ts = [
519
- {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :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( 3, :USD)},
522
- {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
523
- ]
524
- ts.each do |t|
525
- expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
526
- expect(t[:a] % t[:b]).to eq t[:c]
527
- end
528
- end
529
- end
530
-
531
- describe "#remainder" do
532
- it "calculates remainder with Integer" do
533
- ts = [
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)},
537
- {:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
538
- ]
539
- ts.each do |t|
540
- expect(t[:a].remainder(t[:b])).to eq t[:c]
541
- end
542
- end
543
- end
544
-
545
- describe "#abs" do
546
- it "returns the absolute value as a new Money object" do
547
- n = Money.new(-1, :USD)
548
- expect(n.abs).to eq Money.new( 1, :USD)
549
- expect(n).to eq Money.new(-1, :USD)
550
- end
551
-
552
- it "preserves the class in the result when using a subclass of Money" do
553
- special_money_class = Class.new(Money)
554
- expect(special_money_class.new(-1).abs).to be_a special_money_class
555
- end
556
- end
557
-
558
- describe "#zero?" do
559
- it "returns whether the amount is 0" do
560
- expect(Money.new(0, "USD")).to be_zero
561
- expect(Money.new(0, "EUR")).to be_zero
562
- expect(Money.new(1, "USD")).not_to be_zero
563
- expect(Money.new(10, "YEN")).not_to be_zero
564
- expect(Money.new(-1, "EUR")).not_to be_zero
565
- end
566
- end
567
-
568
- describe "#nonzero?" do
569
- it "returns whether the amount is not 0" do
570
- expect(Money.new(0, "USD")).not_to be_nonzero
571
- expect(Money.new(0, "EUR")).not_to be_nonzero
572
- expect(Money.new(1, "USD")).to be_nonzero
573
- expect(Money.new(10, "YEN")).to be_nonzero
574
- expect(Money.new(-1, "EUR")).to be_nonzero
575
- end
576
-
577
- it "has the same return-value semantics as Numeric#nonzero?" do
578
- expect(Money.new(0, "USD").nonzero?).to be_nil
579
-
580
- money = Money.new(1, "USD")
581
- expect(money.nonzero?).to be_equal(money)
582
- end
583
- end
584
-
585
- describe "#coerce" do
586
- it "allows mathematical operations by coercing arguments" do
587
- result = 2 * Money.new(4, 'USD')
588
- expect(result).to eq Money.new(8, 'USD')
589
- end
590
-
591
- it "raises TypeError dividing by a Money (unless other is a Money)" do
592
- expect {
593
- 2 / Money.new(2, 'USD')
594
- }.to raise_exception(TypeError)
595
- end
596
-
597
- it "raises TypeError subtracting by a Money (unless other is a Money)" do
598
- expect {
599
- 2 - Money.new(2, 'USD')
600
- }.to raise_exception(TypeError)
601
- end
602
-
603
- it "raises TypeError adding by a Money (unless other is a Money)" do
604
- expect {
605
- 2 + Money.new(2, 'USD')
606
- }.to raise_exception(TypeError)
607
- end
608
-
609
- it "treats multiplication as commutative" do
610
- expect {
611
- 2 * Money.new(2, 'USD')
612
- }.to_not raise_exception
613
- result = 2 * Money.new(2, 'USD')
614
- expect(result).to eq(Money.new(4, 'USD'))
615
- end
616
-
617
- it "doesn't work with non-numerics" do
618
- expect {
619
- "2" * Money.new(2, 'USD')
620
- }.to raise_exception(TypeError)
621
- end
622
-
623
- it "correctly handles <=>" do
624
- expect {
625
- 2 < Money.new(2, 'USD')
626
- }.to raise_exception(ArgumentError)
627
-
628
- expect {
629
- 2 > Money.new(2, 'USD')
630
- }.to raise_exception(ArgumentError)
631
-
632
- expect {
633
- 2 <= Money.new(2, 'USD')
634
- }.to raise_exception(ArgumentError)
635
-
636
- expect {
637
- 2 >= Money.new(2, 'USD')
638
- }.to raise_exception(ArgumentError)
639
-
640
- expect(2 <=> Money.new(2, 'USD')).to be_nil
641
- end
642
-
643
- it 'compares with numeric 0' do
644
- expect(0 < Money.usd(1)).to eq true
645
- expect(0.0 > Money.usd(1)).to eq false
646
- expect(0.0 >= Money.usd(0)).to eq true
647
- end
648
-
649
- it "raises exceptions for all numeric types, not just Integer" do
650
- expect {
651
- 2.0 / Money.new(2, 'USD')
652
- }.to raise_exception(TypeError)
653
-
654
- expect {
655
- Rational(2,3) / Money.new(2, 'USD')
656
- }.to raise_exception(TypeError)
657
-
658
- expect {
659
- BigDecimal(2) / Money.new(2, 'USD')
660
- }.to raise_exception(TypeError)
661
- end
662
- end
663
-
664
- %w(+ - / <=> divmod remainder).each do |op|
665
- describe "##{op}" do
666
- subject { ->(other = self.other) { instance.send(op, other) } }
667
- let(:instance) { Money.usd(1) }
668
-
669
- context 'when conversions disallowed' do
670
- around do |ex|
671
- begin
672
- old = Money.default_bank
673
- Money.disallow_currency_conversion!
674
- ex.run
675
- ensure
676
- Money.default_bank = old
677
- end
678
- end
679
-
680
- context 'and other is money with different currency' do
681
- let(:other) { Money.gbp(1) }
682
- it { should raise_error Money::Bank::DifferentCurrencyError }
683
-
684
- context 'even for zero' do
685
- let(:instance) { Money.usd(0) }
686
- let(:other) { Money.gbp(0) }
687
- it { should raise_error Money::Bank::DifferentCurrencyError }
688
- end
689
- end
690
- end
691
- end
692
- end
693
- end