money-joshm1 5.1.2
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.
- data/CHANGELOG.md +475 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +52 -0
- data/config/currency_backwards_compatible.json +128 -0
- data/config/currency_iso.json +2297 -0
- data/config/currency_non_iso.json +30 -0
- data/lib/money.rb +6 -0
- data/lib/money/bank/base.rb +130 -0
- data/lib/money/bank/variable_exchange.rb +252 -0
- data/lib/money/core_extensions.rb +82 -0
- data/lib/money/currency.rb +355 -0
- data/lib/money/currency/heuristics.rb +149 -0
- data/lib/money/currency/loader.rb +22 -0
- data/lib/money/money.rb +536 -0
- data/lib/money/money/arithmetic.rb +288 -0
- data/lib/money/money/formatting.rb +315 -0
- data/lib/money/money/parsing.rb +371 -0
- data/money.gemspec +29 -0
- data/spec/bank/base_spec.rb +77 -0
- data/spec/bank/variable_exchange_spec.rb +233 -0
- data/spec/core_extensions_spec.rb +160 -0
- data/spec/currency/heuristics_spec.rb +84 -0
- data/spec/currency_spec.rb +183 -0
- data/spec/money/arithmetic_spec.rb +598 -0
- data/spec/money/formatting_spec.rb +466 -0
- data/spec/money/parsing_spec.rb +309 -0
- data/spec/money_spec.rb +497 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/default_currency_helper.rb +13 -0
- metadata +145 -0
@@ -0,0 +1,183 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Money::Currency do
|
6
|
+
|
7
|
+
FOO = '{ "priority": 1, "iso_code": "FOO", "iso_numeric": "840", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 450, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": "," }'
|
8
|
+
|
9
|
+
describe ".find" do
|
10
|
+
it "returns currency matching given id" do
|
11
|
+
Money::Currency.register(JSON.parse(FOO, :symbolize_names => true))
|
12
|
+
|
13
|
+
expected = Money::Currency.new(:foo)
|
14
|
+
Money::Currency.find(:foo).should == expected
|
15
|
+
Money::Currency.find(:FOO).should == expected
|
16
|
+
Money::Currency.find("foo").should == expected
|
17
|
+
Money::Currency.find("FOO").should == expected
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns nil unless currency matching given id" do
|
21
|
+
Money::Currency.find("ZZZ").should be_nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe ".find_by_iso_numeric" do
|
26
|
+
it "returns currency matching given numeric code" do
|
27
|
+
Money::Currency.find_by_iso_numeric(978).should == Money::Currency.new(:eur)
|
28
|
+
Money::Currency.find_by_iso_numeric(208).should_not == Money::Currency.new(:eur)
|
29
|
+
Money::Currency.find_by_iso_numeric('840').should == Money::Currency.new(:usd)
|
30
|
+
|
31
|
+
class Mock
|
32
|
+
def to_s
|
33
|
+
'208'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
Money::Currency.find_by_iso_numeric(Mock.new).should == Money::Currency.new(:dkk)
|
37
|
+
Money::Currency.find_by_iso_numeric(Mock.new).should_not == Money::Currency.new(:usd)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns nil if no currency has the given numeric code" do
|
41
|
+
Money::Currency.find_by_iso_numeric('non iso 4217 numeric code').should == nil
|
42
|
+
Money::Currency.find_by_iso_numeric(0).should == nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe ".wrap" do
|
47
|
+
it "returns nil if object is nil" do
|
48
|
+
Money::Currency.wrap(nil).should == nil
|
49
|
+
Money::Currency.wrap(Money::Currency.new(:usd)).should == Money::Currency.new(:usd)
|
50
|
+
Money::Currency.wrap(:usd).should == Money::Currency.new(:usd)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe ".all" do
|
55
|
+
it "returns an array of currencies" do
|
56
|
+
Money::Currency.all.should include Money::Currency.new(:usd)
|
57
|
+
end
|
58
|
+
it "includes registered currencies" do
|
59
|
+
Money::Currency.register(JSON.parse(FOO, :symbolize_names => true))
|
60
|
+
Money::Currency.all.should include Money::Currency.new(:foo)
|
61
|
+
end
|
62
|
+
it 'is sorted by priority' do
|
63
|
+
Money::Currency.all.first.priority.should == 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#initialize" do
|
68
|
+
it "lookups data from loaded config" do
|
69
|
+
currency = Money::Currency.new("USD")
|
70
|
+
currency.id.should == :usd
|
71
|
+
currency.priority.should == 1
|
72
|
+
currency.iso_code.should == "USD"
|
73
|
+
currency.iso_numeric.should == "840"
|
74
|
+
currency.name.should == "United States Dollar"
|
75
|
+
currency.decimal_mark.should == "."
|
76
|
+
currency.separator.should == "."
|
77
|
+
currency.thousands_separator.should == ","
|
78
|
+
currency.delimiter.should == ","
|
79
|
+
end
|
80
|
+
|
81
|
+
it "raises UnknownMoney::Currency with unknown currency" do
|
82
|
+
expect { Money::Currency.new("xxx") }.to raise_error(Money::Currency::UnknownCurrency, /xxx/)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#<=>" do
|
87
|
+
it "compares objects by priority" do
|
88
|
+
Money::Currency.new(:cad).should > Money::Currency.new(:usd)
|
89
|
+
Money::Currency.new(:usd).should < Money::Currency.new(:eur)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "#==" do
|
94
|
+
it "returns true if self === other" do
|
95
|
+
currency = Money::Currency.new(:eur)
|
96
|
+
currency.should == currency
|
97
|
+
end
|
98
|
+
|
99
|
+
it "returns true if the id is equal ignorning case" do
|
100
|
+
Money::Currency.new(:eur).should == Money::Currency.new(:eur)
|
101
|
+
Money::Currency.new(:eur).should == Money::Currency.new(:EUR)
|
102
|
+
Money::Currency.new(:eur).should_not == Money::Currency.new(:usd)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "allows direct comparison of currencies and symbols/strings" do
|
106
|
+
Money::Currency.new(:eur).should == 'eur'
|
107
|
+
Money::Currency.new(:eur).should == :EUR
|
108
|
+
Money::Currency.new(:eur).should_not == 'usd'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#eql?" do
|
113
|
+
it "returns true if #== returns true" do
|
114
|
+
Money::Currency.new(:eur).eql?(Money::Currency.new(:eur)).should be true
|
115
|
+
Money::Currency.new(:eur).eql?(Money::Currency.new(:usd)).should be false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe "#hash" do
|
120
|
+
it "returns the same value for equal objects" do
|
121
|
+
Money::Currency.new(:eur).hash.should == Money::Currency.new(:eur).hash
|
122
|
+
Money::Currency.new(:eur).hash.should_not == Money::Currency.new(:usd).hash
|
123
|
+
end
|
124
|
+
|
125
|
+
it "can be used to return the intersection of Currency object arrays" do
|
126
|
+
intersection = [Money::Currency.new(:eur), Money::Currency.new(:usd)] & [Money::Currency.new(:eur)]
|
127
|
+
intersection.should == [Money::Currency.new(:eur)]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "#inspect" do
|
132
|
+
it "works as documented" do
|
133
|
+
Money::Currency.new(:usd).inspect.should ==
|
134
|
+
%Q{#<Money::Currency id: usd, priority: 1, symbol_first: true, thousands_separator: ,, html_entity: $, decimal_mark: ., name: United States Dollar, symbol: $, subunit_to_unit: 100, exponent: 2.0, iso_code: USD, iso_numeric: 840, subunit: Cent>}
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "#to_s" do
|
139
|
+
it "works as documented" do
|
140
|
+
Money::Currency.new(:usd).to_s.should == "USD"
|
141
|
+
Money::Currency.new(:eur).to_s.should == "EUR"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "#to_currency" do
|
146
|
+
it "works as documented" do
|
147
|
+
usd = Money::Currency.new(:usd)
|
148
|
+
usd.to_currency.should == usd
|
149
|
+
end
|
150
|
+
|
151
|
+
it "doesn't create new symbols indefinitely" do
|
152
|
+
expect { Money::Currency.new("bogus") }.to raise_exception(Money::Currency::UnknownCurrency)
|
153
|
+
Symbol.all_symbols.map{|s| s.to_s}.should_not include("bogus")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "#code" do
|
158
|
+
it "works as documented" do
|
159
|
+
Money::Currency.new(:usd).code.should == "$"
|
160
|
+
Money::Currency.new(:azn).code.should == "AZN"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "#exponent" do
|
165
|
+
it "conforms to iso 4217" do
|
166
|
+
Money::Currency.new(:jpy).exponent == 0
|
167
|
+
Money::Currency.new(:usd).exponent == 2
|
168
|
+
Money::Currency.new(:iqd).exponent == 3
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "#decimal_places" do
|
173
|
+
it "proper places for known currency" do
|
174
|
+
Money::Currency.new(:mro).decimal_places == 1
|
175
|
+
Money::Currency.new(:usd).decimal_places == 2
|
176
|
+
end
|
177
|
+
|
178
|
+
it "proper places for custom currency" do
|
179
|
+
Money::Currency.register(JSON.parse(FOO, :symbolize_names => true))
|
180
|
+
Money::Currency.new(:foo).decimal_places == 3
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,598 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Money do
|
6
|
+
describe "-@" do
|
7
|
+
it "changes the sign of a number" do
|
8
|
+
(- Money.new(0)).should == Money.new(0)
|
9
|
+
(- Money.new(1)).should == Money.new(-1)
|
10
|
+
(- Money.new(-1)).should == Money.new(1)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#==" do
|
15
|
+
it "returns true if and only if their amount and currency are equal" do
|
16
|
+
Money.new(1_00, "USD").should == Money.new(1_00, "USD")
|
17
|
+
Money.new(1_00, "USD").should_not == Money.new(1_00, "EUR")
|
18
|
+
Money.new(1_00, "USD").should_not == Money.new(2_00, "USD")
|
19
|
+
Money.new(1_00, "USD").should_not == Money.new(99_00, "EUR")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns false if used to compare with an object that doesn't respond to #to_money" do
|
23
|
+
Money.new(1_00, "USD").should_not == Object.new
|
24
|
+
Money.new(1_00, "USD").should_not == Class
|
25
|
+
Money.new(1_00, "USD").should_not == Kernel
|
26
|
+
Money.new(1_00, "USD").should_not == /foo/
|
27
|
+
Money.new(1_00, "USD").should_not == nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can be used to compare with a String money value" do
|
31
|
+
Money.new(1_00, "USD").should == "1.00"
|
32
|
+
Money.new(1_00, "USD").should_not == "2.00"
|
33
|
+
Money.new(1_00, "GBP").should_not == "1.00"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can be used to compare with a Numeric money value" do
|
37
|
+
Money.new(1_00, "USD").should == 1
|
38
|
+
Money.new(1_57, "USD").should == 1.57
|
39
|
+
Money.new(1_00, "USD").should_not == 2
|
40
|
+
Money.new(1_00, "GBP").should_not == 1
|
41
|
+
end
|
42
|
+
|
43
|
+
it "can be used to compare with an object that responds to #to_money" do
|
44
|
+
klass = Class.new do
|
45
|
+
def initialize(money)
|
46
|
+
@money = money
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_money
|
50
|
+
@money
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Money.new(1_00, "USD").should == klass.new(Money.new(1_00, "USD"))
|
55
|
+
Money.new(2_50, "USD").should == klass.new(Money.new(2_50, "USD"))
|
56
|
+
Money.new(2_50, "USD").should_not == klass.new(Money.new(3_00, "USD"))
|
57
|
+
Money.new(1_00, "GBP").should_not == klass.new(Money.new(1_00, "USD"))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#eql?" do
|
62
|
+
it "returns true if and only if their amount and currency are equal" do
|
63
|
+
Money.new(1_00, "USD").eql?(Money.new(1_00, "USD")).should be true
|
64
|
+
Money.new(1_00, "USD").eql?(Money.new(1_00, "EUR")).should be false
|
65
|
+
Money.new(1_00, "USD").eql?(Money.new(2_00, "USD")).should be false
|
66
|
+
Money.new(1_00, "USD").eql?(Money.new(99_00, "EUR")).should be false
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns false if used to compare with an object that doesn't respond to #to_money" do
|
70
|
+
Money.new(1_00, "USD").eql?(Object.new).should be false
|
71
|
+
Money.new(1_00, "USD").eql?(Class).should be false
|
72
|
+
Money.new(1_00, "USD").eql?(Kernel).should be false
|
73
|
+
Money.new(1_00, "USD").eql?(/foo/).should be false
|
74
|
+
Money.new(1_00, "USD").eql?(nil).should be false
|
75
|
+
end
|
76
|
+
|
77
|
+
it "can be used to compare with a String money value" do
|
78
|
+
Money.new(1_00, "USD").eql?("1.00").should be true
|
79
|
+
Money.new(1_00, "USD").eql?("2.00").should be false
|
80
|
+
Money.new(1_00, "GBP").eql?("1.00").should be false
|
81
|
+
end
|
82
|
+
|
83
|
+
it "can be used to compare with a Numeric money value" do
|
84
|
+
Money.new(1_00, "USD").eql?(1).should be true
|
85
|
+
Money.new(1_57, "USD").eql?(1.57).should be true
|
86
|
+
Money.new(1_00, "USD").eql?(2).should be false
|
87
|
+
Money.new(1_00, "GBP").eql?(1).should be false
|
88
|
+
end
|
89
|
+
|
90
|
+
it "can be used to compare with an object that responds to #to_money" do
|
91
|
+
klass = Class.new do
|
92
|
+
def initialize(money)
|
93
|
+
@money = money
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_money
|
97
|
+
@money
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
Money.new(1_00, "USD").eql?(klass.new(Money.new(1_00, "USD"))).should be true
|
102
|
+
Money.new(2_50, "USD").eql?(klass.new(Money.new(2_50, "USD"))).should be true
|
103
|
+
Money.new(2_50, "USD").eql?(klass.new(Money.new(3_00, "USD"))).should be false
|
104
|
+
Money.new(1_00, "GBP").eql?(klass.new(Money.new(1_00, "USD"))).should be false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#<=>" do
|
109
|
+
it "compares the two object amounts (same currency)" do
|
110
|
+
(Money.new(1_00, "USD") <=> Money.new(1_00, "USD")).should == 0
|
111
|
+
(Money.new(1_00, "USD") <=> Money.new(99, "USD")).should > 0
|
112
|
+
(Money.new(1_00, "USD") <=> Money.new(2_00, "USD")).should < 0
|
113
|
+
end
|
114
|
+
|
115
|
+
it "converts other object amount to current currency, then compares the two object amounts (different currency)" do
|
116
|
+
target = Money.new(200_00, "EUR")
|
117
|
+
target.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(300_00, "USD"))
|
118
|
+
(Money.new(100_00, "USD") <=> target).should < 0
|
119
|
+
|
120
|
+
target = Money.new(200_00, "EUR")
|
121
|
+
target.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(100_00, "USD"))
|
122
|
+
(Money.new(100_00, "USD") <=> target).should == 0
|
123
|
+
|
124
|
+
target = Money.new(200_00, "EUR")
|
125
|
+
target.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(99_00, "USD"))
|
126
|
+
(Money.new(100_00, "USD") <=> target).should > 0
|
127
|
+
end
|
128
|
+
|
129
|
+
it "cannot compare objects with different currencies when no rate exists, unless one of the amounts is zero" do
|
130
|
+
expect { Money.new(100_00, "EUR") <=> Money.new(100_00, "USD") }.to raise_error(Money::Bank::UnknownRate)
|
131
|
+
|
132
|
+
(Money.new(100_00, "EUR") <=> Money.new(0, "USD")).should > 0
|
133
|
+
(Money.new(0, "EUR") <=> Money.new(100_00, "USD")).should < 0
|
134
|
+
(Money.new(0, "EUR") <=> Money.new(0, "USD")).should == 0
|
135
|
+
end
|
136
|
+
|
137
|
+
it "can be used to compare with a String money value when Money object is in default currency" do
|
138
|
+
(Money.new(1_00) <=> "1.00").should == 0
|
139
|
+
(Money.new(1_00) <=> ".99").should > 0
|
140
|
+
(Money.new(1_00) <=> "2.00").should < 0
|
141
|
+
end
|
142
|
+
|
143
|
+
it "can be used to compare with a String money value when Money object is not in default currency if String evaluates to zero" do
|
144
|
+
expect { Money.new(1_00, "EUR") <=> "1.00" }.to raise_error(Money::Bank::UnknownRate)
|
145
|
+
|
146
|
+
(Money.new(1_00, "EUR") <=> "0.00").should > 0
|
147
|
+
(Money.new(0_00, "EUR") <=> "0.00").should == 0
|
148
|
+
(Money.new(-1_00, "EUR") <=> "0.00").should < 0
|
149
|
+
end
|
150
|
+
|
151
|
+
it "can be used to compare with a Numeric money value when Money object is in default currency" do
|
152
|
+
(Money.new(1_00) <=> 1).should == 0
|
153
|
+
(Money.new(1_00) <=> 0.99).should > 0
|
154
|
+
(Money.new(1_00) <=> 2.00).should < 0
|
155
|
+
end
|
156
|
+
|
157
|
+
it "can be used to compare with a Numeric money value when Money object is not in default currency if String evaluates to zero" do
|
158
|
+
expect { Money.new(1_00, "EUR") <=> 1 }.to raise_error(Money::Bank::UnknownRate)
|
159
|
+
|
160
|
+
(Money.new(1_00, "EUR") <=> 0).should > 0
|
161
|
+
(Money.new(0_00, "EUR") <=> 0).should == 0
|
162
|
+
(Money.new(-1_00, "EUR") <=> 0).should < 0
|
163
|
+
end
|
164
|
+
|
165
|
+
it "can be used to compare with an object that responds to #to_money" do
|
166
|
+
klass = Class.new do
|
167
|
+
def initialize(money)
|
168
|
+
@money = money
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_money
|
172
|
+
@money
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
(Money.new(1_00) <=> klass.new(Money.new(1_00))).should == 0
|
177
|
+
(Money.new(1_00) <=> klass.new(Money.new(99))).should > 0
|
178
|
+
(Money.new(1_00) <=> klass.new(Money.new(2_00))).should < 0
|
179
|
+
end
|
180
|
+
|
181
|
+
it "raises ArgumentError when used to compare with an object that doesn't respond to #to_money" do
|
182
|
+
expected_message = /Comparison .+ failed/
|
183
|
+
expect{ Money.new(1_00) <=> Object.new }.to raise_error(ArgumentError, expected_message)
|
184
|
+
expect{ Money.new(1_00) <=> Class }.to raise_error(ArgumentError, expected_message)
|
185
|
+
expect{ Money.new(1_00) <=> Kernel }.to raise_error(ArgumentError, expected_message)
|
186
|
+
expect{ Money.new(1_00) <=> /foo/ }.to raise_error(ArgumentError, expected_message)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "#positive?" do
|
191
|
+
it "returns true if the amount is greater than 0" do
|
192
|
+
Money.new(1).should be_positive
|
193
|
+
end
|
194
|
+
|
195
|
+
it "returns false if the amount is 0" do
|
196
|
+
Money.new(0).should_not be_positive
|
197
|
+
end
|
198
|
+
|
199
|
+
it "returns false if the amount is negative" do
|
200
|
+
Money.new(-1).should_not be_positive
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "#negative?" do
|
205
|
+
it "returns true if the amount is less than 0" do
|
206
|
+
Money.new(-1).should be_negative
|
207
|
+
end
|
208
|
+
|
209
|
+
it "returns false if the amount is 0" do
|
210
|
+
Money.new(0).should_not be_negative
|
211
|
+
end
|
212
|
+
|
213
|
+
it "returns false if the amount is greater than 0" do
|
214
|
+
Money.new(1).should_not be_negative
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "#+" do
|
219
|
+
it "adds other amount to current amount (same currency)" do
|
220
|
+
(Money.new(10_00, "USD") + Money.new(90, "USD")).should == Money.new(10_90, "USD")
|
221
|
+
end
|
222
|
+
|
223
|
+
it "converts other object amount to current currency and adds other amount to current amount (different currency)" do
|
224
|
+
other = Money.new(90, "EUR")
|
225
|
+
other.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
|
226
|
+
(Money.new(10_00, "USD") + other).should == Money.new(19_00, "USD")
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "#-" do
|
231
|
+
it "subtracts other amount from current amount (same currency)" do
|
232
|
+
(Money.new(10_00, "USD") - Money.new(90, "USD")).should == Money.new(9_10, "USD")
|
233
|
+
end
|
234
|
+
|
235
|
+
it "converts other object amount to current currency and subtracts other amount from current amount (different currency)" do
|
236
|
+
other = Money.new(90, "EUR")
|
237
|
+
other.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
|
238
|
+
(Money.new(10_00, "USD") - other).should == Money.new(1_00, "USD")
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe "#*" do
|
243
|
+
it "multiplies Money by Fixnum and returns Money" do
|
244
|
+
ts = [
|
245
|
+
{:a => Money.new( 10, :USD), :b => 4, :c => Money.new( 40, :USD)},
|
246
|
+
{:a => Money.new( 10, :USD), :b => -4, :c => Money.new(-40, :USD)},
|
247
|
+
{:a => Money.new(-10, :USD), :b => 4, :c => Money.new(-40, :USD)},
|
248
|
+
{:a => Money.new(-10, :USD), :b => -4, :c => Money.new( 40, :USD)},
|
249
|
+
]
|
250
|
+
ts.each do |t|
|
251
|
+
(t[:a] * t[:b]).should == t[:c]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
it "does not multiply Money by Money (same currency)" do
|
256
|
+
expect { Money.new( 10, :USD) * Money.new( 4, :USD) }.to raise_error(ArgumentError)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "does not multiply Money by Money (different currency)" do
|
260
|
+
expect { Money.new( 10, :USD) * Money.new( 4, :EUR) }.to raise_error(ArgumentError)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "#/" do
|
265
|
+
it "divides Money by Fixnum and returns Money" do
|
266
|
+
ts = [
|
267
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3, :USD)},
|
268
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-4, :USD)},
|
269
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-4, :USD)},
|
270
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
|
271
|
+
]
|
272
|
+
ts.each do |t|
|
273
|
+
(t[:a] / t[:b]).should == t[:c]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
it "divides Money by Money (same currency) and returns Float" do
|
278
|
+
ts = [
|
279
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => 3.25},
|
280
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => -3.25},
|
281
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => -3.25},
|
282
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
|
283
|
+
]
|
284
|
+
ts.each do |t|
|
285
|
+
(t[:a] / t[:b]).should == t[:c]
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
it "divides Money by Money (different currency) and returns Float" do
|
290
|
+
ts = [
|
291
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => 1.625},
|
292
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => -1.625},
|
293
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => -1.625},
|
294
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
|
295
|
+
]
|
296
|
+
ts.each do |t|
|
297
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
298
|
+
(t[:a] / t[:b]).should == t[:c]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context "infinite_precision = true" do
|
303
|
+
before do
|
304
|
+
Money.infinite_precision = true
|
305
|
+
end
|
306
|
+
|
307
|
+
after do
|
308
|
+
Money.infinite_precision = false
|
309
|
+
end
|
310
|
+
|
311
|
+
it "uses BigDecimal division" do
|
312
|
+
ts = [
|
313
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3.25, :USD)},
|
314
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3.25, :USD)},
|
315
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-3.25, :USD)},
|
316
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3.25, :USD)},
|
317
|
+
]
|
318
|
+
ts.each do |t|
|
319
|
+
(t[:a] / t[:b]).should == t[:c]
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "#div" do
|
326
|
+
it "divides Money by Fixnum and returns Money" do
|
327
|
+
ts = [
|
328
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3, :USD)},
|
329
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-4, :USD)},
|
330
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-4, :USD)},
|
331
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
|
332
|
+
]
|
333
|
+
ts.each do |t|
|
334
|
+
t[:a].div(t[:b]).should == t[:c]
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
it "divides Money by Money (same currency) and returns Float" do
|
339
|
+
ts = [
|
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
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => -3.25},
|
343
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
|
344
|
+
]
|
345
|
+
ts.each do |t|
|
346
|
+
t[:a].div(t[:b]).should == t[:c]
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
it "divides Money by Money (different currency) and returns Float" do
|
351
|
+
ts = [
|
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
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => -1.625},
|
355
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
|
356
|
+
]
|
357
|
+
ts.each do |t|
|
358
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
359
|
+
t[:a].div(t[:b]).should == t[:c]
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
context "infinite_precision = true" do
|
364
|
+
before do
|
365
|
+
Money.infinite_precision = true
|
366
|
+
end
|
367
|
+
|
368
|
+
after do
|
369
|
+
Money.infinite_precision = false
|
370
|
+
end
|
371
|
+
|
372
|
+
it "uses BigDecimal division" do
|
373
|
+
ts = [
|
374
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3.25, :USD)},
|
375
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3.25, :USD)},
|
376
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-3.25, :USD)},
|
377
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3.25, :USD)},
|
378
|
+
]
|
379
|
+
ts.each do |t|
|
380
|
+
t[:a].div(t[:b]).should == t[:c]
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
describe "#divmod" do
|
387
|
+
it "calculates division and modulo with Fixnum" do
|
388
|
+
ts = [
|
389
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => [Money.new( 3, :USD), Money.new( 1, :USD)]},
|
390
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => [Money.new(-4, :USD), Money.new(-3, :USD)]},
|
391
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => [Money.new(-4, :USD), Money.new( 3, :USD)]},
|
392
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => [Money.new( 3, :USD), Money.new(-1, :USD)]},
|
393
|
+
]
|
394
|
+
ts.each do |t|
|
395
|
+
t[:a].divmod(t[:b]).should == t[:c]
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
it "calculates division and modulo with Money (same currency)" do
|
400
|
+
ts = [
|
401
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => [ 3, Money.new( 1, :USD)]},
|
402
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => [-4, Money.new(-3, :USD)]},
|
403
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => [-4, Money.new( 3, :USD)]},
|
404
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => [ 3, Money.new(-1, :USD)]},
|
405
|
+
]
|
406
|
+
ts.each do |t|
|
407
|
+
t[:a].divmod(t[:b]).should == t[:c]
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
it "calculates division and modulo with Money (different currency)" do
|
412
|
+
ts = [
|
413
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => [ 1, Money.new( 5, :USD)]},
|
414
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => [-2, Money.new(-3, :USD)]},
|
415
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => [-2, Money.new( 3, :USD)]},
|
416
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => [ 1, Money.new(-5, :USD)]},
|
417
|
+
]
|
418
|
+
ts.each do |t|
|
419
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
420
|
+
t[:a].divmod(t[:b]).should == t[:c]
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
context "infinite_precision = true" do
|
425
|
+
before do
|
426
|
+
Money.infinite_precision = true
|
427
|
+
end
|
428
|
+
|
429
|
+
after do
|
430
|
+
Money.infinite_precision = false
|
431
|
+
end
|
432
|
+
|
433
|
+
it "uses BigDecimal division" do
|
434
|
+
ts = [
|
435
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => [Money.new( 3, :USD), Money.new( 1, :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(-4, :USD), Money.new( 3, :USD)]},
|
438
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => [Money.new( 3, :USD), Money.new(-1, :USD)]},
|
439
|
+
]
|
440
|
+
ts.each do |t|
|
441
|
+
t[:a].divmod(t[:b]).should == t[:c]
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
describe "#modulo" do
|
448
|
+
it "calculates modulo with Fixnum" do
|
449
|
+
ts = [
|
450
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
|
451
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
|
452
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new( 3, :USD)},
|
453
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
|
454
|
+
]
|
455
|
+
ts.each do |t|
|
456
|
+
t[:a].modulo(t[:b]).should == t[:c]
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
it "calculates modulo with Money (same currency)" do
|
461
|
+
ts = [
|
462
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
|
463
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-3, :USD)},
|
464
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 3, :USD)},
|
465
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
|
466
|
+
]
|
467
|
+
ts.each do |t|
|
468
|
+
t[:a].modulo(t[:b]).should == t[:c]
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
it "calculates modulo with Money (different currency)" do
|
473
|
+
ts = [
|
474
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
|
475
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-3, :USD)},
|
476
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 3, :USD)},
|
477
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
|
478
|
+
]
|
479
|
+
ts.each do |t|
|
480
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
481
|
+
t[:a].modulo(t[:b]).should == t[:c]
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
describe "#%" do
|
487
|
+
it "calculates modulo with Fixnum" do
|
488
|
+
ts = [
|
489
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
|
490
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
|
491
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new( 3, :USD)},
|
492
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
|
493
|
+
]
|
494
|
+
ts.each do |t|
|
495
|
+
(t[:a] % t[:b]).should == t[:c]
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
it "calculates modulo with Money (same currency)" do
|
500
|
+
ts = [
|
501
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
|
502
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-3, :USD)},
|
503
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 3, :USD)},
|
504
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
|
505
|
+
]
|
506
|
+
ts.each do |t|
|
507
|
+
(t[:a] % t[:b]).should == t[:c]
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
it "calculates modulo with Money (different currency)" do
|
512
|
+
ts = [
|
513
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
|
514
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-3, :USD)},
|
515
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 3, :USD)},
|
516
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
|
517
|
+
]
|
518
|
+
ts.each do |t|
|
519
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
520
|
+
(t[:a] % t[:b]).should == t[:c]
|
521
|
+
end
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
describe "#remainder" do
|
526
|
+
it "calculates remainder with Fixnum" do
|
527
|
+
ts = [
|
528
|
+
{:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
|
529
|
+
{:a => Money.new( 13, :USD), :b => -4, :c => Money.new( 1, :USD)},
|
530
|
+
{:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-1, :USD)},
|
531
|
+
{:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
|
532
|
+
]
|
533
|
+
ts.each do |t|
|
534
|
+
t[:a].remainder(t[:b]).should == t[:c]
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
it "calculates remainder with Money (same currency)" do
|
539
|
+
ts = [
|
540
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
|
541
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new( 1, :USD)},
|
542
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new(-1, :USD)},
|
543
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
|
544
|
+
]
|
545
|
+
ts.each do |t|
|
546
|
+
t[:a].remainder(t[:b]).should == t[:c]
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
it "calculates remainder with Money (different currency)" do
|
551
|
+
ts = [
|
552
|
+
{:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
|
553
|
+
{:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new( 5, :USD)},
|
554
|
+
{:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new(-5, :USD)},
|
555
|
+
{:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
|
556
|
+
]
|
557
|
+
ts.each do |t|
|
558
|
+
t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
|
559
|
+
t[:a].remainder(t[:b]).should == t[:c]
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
describe "#abs" do
|
565
|
+
it "returns the absolute value as a new Money object" do
|
566
|
+
n = Money.new(-1, :USD)
|
567
|
+
n.abs.should == Money.new( 1, :USD)
|
568
|
+
n.should == Money.new(-1, :USD)
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
describe "#zero?" do
|
573
|
+
it "returns whether the amount is 0" do
|
574
|
+
Money.new(0, "USD").should be_zero
|
575
|
+
Money.new(0, "EUR").should be_zero
|
576
|
+
Money.new(1, "USD").should_not be_zero
|
577
|
+
Money.new(10, "YEN").should_not be_zero
|
578
|
+
Money.new(-1, "EUR").should_not be_zero
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
describe "#nonzero?" do
|
583
|
+
it "returns whether the amount is not 0" do
|
584
|
+
Money.new(0, "USD").should_not be_nonzero
|
585
|
+
Money.new(0, "EUR").should_not be_nonzero
|
586
|
+
Money.new(1, "USD").should be_nonzero
|
587
|
+
Money.new(10, "YEN").should be_nonzero
|
588
|
+
Money.new(-1, "EUR").should be_nonzero
|
589
|
+
end
|
590
|
+
|
591
|
+
it "has the same return-value semantics as Numeric#nonzero?" do
|
592
|
+
Money.new(0, "USD").nonzero?.should be_nil
|
593
|
+
|
594
|
+
money = Money.new(1, "USD")
|
595
|
+
money.nonzero?.should be_equal(money)
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|