papercavalier-money 3.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,133 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Money::Currency do
6
+
7
+ specify "#initialize should lookup data from TABLE" do
8
+ with_custom_definitions do
9
+ Money::Currency::TABLE[:usd] = {:priority => 1, :iso_code => "USD", :name => "United States Dollar", :symbol => "$", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => ","}
10
+ Money::Currency::TABLE[:eur] = {:priority => 2, :iso_code => "EUR", :name => "Euro", :symbol => "€", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => ","}
11
+
12
+ currency = Money::Currency.new("USD")
13
+ currency.id.should == :usd
14
+ currency.priority.should == 1
15
+ currency.iso_code.should == "USD"
16
+ currency.name.should == "United States Dollar"
17
+ currency.decimal_mark.should == "."
18
+ currency.separator.should == "."
19
+ currency.thousands_separator.should == ","
20
+ currency.delimiter.should == ","
21
+ end
22
+ end
23
+
24
+ specify "#initialize should raise UnknownMoney::Currency with unknown currency" do
25
+ lambda { Money::Currency.new("xxx") }.should raise_error(Money::Currency::UnknownCurrency, /xxx/)
26
+ end
27
+
28
+ specify "#== should return true if self === other" do
29
+ currency = Money::Currency.new(:eur)
30
+ currency.should == currency
31
+ end
32
+
33
+ specify "#== should return true if the id is equal" do
34
+ Money::Currency.new(:eur).should == Money::Currency.new(:eur)
35
+ Money::Currency.new(:eur).should_not == Money::Currency.new(:usd)
36
+ end
37
+
38
+ specify "#eql? should return true if #== returns true" do
39
+ Money::Currency.new(:eur).eql?(Money::Currency.new(:eur)).should be true
40
+ Money::Currency.new(:eur).eql?(Money::Currency.new(:usd)).should be false
41
+ end
42
+
43
+ specify "#hash should return the same value for equal objects" do
44
+ Money::Currency.new(:eur).hash.should == Money::Currency.new(:eur).hash
45
+ Money::Currency.new(:eur).hash.should_not == Money::Currency.new(:usd).hash
46
+ end
47
+
48
+ specify "#hash can be used to return the intersection of Currency object arrays" do
49
+ intersection = [Money::Currency.new(:eur), Money::Currency.new(:usd)] & [Money::Currency.new(:eur)]
50
+ intersection.should == [Money::Currency.new(:eur)]
51
+ end
52
+
53
+ specify "#<=> should compare objects by priority" do
54
+ Money::Currency.new(:cad).should > Money::Currency.new(:usd)
55
+ Money::Currency.new(:usd).should < Money::Currency.new(:eur)
56
+ end
57
+
58
+ specify "#to_s" do
59
+ Money::Currency.new(:usd).to_s.should == "USD"
60
+ Money::Currency.new(:eur).to_s.should == "EUR"
61
+ end
62
+
63
+ specify "#to_currency" do
64
+ usd = Money::Currency.new(:usd)
65
+ usd.to_currency.should == usd
66
+ end
67
+
68
+ specify "#inspect" do
69
+ Money::Currency.new(:usd).inspect.should ==
70
+ %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, iso_code: USD, subunit: Cent>}
71
+ end
72
+
73
+
74
+ specify "#self.find should return currency matching given id" do
75
+ with_custom_definitions do
76
+ Money::Currency::TABLE[:usd] = { :priority => 1, :iso_code => "USD", :name => "United States Dollar", :symbol => "$", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => "," }
77
+ Money::Currency::TABLE[:eur] = { :priority => 2, :iso_code => "EUR", :name => "Euro", :symbol => "€", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => "," }
78
+
79
+ expected = Money::Currency.new(:eur)
80
+ Money::Currency.find(:eur).should == expected
81
+ Money::Currency.find(:EUR).should == expected
82
+ Money::Currency.find("eur").should == expected
83
+ Money::Currency.find("EUR").should == expected
84
+ end
85
+ end
86
+
87
+ specify "#self.find should return nil unless currency matching given id" do
88
+ with_custom_definitions do
89
+ Money::Currency::TABLE[:usd] = { :position => 1, :iso_code => "USD", :name => "United States Dollar", :symbol => "$", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => "," }
90
+ Money::Currency::TABLE[:eur] = { :position => 2, :iso_code => "EUR", :name => "Euro", :symbol => "€", :subunit => "Cent", :subunit_to_unit => 100, :decimal_mark => ".", :thousands_separator => "," }
91
+
92
+ expected = Money::Currency.new(:eur)
93
+ Money::Currency.find(:eur).should == expected
94
+ Money::Currency.find(:EUR).should == expected
95
+ Money::Currency.find("eur").should == expected
96
+ Money::Currency.find("EUR").should == expected
97
+ end
98
+ end
99
+
100
+ specify "#self.wrap should return nil if object is nil" do
101
+ Money::Currency.wrap(nil).should == nil
102
+ Money::Currency.wrap(Money::Currency.new(:usd)).should == Money::Currency.new(:usd)
103
+ Money::Currency.wrap(:usd).should == Money::Currency.new(:usd)
104
+ end
105
+
106
+
107
+ def with_custom_definitions(&block)
108
+ begin
109
+ old = Money::Currency::TABLE.dup
110
+ Money::Currency::TABLE.clear
111
+ yield
112
+ ensure
113
+ silence_warnings do
114
+ Money::Currency.const_set("TABLE", old)
115
+ end
116
+ end
117
+ end
118
+
119
+ # Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
120
+ #
121
+ # silence_warnings do
122
+ # value = noisy_call # no warning voiced
123
+ # end
124
+ #
125
+ # noisy_call # warning voiced
126
+ def silence_warnings
127
+ old_verbose, $VERBOSE = $VERBOSE, nil
128
+ yield
129
+ ensure
130
+ $VERBOSE = old_verbose
131
+ end
132
+
133
+ end
@@ -0,0 +1,479 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Money do
6
+ specify "#zero? returns whether the amount is 0" do
7
+ Money.new(0, "USD").should be_zero
8
+ Money.new(0, "EUR").should be_zero
9
+ Money.new(1, "USD").should_not be_zero
10
+ Money.new(10, "YEN").should_not be_zero
11
+ Money.new(-1, "EUR").should_not be_zero
12
+ end
13
+
14
+ specify "#nonzero? returns whether the amount is not 0" do
15
+ Money.new(0, "USD").should_not be_nonzero
16
+ Money.new(0, "EUR").should_not be_nonzero
17
+ Money.new(1, "USD").should be_nonzero
18
+ Money.new(10, "YEN").should be_nonzero
19
+ Money.new(-1, "EUR").should be_nonzero
20
+ end
21
+
22
+ specify "#nonzero? has the same return-value semantics as Numeric#nonzero?" do
23
+ Money.new(0, "USD").nonzero?.should be_nil
24
+
25
+ money = Money.new(1, "USD")
26
+ money.nonzero?.should be_equal(money)
27
+ end
28
+
29
+ specify "#== returns true if and only if their amount and currency are equal" do
30
+ Money.new(1_00, "USD").should == Money.new(1_00, "USD")
31
+ Money.new(1_00, "USD").should_not == Money.new(1_00, "EUR")
32
+ Money.new(1_00, "USD").should_not == Money.new(2_00, "USD")
33
+ Money.new(1_00, "USD").should_not == Money.new(99_00, "EUR")
34
+ end
35
+
36
+ specify "#== can be used to compare with a String money value" do
37
+ Money.new(1_00, "USD").should == "1.00"
38
+ Money.new(1_00, "USD").should_not == "2.00"
39
+ Money.new(1_00, "GBP").should_not == "1.00"
40
+ end
41
+
42
+ specify "#== can be used to compare with a Numeric money value" do
43
+ Money.new(1_00, "USD").should == 1
44
+ Money.new(1_57, "USD").should == 1.57
45
+ Money.new(1_00, "USD").should_not == 2
46
+ Money.new(1_00, "GBP").should_not == 1
47
+ end
48
+
49
+ specify "#== can be used to compare with an object that responds to #to_money" do
50
+ klass = Class.new do
51
+ def initialize(money)
52
+ @money = money
53
+ end
54
+
55
+ def to_money
56
+ @money
57
+ end
58
+ end
59
+
60
+ Money.new(1_00, "USD").should == klass.new(Money.new(1_00, "USD"))
61
+ Money.new(2_50, "USD").should == klass.new(Money.new(2_50, "USD"))
62
+ Money.new(2_50, "USD").should_not == klass.new(Money.new(3_00, "USD"))
63
+ Money.new(1_00, "GBP").should_not == klass.new(Money.new(1_00, "USD"))
64
+ end
65
+
66
+ specify "#== returns false if used to compare with an object that doesn't respond to #to_money" do
67
+ Money.new(1_00, "USD").should_not == Object.new
68
+ Money.new(1_00, "USD").should_not == Class
69
+ Money.new(1_00, "USD").should_not == Kernel
70
+ Money.new(1_00, "USD").should_not == /foo/
71
+ Money.new(1_00, "USD").should_not == nil
72
+ end
73
+
74
+ specify "#eql? returns true if and only if their amount and currency are equal" do
75
+ Money.new(1_00, "USD").eql?(Money.new(1_00, "USD")).should be true
76
+ Money.new(1_00, "USD").eql?(Money.new(1_00, "EUR")).should be false
77
+ Money.new(1_00, "USD").eql?(Money.new(2_00, "USD")).should be false
78
+ Money.new(1_00, "USD").eql?(Money.new(99_00, "EUR")).should be false
79
+ end
80
+
81
+ specify "#eql? can be used to compare with a String money value" do
82
+ Money.new(1_00, "USD").eql?("1.00").should be true
83
+ Money.new(1_00, "USD").eql?("2.00").should be false
84
+ Money.new(1_00, "GBP").eql?("1.00").should be false
85
+ end
86
+
87
+ specify "#eql? can be used to compare with a Numeric money value" do
88
+ Money.new(1_00, "USD").eql?(1).should be true
89
+ Money.new(1_57, "USD").eql?(1.57).should be true
90
+ Money.new(1_00, "USD").eql?(2).should be false
91
+ Money.new(1_00, "GBP").eql?(1).should be false
92
+ end
93
+
94
+ specify "#eql? can be used to compare with an object that responds to #to_money" do
95
+ klass = Class.new do
96
+ def initialize(money)
97
+ @money = money
98
+ end
99
+
100
+ def to_money
101
+ @money
102
+ end
103
+ end
104
+
105
+ Money.new(1_00, "USD").eql?(klass.new(Money.new(1_00, "USD"))).should be true
106
+ Money.new(2_50, "USD").eql?(klass.new(Money.new(2_50, "USD"))).should be true
107
+ Money.new(2_50, "USD").eql?(klass.new(Money.new(3_00, "USD"))).should be false
108
+ Money.new(1_00, "GBP").eql?(klass.new(Money.new(1_00, "USD"))).should be false
109
+ end
110
+
111
+ specify "#eql? returns false if used to compare with an object that doesn't respond to #to_money" do
112
+ Money.new(1_00, "USD").eql?(Object.new).should be false
113
+ Money.new(1_00, "USD").eql?(Class).should be false
114
+ Money.new(1_00, "USD").eql?(Kernel).should be false
115
+ Money.new(1_00, "USD").eql?(/foo/).should be false
116
+ Money.new(1_00, "USD").eql?(nil).should be false
117
+ end
118
+
119
+ specify "#<=> can be used to compare with a String money value" do
120
+ (Money.new(1_00) <=> "1.00").should == 0
121
+ (Money.new(1_00) <=> ".99").should > 0
122
+ (Money.new(1_00) <=> "2.00").should < 0
123
+ end
124
+
125
+ specify "#<=> can be used to compare with a Numeric money value" do
126
+ (Money.new(1_00) <=> 1).should == 0
127
+ (Money.new(1_00) <=> 0.99).should > 0
128
+ (Money.new(1_00) <=> 2.00).should < 0
129
+ end
130
+
131
+ specify "#<=> can be used to compare with an object that responds to #to_money" do
132
+ klass = Class.new do
133
+ def initialize(money)
134
+ @money = money
135
+ end
136
+
137
+ def to_money
138
+ @money
139
+ end
140
+ end
141
+
142
+ (Money.new(1_00) <=> klass.new(Money.new(1_00))).should == 0
143
+ (Money.new(1_00) <=> klass.new(Money.new(99))).should > 0
144
+ (Money.new(1_00) <=> klass.new(Money.new(2_00))).should < 0
145
+ end
146
+
147
+ specify "#<=> raises ArgumentError when used to compare with an object that doesn't respond to #to_money" do
148
+ expected_message = /Comparison .+ failed/
149
+ lambda{ Money.new(1_00) <=> Object.new }.should raise_error(ArgumentError, expected_message)
150
+ lambda{ Money.new(1_00) <=> Class }.should raise_error(ArgumentError, expected_message)
151
+ lambda{ Money.new(1_00) <=> Kernel }.should raise_error(ArgumentError, expected_message)
152
+ lambda{ Money.new(1_00) <=> /foo/ }.should raise_error(ArgumentError, expected_message)
153
+ end
154
+
155
+ describe "#*" do
156
+ it "should multiply current money amount by the multiplier while retaining the currency" do
157
+ (Money.new(1_00, "USD") * 10).should == Money.new(10_00, "USD")
158
+ end
159
+
160
+ it "should multiply Money by Fixnum and returns Money" do
161
+ ts = [
162
+ {:a => Money.new( 10, :USD), :b => 4, :c => Money.new( 40, :USD)},
163
+ {:a => Money.new( 10, :USD), :b => -4, :c => Money.new(-40, :USD)},
164
+ {:a => Money.new(-10, :USD), :b => 4, :c => Money.new(-40, :USD)},
165
+ {:a => Money.new(-10, :USD), :b => -4, :c => Money.new( 40, :USD)},
166
+ ]
167
+ ts.each do |t|
168
+ (t[:a] * t[:b]).should == t[:c]
169
+ end
170
+ end
171
+
172
+ it "should not multiply Money by Money (same currency)" do
173
+ lambda { Money.new( 10, :USD) * Money.new( 4, :USD) }.should raise_error(ArgumentError)
174
+ end
175
+
176
+ it "should not multiply Money by Money (different currency)" do
177
+ lambda { Money.new( 10, :USD) * Money.new( 4, :EUR) }.should raise_error(ArgumentError)
178
+ end
179
+ end
180
+
181
+ describe "#/" do
182
+ it "divides current money amount by the divisor while retaining the currency" do
183
+ (Money.new(10_00, "USD") / 10).should == Money.new(1_00, "USD")
184
+ end
185
+
186
+ it "divides Money by Fixnum and returns Money" do
187
+ ts = [
188
+ {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3, :USD)},
189
+ {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-4, :USD)},
190
+ {:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-4, :USD)},
191
+ {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
192
+ ]
193
+ ts.each do |t|
194
+ (t[:a] / t[:b]).should == t[:c]
195
+ end
196
+ end
197
+
198
+ it "divides Money by Money (same currency) and returns Float" do
199
+ ts = [
200
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => 3.25},
201
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => -3.25},
202
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => -3.25},
203
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
204
+ ]
205
+ ts.each do |t|
206
+ (t[:a] / t[:b]).should == t[:c]
207
+ end
208
+ end
209
+
210
+ it "divides Money by Money (different currency) and returns Float" do
211
+ ts = [
212
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => 1.625},
213
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => -1.625},
214
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => -1.625},
215
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
216
+ ]
217
+ ts.each do |t|
218
+ t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
219
+ (t[:a] / t[:b]).should == t[:c]
220
+ end
221
+ end
222
+ end
223
+
224
+ specify "#div -> money / fixnum" do
225
+ ts = [
226
+ {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 3, :USD)},
227
+ {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-4, :USD)},
228
+ {:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-4, :USD)},
229
+ {:a => Money.new(-13, :USD), :b => -4, :c => Money.new( 3, :USD)},
230
+ ]
231
+ ts.each do |t|
232
+ t[:a].div(t[:b]).should == t[:c]
233
+ end
234
+ end
235
+
236
+ specify "#div -> money / money (same currency)" do
237
+ ts = [
238
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => 3.25},
239
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => -3.25},
240
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => -3.25},
241
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => 3.25},
242
+ ]
243
+ ts.each do |t|
244
+ t[:a].div(t[:b]).should == t[:c]
245
+ end
246
+ end
247
+
248
+ specify "#div -> money / money (different currency)" do
249
+ ts = [
250
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => 1.625},
251
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => -1.625},
252
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => -1.625},
253
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => 1.625},
254
+ ]
255
+ ts.each do |t|
256
+ t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
257
+ t[:a].div(t[:b]).should == t[:c]
258
+ end
259
+ end
260
+
261
+ specify "#divmod -> money `divmod` fixnum" do
262
+ ts = [
263
+ {:a => Money.new( 13, :USD), :b => 4, :c => [Money.new( 3, :USD), Money.new( 1, :USD)]},
264
+ {:a => Money.new( 13, :USD), :b => -4, :c => [Money.new(-4, :USD), Money.new(-3, :USD)]},
265
+ {:a => Money.new(-13, :USD), :b => 4, :c => [Money.new(-4, :USD), Money.new( 3, :USD)]},
266
+ {:a => Money.new(-13, :USD), :b => -4, :c => [Money.new( 3, :USD), Money.new(-1, :USD)]},
267
+ ]
268
+ ts.each do |t|
269
+ t[:a].divmod(t[:b]).should == t[:c]
270
+ end
271
+ end
272
+
273
+ specify "#divmod -> money `divmod` money (same currency)" do
274
+ ts = [
275
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => [ 3, Money.new( 1, :USD)]},
276
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => [-4, Money.new(-3, :USD)]},
277
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => [-4, Money.new( 3, :USD)]},
278
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => [ 3, Money.new(-1, :USD)]},
279
+ ]
280
+ ts.each do |t|
281
+ t[:a].divmod(t[:b]).should == t[:c]
282
+ end
283
+ end
284
+
285
+ specify "#divmod -> money `divmod` money (different currency)" do
286
+ ts = [
287
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => [ 1, Money.new( 5, :USD)]},
288
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => [-2, Money.new(-3, :USD)]},
289
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => [-2, Money.new( 3, :USD)]},
290
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => [ 1, Money.new(-5, :USD)]},
291
+ ]
292
+ ts.each do |t|
293
+ t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
294
+ t[:a].divmod(t[:b]).should == t[:c]
295
+ end
296
+ end
297
+
298
+ specify "#modulo -> money `modulo` fixnum" do
299
+ ts = [
300
+ {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
301
+ {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
302
+ {:a => Money.new(-13, :USD), :b => 4, :c => Money.new( 3, :USD)},
303
+ {:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
304
+ ]
305
+ ts.each do |t|
306
+ t[:a].modulo(t[:b]).should == t[:c]
307
+ end
308
+ end
309
+
310
+ specify "#modulo -> money `modulo` money (same currency)" do
311
+ ts = [
312
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
313
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-3, :USD)},
314
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 3, :USD)},
315
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
316
+ ]
317
+ ts.each do |t|
318
+ t[:a].modulo(t[:b]).should == t[:c]
319
+ end
320
+ end
321
+
322
+ specify "#modulo -> money `modulo` money (different currency)" do
323
+ ts = [
324
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
325
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-3, :USD)},
326
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 3, :USD)},
327
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
328
+ ]
329
+ ts.each do |t|
330
+ t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
331
+ t[:a].modulo(t[:b]).should == t[:c]
332
+ end
333
+ end
334
+
335
+ specify "#% -> money % fixnum" do
336
+ ts = [
337
+ {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
338
+ {:a => Money.new( 13, :USD), :b => -4, :c => Money.new(-3, :USD)},
339
+ {:a => Money.new(-13, :USD), :b => 4, :c => Money.new( 3, :USD)},
340
+ {:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
341
+ ]
342
+ ts.each do |t|
343
+ (t[:a] % t[:b]).should == t[:c]
344
+ end
345
+ end
346
+
347
+ specify "#% -> money % money (same currency)" do
348
+ ts = [
349
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
350
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-3, :USD)},
351
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 3, :USD)},
352
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
353
+ ]
354
+ ts.each do |t|
355
+ (t[:a] % t[:b]).should == t[:c]
356
+ end
357
+ end
358
+
359
+ specify "#% -> money % money (different currency)" do
360
+ ts = [
361
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
362
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-3, :USD)},
363
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 3, :USD)},
364
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
365
+ ]
366
+ ts.each do |t|
367
+ t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
368
+ (t[:a] % t[:b]).should == t[:c]
369
+ end
370
+ end
371
+
372
+ specify "#remainder -> money `remainder` fixnum" do
373
+ ts = [
374
+ {:a => Money.new( 13, :USD), :b => 4, :c => Money.new( 1, :USD)},
375
+ {:a => Money.new( 13, :USD), :b => -4, :c => Money.new( 1, :USD)},
376
+ {:a => Money.new(-13, :USD), :b => 4, :c => Money.new(-1, :USD)},
377
+ {:a => Money.new(-13, :USD), :b => -4, :c => Money.new(-1, :USD)},
378
+ ]
379
+ ts.each do |t|
380
+ t[:a].remainder(t[:b]).should == t[:c]
381
+ end
382
+ end
383
+
384
+ specify "#remainder -> money `remainder` money (same currency)" do
385
+ ts = [
386
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :USD), :c => Money.new( 1, :USD)},
387
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :USD), :c => Money.new( 1, :USD)},
388
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :USD), :c => Money.new(-1, :USD)},
389
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :USD), :c => Money.new(-1, :USD)},
390
+ ]
391
+ ts.each do |t|
392
+ t[:a].remainder(t[:b]).should == t[:c]
393
+ end
394
+ end
395
+
396
+ specify "#remainder -> money `remainder` money (different currency)" do
397
+ ts = [
398
+ {:a => Money.new( 13, :USD), :b => Money.new( 4, :EUR), :c => Money.new( 5, :USD)},
399
+ {:a => Money.new( 13, :USD), :b => Money.new(-4, :EUR), :c => Money.new( 5, :USD)},
400
+ {:a => Money.new(-13, :USD), :b => Money.new( 4, :EUR), :c => Money.new(-5, :USD)},
401
+ {:a => Money.new(-13, :USD), :b => Money.new(-4, :EUR), :c => Money.new(-5, :USD)},
402
+ ]
403
+ ts.each do |t|
404
+ t[:b].should_receive(:exchange_to).once.with(t[:a].currency).and_return(Money.new(t[:b].cents * 2, :USD))
405
+ t[:a].remainder(t[:b]).should == t[:c]
406
+ end
407
+ end
408
+
409
+ specify "#abs correctly returns the absolute value as a new Money object" do
410
+ n = Money.new(-1, :USD)
411
+ n.abs.should == Money.new( 1, :USD)
412
+ n.should == Money.new(-1, :USD)
413
+ end
414
+
415
+ describe "Actions involving two Money objects" do
416
+ describe "if the other Money object has the same currency" do
417
+ specify "#<=> compares the two object amounts" do
418
+ (Money.new(1_00, "USD") <=> Money.new(1_00, "USD")).should == 0
419
+ (Money.new(1_00, "USD") <=> Money.new(99, "USD")).should > 0
420
+ (Money.new(1_00, "USD") <=> Money.new(2_00, "USD")).should < 0
421
+ end
422
+
423
+ specify "#+ adds other amount to current amount and returns a Money while retaining the currency" do
424
+ (Money.new(10_00, "USD") + Money.new(90, "USD")).should == Money.new(10_90, "USD")
425
+ end
426
+
427
+ specify "#- subtracts other amount from current amount and returns a Money while retaining the currency" do
428
+ (Money.new(10_00, "USD") - Money.new(90, "USD")).should == Money.new(9_10, "USD")
429
+ end
430
+
431
+ specify "#* should raise ArgumentError" do
432
+ lambda { Money.new(10_00, "USD") * Money.new(2, "USD") }.should raise_error(ArgumentError)
433
+ end
434
+
435
+ specify "#/ divides current amount by other amount and returns a Float" do
436
+ (Money.new(10_00, "USD") / Money.new(100_00, "USD")).should == 0.1
437
+ end
438
+ end
439
+
440
+ describe "if the other Money object has a different currency" do
441
+ specify "#<=> converts other object amount to current currency, then compares the two object amounts" do
442
+ target = Money.new(200_00, "EUR")
443
+ target.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(300_00, "USD"))
444
+ (Money.new(100_00, "USD") <=> target).should < 0
445
+
446
+ target = Money.new(200_00, "EUR")
447
+ target.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(100_00, "USD"))
448
+ (Money.new(100_00, "USD") <=> target).should == 0
449
+
450
+ target = Money.new(200_00, "EUR")
451
+ target.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(99_00, "USD"))
452
+ (Money.new(100_00, "USD") <=> target).should > 0
453
+ end
454
+
455
+ specify "#+ converts other object amount to current currency, then adds other amount to current amount and returns a Money" do
456
+ other = Money.new(90, "EUR")
457
+ other.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
458
+ (Money.new(10_00, "USD") + other).should == Money.new(19_00, "USD")
459
+ end
460
+
461
+ specify "#- converts other object amount to current currency, then subtracts other amount from current amount and returns a Money" do
462
+ other = Money.new(90, "EUR")
463
+ other.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(9_00, "USD"))
464
+ (Money.new(10_00, "USD") - other).should == Money.new(1_00, "USD")
465
+ end
466
+
467
+ specify "#* should raise ArgumentError" do
468
+ lambda { Money.new(10_00, "USD") * Money.new(10, "EUR") }.should raise_error(ArgumentError)
469
+ end
470
+
471
+ specify "#/ converts other object amount to current currency, then divides current amount by other amount and returns a Float" do
472
+ other = Money.new(1000, "EUR")
473
+ other.should_receive(:exchange_to).with(Money::Currency.new("USD")).and_return(Money.new(100_00, "USD"))
474
+ (Money.new(10_00, "USD") / other).should == 0.1
475
+ end
476
+ end
477
+ end
478
+
479
+ end