money-joshm1 5.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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