money 6.13.0 → 6.13.8
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 +5 -5
- data/CHANGELOG.md +49 -2
- data/README.md +93 -3
- data/config/currency_backwards_compatible.json +31 -0
- data/config/currency_iso.json +18 -18
- data/lib/money/bank/variable_exchange.rb +14 -6
- data/lib/money/currency.rb +4 -4
- data/lib/money/currency/loader.rb +15 -13
- data/lib/money/locale_backend/currency.rb +11 -0
- data/lib/money/locale_backend/i18n.rb +2 -1
- data/lib/money/locale_backend/legacy.rb +2 -2
- data/lib/money/money.rb +100 -52
- data/lib/money/money/allocation.rb +8 -5
- data/lib/money/money/arithmetic.rb +20 -10
- data/lib/money/money/formatter.rb +3 -0
- data/lib/money/money/formatting_rules.rb +15 -5
- data/lib/money/money/locale_backend.rb +3 -1
- data/lib/money/rates_store/memory.rb +24 -23
- data/lib/money/version.rb +1 -1
- data/money.gemspec +4 -4
- metadata +11 -51
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -23
- data/.rspec +0 -2
- data/.travis.yml +0 -32
- data/AUTHORS +0 -131
- data/CONTRIBUTING.md +0 -17
- data/Gemfile +0 -16
- data/Rakefile +0 -17
- data/spec/bank/base_spec.rb +0 -79
- data/spec/bank/single_currency_spec.rb +0 -13
- data/spec/bank/variable_exchange_spec.rb +0 -265
- data/spec/currency/heuristics_spec.rb +0 -11
- data/spec/currency/loader_spec.rb +0 -19
- data/spec/currency_spec.rb +0 -400
- data/spec/locale_backend/i18n_spec.rb +0 -62
- data/spec/locale_backend/legacy_spec.rb +0 -74
- data/spec/money/allocation_spec.rb +0 -130
- data/spec/money/arithmetic_spec.rb +0 -756
- data/spec/money/constructors_spec.rb +0 -91
- data/spec/money/formatting_spec.rb +0 -855
- data/spec/money/locale_backend_spec.rb +0 -14
- data/spec/money_spec.rb +0 -880
- data/spec/rates_store/memory_spec.rb +0 -80
- data/spec/spec_helper.rb +0 -30
- data/spec/support/shared_examples/money_examples.rb +0 -14
@@ -1,62 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
describe Money::LocaleBackend::I18n do
|
4
|
-
describe '#initialize' do
|
5
|
-
it 'raises an error when I18n is not defined' do
|
6
|
-
hide_const('I18n')
|
7
|
-
|
8
|
-
expect { described_class.new }.to raise_error(Money::LocaleBackend::NotSupported)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
describe '#lookup' do
|
13
|
-
after do
|
14
|
-
reset_i18n
|
15
|
-
I18n.locale = :en
|
16
|
-
end
|
17
|
-
|
18
|
-
subject { described_class.new }
|
19
|
-
|
20
|
-
context 'with number.currency.format defined' do
|
21
|
-
before do
|
22
|
-
I18n.locale = :de
|
23
|
-
I18n.backend.store_translations(:de, number: {
|
24
|
-
currency: { format: { delimiter: '.', separator: ',' } }
|
25
|
-
})
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'returns thousands_separator based on the current locale' do
|
29
|
-
expect(subject.lookup(:thousands_separator, nil)).to eq('.')
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'returns decimal_mark based on the current locale' do
|
33
|
-
expect(subject.lookup(:decimal_mark, nil)).to eq(',')
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context 'with number.format defined' do
|
38
|
-
before do
|
39
|
-
I18n.locale = :de
|
40
|
-
I18n.backend.store_translations(:de, number: { format: { delimiter: '.', separator: ',' } })
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'returns thousands_separator based on the current locale' do
|
44
|
-
expect(subject.lookup(:thousands_separator, nil)).to eq('.')
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'returns decimal_mark based on the current locale' do
|
48
|
-
expect(subject.lookup(:decimal_mark, nil)).to eq(',')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context 'with no translation defined' do
|
53
|
-
it 'returns thousands_separator based on the current locale' do
|
54
|
-
expect(subject.lookup(:thousands_separator, nil)).to eq(nil)
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'returns decimal_mark based on the current locale' do
|
58
|
-
expect(subject.lookup(:decimal_mark, nil)).to eq(nil)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
describe Money::LocaleBackend::Legacy do
|
4
|
-
after { Money.use_i18n = true }
|
5
|
-
|
6
|
-
describe '#initialize' do
|
7
|
-
it 'raises an error when use_i18n is true and I18n is not defined' do
|
8
|
-
Money.use_i18n = true
|
9
|
-
hide_const('I18n')
|
10
|
-
|
11
|
-
expect { described_class.new }.to raise_error(Money::LocaleBackend::NotSupported)
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'does not raise error when use_i18n is false and I18n is not defined' do
|
15
|
-
Money.use_i18n = false
|
16
|
-
hide_const('I18n')
|
17
|
-
|
18
|
-
expect { described_class.new }.not_to raise_error
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe '#lookup' do
|
23
|
-
subject { described_class.new }
|
24
|
-
let(:currency) { Money::Currency.new('USD') }
|
25
|
-
|
26
|
-
context 'use_i18n is true and i18n lookup is successful' do
|
27
|
-
before do
|
28
|
-
allow(subject.send(:i18n_backend))
|
29
|
-
.to receive(:lookup)
|
30
|
-
.with(:thousands_separator, nil)
|
31
|
-
.and_return('.')
|
32
|
-
|
33
|
-
allow(subject.send(:i18n_backend))
|
34
|
-
.to receive(:lookup)
|
35
|
-
.with(:decimal_mark, nil)
|
36
|
-
.and_return(',')
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'returns thousands_separator from I18n' do
|
40
|
-
expect(subject.lookup(:thousands_separator, currency)).to eq('.')
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'returns decimal_mark based from I18n' do
|
44
|
-
expect(subject.lookup(:decimal_mark, currency)).to eq(',')
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
context 'use_i18n is true but i18n lookup is unsuccessful' do
|
49
|
-
before do
|
50
|
-
allow(subject.send(:i18n_backend)).to receive(:lookup).and_return(nil)
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'returns thousands_separator as defined in currency' do
|
54
|
-
expect(subject.lookup(:thousands_separator, currency)).to eq(',')
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'returns decimal_mark based as defined in currency' do
|
58
|
-
expect(subject.lookup(:decimal_mark, currency)).to eq('.')
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
context 'use_i18n is false' do
|
63
|
-
before { Money.use_i18n = false }
|
64
|
-
|
65
|
-
it 'returns thousands_separator as defined in currency' do
|
66
|
-
expect(subject.lookup(:thousands_separator, currency)).to eq(',')
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'returns decimal_mark based as defined in currency' do
|
70
|
-
expect(subject.lookup(:decimal_mark, currency)).to eq('.')
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
@@ -1,130 +0,0 @@
|
|
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
|
@@ -1,756 +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
|
-
|
16
|
-
it_behaves_like 'instance with custom bank', :-@
|
17
|
-
end
|
18
|
-
|
19
|
-
describe "#==" do
|
20
|
-
it "returns true if both the amounts and currencies are equal" do
|
21
|
-
expect(Money.new(1_00, "USD")).to eq Money.new(1_00, "USD")
|
22
|
-
expect(Money.new(1_00, "USD")).not_to eq Money.new(1_00, "EUR")
|
23
|
-
expect(Money.new(1_00, "USD")).not_to eq Money.new(2_00, "USD")
|
24
|
-
expect(Money.new(1_00, "USD")).not_to eq Money.new(99_00, "EUR")
|
25
|
-
end
|
26
|
-
|
27
|
-
it "returns true if both amounts are zero, even if currency differs" do
|
28
|
-
expect(Money.new(0, "USD")).to eq Money.new(0, "USD")
|
29
|
-
expect(Money.new(0, "USD")).to eq Money.new(0, "EUR")
|
30
|
-
expect(Money.new(0, "USD")).to eq Money.new(0, "AUD")
|
31
|
-
expect(Money.new(0, "USD")).to eq Money.new(0, "JPY")
|
32
|
-
end
|
33
|
-
|
34
|
-
it "returns false if used to compare with an object that doesn't inherit from Money" do
|
35
|
-
expect(Money.new(1_00, "USD")).not_to eq Object.new
|
36
|
-
expect(Money.new(1_00, "USD")).not_to eq Class
|
37
|
-
expect(Money.new(1_00, "USD")).not_to eq Kernel
|
38
|
-
expect(Money.new(1_00, "USD")).not_to eq(/foo/)
|
39
|
-
expect(Money.new(1_00, "USD")).not_to eq nil
|
40
|
-
end
|
41
|
-
|
42
|
-
it "can be used to compare with an object that inherits from Money" do
|
43
|
-
klass = Class.new(Money)
|
44
|
-
|
45
|
-
expect(Money.new(1_00, "USD")).to eq klass.new(1_00, "USD")
|
46
|
-
expect(Money.new(2_50, "USD")).to eq klass.new(2_50, "USD")
|
47
|
-
expect(Money.new(2_50, "USD")).not_to eq klass.new(3_00, "USD")
|
48
|
-
expect(Money.new(1_00, "GBP")).not_to eq klass.new(1_00, "USD")
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'allows comparison with zero' do
|
52
|
-
expect(Money.new(0, :usd)).to eq 0
|
53
|
-
expect(Money.new(0, :usd)).to eq 0.0
|
54
|
-
expect(Money.new(0, :usd)).to eq BigDecimal(0)
|
55
|
-
expect(Money.new(1, :usd)).to_not eq 0
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'raises error for non-zero numerics' do
|
59
|
-
expect { Money.new(1_00, :usd) == 1 }.to raise_error ArgumentError
|
60
|
-
expect { Money.new(1_00, :usd) == -2.0 }.to raise_error ArgumentError
|
61
|
-
expect { Money.new(1_00, :usd) == Float::INFINITY }.to raise_error ArgumentError
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
describe "#eql?" do
|
66
|
-
it "returns true if and only if their amount and currency are equal" do
|
67
|
-
expect(Money.new(1_00, "USD").eql?(Money.new(1_00, "USD"))).to be true
|
68
|
-
expect(Money.new(1_00, "USD").eql?(Money.new(1_00, "EUR"))).to be false
|
69
|
-
expect(Money.new(1_00, "USD").eql?(Money.new(2_00, "USD"))).to be false
|
70
|
-
expect(Money.new(1_00, "USD").eql?(Money.new(99_00, "EUR"))).to be false
|
71
|
-
end
|
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
|
-
|
79
|
-
it "returns false if used to compare with an object that doesn't inherit from Money" do
|
80
|
-
expect(Money.new(1_00, "USD").eql?(Object.new)).to be false
|
81
|
-
expect(Money.new(1_00, "USD").eql?(Class)).to be false
|
82
|
-
expect(Money.new(1_00, "USD").eql?(Kernel)).to be false
|
83
|
-
expect(Money.new(1_00, "USD").eql?(/foo/)).to be false
|
84
|
-
expect(Money.new(1_00, "USD").eql?(nil)).to be false
|
85
|
-
end
|
86
|
-
|
87
|
-
it "can be used to compare with an object that inherits from Money" do
|
88
|
-
klass = Class.new(Money)
|
89
|
-
|
90
|
-
expect(Money.new(1_00, "USD").eql?(klass.new(1_00, "USD"))).to be true
|
91
|
-
expect(Money.new(2_50, "USD").eql?(klass.new(2_50, "USD"))).to be true
|
92
|
-
expect(Money.new(2_50, "USD").eql?(klass.new(3_00, "USD"))).to be false
|
93
|
-
expect(Money.new(1_00, "GBP").eql?(klass.new(1_00, "USD"))).to be false
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
describe "#<=>" do
|
98
|
-
it "compares the two object amounts (same currency)" do
|
99
|
-
expect((Money.new(1_00, "USD") <=> Money.new(1_00, "USD"))).to eq 0
|
100
|
-
expect((Money.new(1_00, "USD") <=> Money.new(99, "USD"))).to be > 0
|
101
|
-
expect((Money.new(1_00, "USD") <=> Money.new(2_00, "USD"))).to be < 0
|
102
|
-
end
|
103
|
-
|
104
|
-
it "converts other object amount to current currency, then compares the two object amounts (different currency)" do
|
105
|
-
target = Money.new(200_00, "EUR")
|
106
|
-
expect(target).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(300_00, "USD"))
|
107
|
-
expect(Money.new(100_00, "USD") <=> target).to be < 0
|
108
|
-
|
109
|
-
target = Money.new(200_00, "EUR")
|
110
|
-
expect(target).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(100_00, "USD"))
|
111
|
-
expect(Money.new(100_00, "USD") <=> target).to eq 0
|
112
|
-
|
113
|
-
target = Money.new(200_00, "EUR")
|
114
|
-
expect(target).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(99_00, "USD"))
|
115
|
-
expect(Money.new(100_00, "USD") <=> target).to be > 0
|
116
|
-
end
|
117
|
-
|
118
|
-
it "returns nil if currency conversion fails, and therefore cannot be compared" do
|
119
|
-
target = Money.new(200_00, "EUR")
|
120
|
-
expect(target).to receive(:exchange_to).with(Money::Currency.new("USD")).and_raise(Money::Bank::UnknownRate)
|
121
|
-
expect(Money.new(100_00, "USD") <=> target).to be_nil
|
122
|
-
end
|
123
|
-
|
124
|
-
it "can be used to compare with an object that inherits from Money" do
|
125
|
-
klass = Class.new(Money)
|
126
|
-
|
127
|
-
expect(Money.new(1_00) <=> klass.new(1_00)).to eq 0
|
128
|
-
expect(Money.new(1_00) <=> klass.new(99)).to be > 0
|
129
|
-
expect(Money.new(1_00) <=> klass.new(2_00)).to be < 0
|
130
|
-
end
|
131
|
-
|
132
|
-
it "returns nill when comparing with an object that doesn't inherit from Money" do
|
133
|
-
expect(Money.new(1_00) <=> 100).to be_nil
|
134
|
-
expect(Money.new(1_00) <=> Object.new).to be_nil
|
135
|
-
expect(Money.new(1_00) <=> Class).to be_nil
|
136
|
-
expect(Money.new(1_00) <=> Kernel).to be_nil
|
137
|
-
expect(Money.new(1_00) <=> /foo/).to be_nil
|
138
|
-
end
|
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
|
-
|
166
|
-
it 'compares with numeric 0' do
|
167
|
-
expect(Money.usd(1) < 0).to eq false
|
168
|
-
expect(Money.usd(1) > 0.0).to eq true
|
169
|
-
expect(Money.usd(0) >= 0.0).to eq true
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
describe "#positive?" do
|
174
|
-
it "returns true if the amount is greater than 0" do
|
175
|
-
expect(Money.new(1)).to be_positive
|
176
|
-
end
|
177
|
-
|
178
|
-
it "returns false if the amount is 0" do
|
179
|
-
expect(Money.new(0)).not_to be_positive
|
180
|
-
end
|
181
|
-
|
182
|
-
it "returns false if the amount is negative" do
|
183
|
-
expect(Money.new(-1)).not_to be_positive
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
describe "#negative?" do
|
188
|
-
it "returns true if the amount is less than 0" do
|
189
|
-
expect(Money.new(-1)).to be_negative
|
190
|
-
end
|
191
|
-
|
192
|
-
it "returns false if the amount is 0" do
|
193
|
-
expect(Money.new(0)).not_to be_negative
|
194
|
-
end
|
195
|
-
|
196
|
-
it "returns false if the amount is greater than 0" do
|
197
|
-
expect(Money.new(1)).not_to be_negative
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
describe "#+" do
|
202
|
-
it "adds other amount to current amount (same currency)" do
|
203
|
-
expect(Money.new(10_00, "USD") + Money.new(90, "USD")).to eq Money.new(10_90, "USD")
|
204
|
-
end
|
205
|
-
|
206
|
-
it "converts other object amount to current currency and adds other amount to current amount (different currency)" do
|
207
|
-
other = Money.new(90, "EUR")
|
208
|
-
expect(other).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
|
209
|
-
expect(Money.new(10_00, "USD") + other).to eq Money.new(19_00, "USD")
|
210
|
-
end
|
211
|
-
|
212
|
-
it "adds Integer 0 to money and returns the same ammount" do
|
213
|
-
expect(Money.new(10_00) + 0).to eq Money.new(10_00)
|
214
|
-
end
|
215
|
-
|
216
|
-
it "preserves the class in the result when using a subclass of Money" do
|
217
|
-
special_money_class = Class.new(Money)
|
218
|
-
expect(special_money_class.new(10_00, "USD") + Money.new(90, "USD")).to be_a special_money_class
|
219
|
-
end
|
220
|
-
|
221
|
-
it_behaves_like 'instance with custom bank', :+, Money.new(1)
|
222
|
-
end
|
223
|
-
|
224
|
-
describe "#-" do
|
225
|
-
it "subtracts other amount from current amount (same currency)" do
|
226
|
-
expect(Money.new(10_00, "USD") - Money.new(90, "USD")).to eq Money.new(9_10, "USD")
|
227
|
-
end
|
228
|
-
|
229
|
-
it "converts other object amount to current currency and subtracts other amount from current amount (different currency)" do
|
230
|
-
other = Money.new(90, "EUR")
|
231
|
-
expect(other).to receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
|
232
|
-
expect(Money.new(10_00, "USD") - other).to eq Money.new(1_00, "USD")
|
233
|
-
end
|
234
|
-
|
235
|
-
it "subtract Integer 0 to money and returns the same ammount" do
|
236
|
-
expect(Money.new(10_00) - 0).to eq Money.new(10_00)
|
237
|
-
end
|
238
|
-
|
239
|
-
it "preserves the class in the result when using a subclass of Money" do
|
240
|
-
special_money_class = Class.new(Money)
|
241
|
-
expect(special_money_class.new(10_00, "USD") - Money.new(90, "USD")).to be_a special_money_class
|
242
|
-
end
|
243
|
-
|
244
|
-
it_behaves_like 'instance with custom bank', :-, Money.new(1)
|
245
|
-
end
|
246
|
-
|
247
|
-
describe "#*" do
|
248
|
-
it "multiplies Money by Integer and returns Money" do
|
249
|
-
ts = [
|
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)},
|
254
|
-
]
|
255
|
-
ts.each do |t|
|
256
|
-
expect(t[:a] * t[:b]).to eq t[:c]
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
it "does not multiply Money by Money (same currency)" do
|
261
|
-
expect { Money.new(10, :USD) * Money.new(4, :USD) }.to raise_error(TypeError)
|
262
|
-
end
|
263
|
-
|
264
|
-
it "does not multiply Money by Money (different currency)" do
|
265
|
-
expect { Money.new(10, :USD) * Money.new(4, :EUR) }.to raise_error(TypeError)
|
266
|
-
end
|
267
|
-
|
268
|
-
it "does not multiply Money by an object which is NOT a number" do
|
269
|
-
expect { Money.new(10, :USD) * 'abc' }.to raise_error(TypeError)
|
270
|
-
end
|
271
|
-
|
272
|
-
it "preserves the class in the result when using a subclass of Money" do
|
273
|
-
special_money_class = Class.new(Money)
|
274
|
-
expect(special_money_class.new(10_00, "USD") * 2).to be_a special_money_class
|
275
|
-
end
|
276
|
-
|
277
|
-
it_behaves_like 'instance with custom bank', :*, 1
|
278
|
-
end
|
279
|
-
|
280
|
-
describe "#/" do
|
281
|
-
it "divides Money by Integer and returns Money" do
|
282
|
-
ts = [
|
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)},
|
287
|
-
]
|
288
|
-
ts.each do |t|
|
289
|
-
expect(t[:a] / t[:b]).to eq t[:c]
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
it "preserves the class in the result when using a subclass of Money" do
|
294
|
-
special_money_class = Class.new(Money)
|
295
|
-
expect(special_money_class.new(10_00, "USD") / 2).to be_a special_money_class
|
296
|
-
end
|
297
|
-
|
298
|
-
context 'rounding preference' do
|
299
|
-
before do
|
300
|
-
allow(Money).to receive(:rounding_mode).and_return(rounding_mode)
|
301
|
-
end
|
302
|
-
|
303
|
-
after do
|
304
|
-
allow(Money).to receive(:rounding_mode).and_call_original
|
305
|
-
end
|
306
|
-
|
307
|
-
context 'ceiling rounding' do
|
308
|
-
let(:rounding_mode) { BigDecimal::ROUND_CEILING }
|
309
|
-
it "obeys the rounding preference" do
|
310
|
-
expect(Money.new(10) / 3).to eq Money.new(4)
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
context 'floor rounding' do
|
315
|
-
let(:rounding_mode) { BigDecimal::ROUND_FLOOR }
|
316
|
-
it "obeys the rounding preference" do
|
317
|
-
expect(Money.new(10) / 6).to eq Money.new(1)
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
context 'half up rounding' do
|
322
|
-
let(:rounding_mode) { BigDecimal::ROUND_HALF_UP }
|
323
|
-
it "obeys the rounding preference" do
|
324
|
-
expect(Money.new(10) / 4).to eq Money.new(3)
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
context 'half down rounding' do
|
329
|
-
let(:rounding_mode) { BigDecimal::ROUND_HALF_DOWN }
|
330
|
-
it "obeys the rounding preference" do
|
331
|
-
expect(Money.new(10) / 4).to eq Money.new(2)
|
332
|
-
end
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
it "divides Money by Money (same currency) and returns Float" do
|
337
|
-
ts = [
|
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},
|
342
|
-
]
|
343
|
-
ts.each do |t|
|
344
|
-
expect(t[:a] / t[:b]).to eq t[:c]
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
it "divides Money by Money (different currency) and returns Float" do
|
349
|
-
ts = [
|
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},
|
354
|
-
]
|
355
|
-
ts.each do |t|
|
356
|
-
expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
357
|
-
expect(t[:a] / t[:b]).to eq t[:c]
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
context "with infinite_precision", :infinite_precision do
|
362
|
-
it "uses BigDecimal division" do
|
363
|
-
ts = [
|
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)},
|
368
|
-
]
|
369
|
-
ts.each do |t|
|
370
|
-
expect(t[:a] / t[:b]).to eq t[:c]
|
371
|
-
end
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
it_behaves_like 'instance with custom bank', :/, 1
|
376
|
-
end
|
377
|
-
|
378
|
-
describe "#div" do
|
379
|
-
it "divides Money by Integer and returns Money" do
|
380
|
-
ts = [
|
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)},
|
385
|
-
]
|
386
|
-
ts.each do |t|
|
387
|
-
expect(t[:a].div(t[:b])).to eq t[:c]
|
388
|
-
end
|
389
|
-
end
|
390
|
-
|
391
|
-
it "divides Money by Money (same currency) and returns Float" do
|
392
|
-
ts = [
|
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},
|
397
|
-
]
|
398
|
-
ts.each do |t|
|
399
|
-
expect(t[:a].div(t[:b])).to eq t[:c]
|
400
|
-
end
|
401
|
-
end
|
402
|
-
|
403
|
-
it "divides Money by Money (different currency) and returns Float" do
|
404
|
-
ts = [
|
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},
|
409
|
-
]
|
410
|
-
ts.each do |t|
|
411
|
-
expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
412
|
-
expect(t[:a].div(t[:b])).to eq t[:c]
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
context "with infinite_precision", :infinite_precision do
|
417
|
-
it "uses BigDecimal division" do
|
418
|
-
ts = [
|
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)},
|
423
|
-
]
|
424
|
-
ts.each do |t|
|
425
|
-
expect(t[:a].div(t[:b])).to eq t[:c]
|
426
|
-
end
|
427
|
-
end
|
428
|
-
end
|
429
|
-
end
|
430
|
-
|
431
|
-
describe "#divmod" do
|
432
|
-
it "calculates division and modulo with Integer" do
|
433
|
-
ts = [
|
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)]},
|
438
|
-
]
|
439
|
-
ts.each do |t|
|
440
|
-
expect(t[:a].divmod(t[:b])).to eq t[:c]
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
it "calculates division and modulo with Money (same currency)" do
|
445
|
-
ts = [
|
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)]},
|
450
|
-
]
|
451
|
-
ts.each do |t|
|
452
|
-
expect(t[:a].divmod(t[:b])).to eq t[:c]
|
453
|
-
end
|
454
|
-
end
|
455
|
-
|
456
|
-
it "calculates division and modulo with Money (different currency)" do
|
457
|
-
ts = [
|
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)]},
|
462
|
-
]
|
463
|
-
ts.each do |t|
|
464
|
-
expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
465
|
-
expect(t[:a].divmod(t[:b])).to eq t[:c]
|
466
|
-
end
|
467
|
-
end
|
468
|
-
|
469
|
-
context "with infinite_precision", :infinite_precision do
|
470
|
-
it "uses BigDecimal division" do
|
471
|
-
ts = [
|
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)]},
|
476
|
-
]
|
477
|
-
ts.each do |t|
|
478
|
-
expect(t[:a].divmod(t[:b])).to eq t[:c]
|
479
|
-
end
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
it "preserves the class in the result when dividing a subclass of Money by a fixnum" do
|
484
|
-
special_money_class = Class.new(Money)
|
485
|
-
expect(special_money_class.new(10_00, "USD").divmod(4).last).to be_a special_money_class
|
486
|
-
end
|
487
|
-
|
488
|
-
it "preserves the class in the result when using a subclass of Money by a subclass of Money" do
|
489
|
-
special_money_class = Class.new(Money)
|
490
|
-
expect(special_money_class.new(10_00, "USD").divmod(special_money_class.new(4_00)).last).to be_a special_money_class
|
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
|
495
|
-
end
|
496
|
-
|
497
|
-
describe "#modulo" do
|
498
|
-
it "calculates modulo with Integer" do
|
499
|
-
ts = [
|
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)},
|
504
|
-
]
|
505
|
-
ts.each do |t|
|
506
|
-
expect(t[:a].modulo(t[:b])).to eq t[:c]
|
507
|
-
end
|
508
|
-
end
|
509
|
-
|
510
|
-
it "calculates modulo with Money (same currency)" do
|
511
|
-
ts = [
|
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)},
|
516
|
-
]
|
517
|
-
ts.each do |t|
|
518
|
-
expect(t[:a].modulo(t[:b])).to eq t[:c]
|
519
|
-
end
|
520
|
-
end
|
521
|
-
|
522
|
-
it "calculates modulo with Money (different currency)" do
|
523
|
-
ts = [
|
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)},
|
528
|
-
]
|
529
|
-
ts.each do |t|
|
530
|
-
expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
531
|
-
expect(t[:a].modulo(t[:b])).to eq t[:c]
|
532
|
-
end
|
533
|
-
end
|
534
|
-
end
|
535
|
-
|
536
|
-
describe "#%" do
|
537
|
-
it "calculates modulo with Integer" do
|
538
|
-
ts = [
|
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)},
|
543
|
-
]
|
544
|
-
ts.each do |t|
|
545
|
-
expect(t[:a] % t[:b]).to eq t[:c]
|
546
|
-
end
|
547
|
-
end
|
548
|
-
|
549
|
-
it "calculates modulo with Money (same currency)" do
|
550
|
-
ts = [
|
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)},
|
555
|
-
]
|
556
|
-
ts.each do |t|
|
557
|
-
expect(t[:a] % t[:b]).to eq t[:c]
|
558
|
-
end
|
559
|
-
end
|
560
|
-
|
561
|
-
it "calculates modulo with Money (different currency)" do
|
562
|
-
ts = [
|
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)},
|
567
|
-
]
|
568
|
-
ts.each do |t|
|
569
|
-
expect(t[:b]).to receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
570
|
-
expect(t[:a] % t[:b]).to eq t[:c]
|
571
|
-
end
|
572
|
-
end
|
573
|
-
end
|
574
|
-
|
575
|
-
describe "#remainder" do
|
576
|
-
it "calculates remainder with Integer" do
|
577
|
-
ts = [
|
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)},
|
582
|
-
]
|
583
|
-
ts.each do |t|
|
584
|
-
expect(t[:a].remainder(t[:b])).to eq t[:c]
|
585
|
-
end
|
586
|
-
end
|
587
|
-
|
588
|
-
it_behaves_like 'instance with custom bank', :remainder, -1
|
589
|
-
end
|
590
|
-
|
591
|
-
describe "#abs" do
|
592
|
-
it "returns the absolute value as a new Money object" do
|
593
|
-
n = Money.new(-1, :USD)
|
594
|
-
expect(n.abs).to eq Money.new( 1, :USD)
|
595
|
-
expect(n).to eq Money.new(-1, :USD)
|
596
|
-
end
|
597
|
-
|
598
|
-
it "preserves the class in the result when using a subclass of Money" do
|
599
|
-
special_money_class = Class.new(Money)
|
600
|
-
expect(special_money_class.new(-1).abs).to be_a special_money_class
|
601
|
-
end
|
602
|
-
|
603
|
-
it_behaves_like 'instance with custom bank', :abs
|
604
|
-
end
|
605
|
-
|
606
|
-
describe "#zero?" do
|
607
|
-
it "returns whether the amount is 0" do
|
608
|
-
expect(Money.new(0, "USD")).to be_zero
|
609
|
-
expect(Money.new(0, "EUR")).to be_zero
|
610
|
-
expect(Money.new(1, "USD")).not_to be_zero
|
611
|
-
expect(Money.new(10, "YEN")).not_to be_zero
|
612
|
-
expect(Money.new(-1, "EUR")).not_to be_zero
|
613
|
-
end
|
614
|
-
end
|
615
|
-
|
616
|
-
describe "#nonzero?" do
|
617
|
-
it "returns whether the amount is not 0" do
|
618
|
-
expect(Money.new(0, "USD")).not_to be_nonzero
|
619
|
-
expect(Money.new(0, "EUR")).not_to be_nonzero
|
620
|
-
expect(Money.new(1, "USD")).to be_nonzero
|
621
|
-
expect(Money.new(10, "YEN")).to be_nonzero
|
622
|
-
expect(Money.new(-1, "EUR")).to be_nonzero
|
623
|
-
end
|
624
|
-
|
625
|
-
it "has the same return-value semantics as Numeric#nonzero?" do
|
626
|
-
expect(Money.new(0, "USD").nonzero?).to be_nil
|
627
|
-
|
628
|
-
money = Money.new(1, "USD")
|
629
|
-
expect(money.nonzero?).to be_equal(money)
|
630
|
-
end
|
631
|
-
end
|
632
|
-
|
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
|
-
|
639
|
-
it "allows mathematical operations by coercing arguments" do
|
640
|
-
result = 2 * Money.new(4, 'USD')
|
641
|
-
expect(result).to eq Money.new(8, 'USD')
|
642
|
-
end
|
643
|
-
|
644
|
-
it "raises TypeError dividing by a Money (unless other is a Money)" do
|
645
|
-
expect {
|
646
|
-
2 / Money.new(2, 'USD')
|
647
|
-
}.to raise_exception(TypeError)
|
648
|
-
end
|
649
|
-
|
650
|
-
it "raises TypeError subtracting by a Money (unless other is a Money)" do
|
651
|
-
expect {
|
652
|
-
2 - Money.new(2, 'USD')
|
653
|
-
}.to raise_exception(TypeError)
|
654
|
-
end
|
655
|
-
|
656
|
-
it "raises TypeError adding by a Money (unless other is a Money)" do
|
657
|
-
expect {
|
658
|
-
2 + Money.new(2, 'USD')
|
659
|
-
}.to raise_exception(TypeError)
|
660
|
-
end
|
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
|
-
|
672
|
-
it "treats multiplication as commutative" do
|
673
|
-
expect {
|
674
|
-
2 * Money.new(2, 'USD')
|
675
|
-
}.to_not raise_exception
|
676
|
-
result = 2 * Money.new(2, 'USD')
|
677
|
-
expect(result).to eq(Money.new(4, 'USD'))
|
678
|
-
end
|
679
|
-
|
680
|
-
it "doesn't work with non-numerics" do
|
681
|
-
expect {
|
682
|
-
"2" * Money.new(2, 'USD')
|
683
|
-
}.to raise_exception(TypeError)
|
684
|
-
end
|
685
|
-
|
686
|
-
it "correctly handles <=>" do
|
687
|
-
expect {
|
688
|
-
2 < Money.new(2, 'USD')
|
689
|
-
}.to raise_exception(ArgumentError)
|
690
|
-
|
691
|
-
expect {
|
692
|
-
2 > Money.new(2, 'USD')
|
693
|
-
}.to raise_exception(ArgumentError)
|
694
|
-
|
695
|
-
expect {
|
696
|
-
2 <= Money.new(2, 'USD')
|
697
|
-
}.to raise_exception(ArgumentError)
|
698
|
-
|
699
|
-
expect {
|
700
|
-
2 >= Money.new(2, 'USD')
|
701
|
-
}.to raise_exception(ArgumentError)
|
702
|
-
|
703
|
-
expect(2 <=> Money.new(2, 'USD')).to be_nil
|
704
|
-
end
|
705
|
-
|
706
|
-
it 'compares with numeric 0' do
|
707
|
-
expect(0 < Money.usd(1)).to eq true
|
708
|
-
expect(0.0 > Money.usd(1)).to eq false
|
709
|
-
expect(0.0 >= Money.usd(0)).to eq true
|
710
|
-
end
|
711
|
-
|
712
|
-
it "raises exceptions for all numeric types, not just Integer" do
|
713
|
-
expect {
|
714
|
-
2.0 / Money.new(2, 'USD')
|
715
|
-
}.to raise_exception(TypeError)
|
716
|
-
|
717
|
-
expect {
|
718
|
-
Rational(2,3) / Money.new(2, 'USD')
|
719
|
-
}.to raise_exception(TypeError)
|
720
|
-
|
721
|
-
expect {
|
722
|
-
BigDecimal(2) / Money.new(2, 'USD')
|
723
|
-
}.to raise_exception(TypeError)
|
724
|
-
end
|
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
|
756
|
-
end
|