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,309 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Money, "parsing" do
6
+
7
+ bar = '{ "priority": 1, "iso_code": "BAR", "iso_numeric": "840", "name": "Dollar with 4 decimal places", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 10000, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": "," }'
8
+ eu4 = '{ "priority": 1, "iso_code": "EU4", "iso_numeric": "841", "name": "Euro with 4 decimal places", "symbol": "€", "subunit": "Cent", "subunit_to_unit": 10000, "symbol_first": true, "html_entity": "€", "decimal_mark": ",", "thousands_separator": "." }'
9
+
10
+ describe ".parse" do
11
+ it "parses european-formatted inputs under 10EUR" do
12
+ five_ninety_five = Money.new(595, 'EUR')
13
+
14
+ Money.parse('EUR 5,95').should == five_ninety_five
15
+ #TODO: try and handle these
16
+ #Money.parse('€5,95').should == five_ninety_five
17
+ #Money.parse('$5.95').should == five_ninety_five
18
+ end
19
+
20
+ it "parses european-formatted inputs with multiple thousands-seperators" do
21
+ Money.parse('EUR 1.234.567,89').should == Money.new(123456789, 'EUR')
22
+ Money.parse('EUR 1.111.234.567,89').should == Money.new(111123456789, 'EUR')
23
+ end
24
+
25
+ describe 'currency assumption' do
26
+ context 'opted in' do
27
+ before do
28
+ Money.assume_from_symbol = true
29
+ end
30
+ it "parses formatted inputs with the currency passed as a symbol" do
31
+ with_default_currency("EUR") do
32
+ Money.parse("$5.95").should == Money.new(595, 'USD')
33
+ end
34
+ Money.parse("€5.95").should == Money.new(595, 'EUR')
35
+ Money.parse(" €5.95 ").should == Money.new(595, 'EUR')
36
+ Money.parse("£9.99").should == Money.new(999, 'GBP')
37
+ end
38
+ it 'should assume default currency if not a recognised symbol' do
39
+ Money.parse("L9.99").should == Money.new(999, 'USD')
40
+ end
41
+ end
42
+ context 'opted out' do
43
+ before do
44
+ Money.assume_from_symbol = false
45
+ end
46
+ it "parses formatted inputs with the currency passed as a symbol but ignores the symbol" do
47
+ Money.parse("$5.95").should == Money.new(595, 'USD')
48
+ Money.parse("€5.95").should == Money.new(595, 'USD')
49
+ Money.parse(" €5.95 ").should == Money.new(595, 'USD')
50
+ Money.parse("£9.99").should == Money.new(999, 'USD')
51
+
52
+ end
53
+ end
54
+ it 'should opt out by default' do
55
+ Money.assume_from_symbol.should be_false
56
+ end
57
+ end
58
+
59
+ it "parses USD-formatted inputs under $10" do
60
+ five_ninety_five = Money.new(595, 'USD')
61
+
62
+ Money.parse(5.95).should == five_ninety_five
63
+ Money.parse('5.95').should == five_ninety_five
64
+ Money.parse('$5.95').should == five_ninety_five
65
+ Money.parse("\n $5.95 \n").should == five_ninety_five
66
+ Money.parse('$ 5.95').should == five_ninety_five
67
+ Money.parse('$5.95 ea.').should == five_ninety_five
68
+ Money.parse('$5.95, each').should == five_ninety_five
69
+ end
70
+
71
+ it "parses USD-formatted inputs with multiple thousands-seperators" do
72
+ Money.parse('1,234,567.89').should == Money.new(123456789, 'USD')
73
+ Money.parse('1,111,234,567.89').should == Money.new(111123456789, 'USD')
74
+ end
75
+
76
+ it "does not return a price if there is a price range" do
77
+ expect { Money.parse('$5.95-10.95') }.to raise_error ArgumentError
78
+ expect { Money.parse('$5.95 - 10.95') }.to raise_error ArgumentError
79
+ expect { Money.parse('$5.95 - $10.95') }.to raise_error ArgumentError
80
+ end
81
+
82
+ it "does not return a price for completely invalid input" do
83
+ # TODO: shouldn't these throw an error instead of being considered
84
+ # equal to $0.0?
85
+ empty_price = Money.new(0, 'USD')
86
+
87
+ Money.parse(nil).should == empty_price
88
+ Money.parse('hellothere').should == empty_price
89
+ Money.parse('').should == empty_price
90
+ end
91
+
92
+ it "handles negative inputs" do
93
+ five_ninety_five = Money.new(595, 'USD')
94
+
95
+ Money.parse("$-5.95").should == -five_ninety_five
96
+ Money.parse("-$5.95").should == -five_ninety_five
97
+ Money.parse("$5.95-").should == -five_ninety_five
98
+ end
99
+
100
+ it "raises ArgumentError when unable to detect polarity" do
101
+ expect { Money.parse('-$5.95-') }.to raise_error ArgumentError
102
+ end
103
+
104
+ it "parses correctly strings with exactly 3 decimal digits" do
105
+ Money.parse("6,534", "EUR").should == Money.new(653, "EUR")
106
+ end
107
+
108
+ context "custom currencies with 4 decimal places" do
109
+ before :each do
110
+ Money::Currency.register(JSON.parse(bar, :symbolize_names => true))
111
+ Money::Currency.register(JSON.parse(eu4, :symbolize_names => true))
112
+ end
113
+
114
+ # String#to_money(Currency) is equivalent to Money.parse(String, Currency)
115
+ it "parses strings respecting subunit to unit, decimal and thousands separator" do
116
+ "$0.4".to_money("BAR").should == Money.new(4000, "BAR")
117
+ "€0,4".to_money("EU4").should == Money.new(4000, "EU4")
118
+
119
+ "$0.04".to_money("BAR").should == Money.new(400, "BAR")
120
+ "€0,04".to_money("EU4").should == Money.new(400, "EU4")
121
+
122
+ "$0.004".to_money("BAR").should == Money.new(40, "BAR")
123
+ "€0,004".to_money("EU4").should == Money.new(40, "EU4")
124
+
125
+ "$0.0004".to_money("BAR").should == Money.new(4, "BAR")
126
+ "€0,0004".to_money("EU4").should == Money.new(4, "EU4")
127
+
128
+ "$0.0024".to_money("BAR").should == Money.new(24, "BAR")
129
+ "€0,0024".to_money("EU4").should == Money.new(24, "EU4")
130
+
131
+ "$0.0324".to_money("BAR").should == Money.new(324, "BAR")
132
+ "€0,0324".to_money("EU4").should == Money.new(324, "EU4")
133
+
134
+ "$0.5324".to_money("BAR").should == Money.new(5324, "BAR")
135
+ "€0,5324".to_money("EU4").should == Money.new(5324, "EU4")
136
+
137
+ "$6.5324".to_money("BAR").should == Money.new(65324, "BAR")
138
+ "€6,5324".to_money("EU4").should == Money.new(65324, "EU4")
139
+
140
+ "$86.5324".to_money("BAR").should == Money.new(865324, "BAR")
141
+ "€86,5324".to_money("EU4").should == Money.new(865324, "EU4")
142
+
143
+ "$186.5324".to_money("BAR").should == Money.new(1865324, "BAR")
144
+ "€186,5324".to_money("EU4").should == Money.new(1865324, "EU4")
145
+
146
+ "$3,331.0034".to_money("BAR").should == Money.new(33310034, "BAR")
147
+ "€3.331,0034".to_money("EU4").should == Money.new(33310034, "EU4")
148
+
149
+ "$8,883,331.0034".to_money("BAR").should == Money.new(88833310034, "BAR")
150
+ "€8.883.331,0034".to_money("EU4").should == Money.new(88833310034, "EU4")
151
+ end
152
+ end
153
+ end
154
+
155
+ describe ".from_string" do
156
+ it "converts given amount to cents" do
157
+ Money.from_string("1").should == Money.new(1_00)
158
+ Money.from_string("1").should == Money.new(1_00, "USD")
159
+ Money.from_string("1", "EUR").should == Money.new(1_00, "EUR")
160
+ end
161
+
162
+ it "respects :subunit_to_unit currency property" do
163
+ Money.from_string("1", "USD").should == Money.new(1_00, "USD")
164
+ Money.from_string("1", "TND").should == Money.new(1_000, "TND")
165
+ Money.from_string("1", "CLP").should == Money.new(1, "CLP")
166
+ end
167
+
168
+ it "accepts a currency options" do
169
+ m = Money.from_string("1")
170
+ m.currency.should == Money.default_currency
171
+
172
+ m = Money.from_string("1", Money::Currency.wrap("EUR"))
173
+ m.currency.should == Money::Currency.wrap("EUR")
174
+
175
+ m = Money.from_string("1", "EUR")
176
+ m.currency.should == Money::Currency.wrap("EUR")
177
+ end
178
+ end
179
+
180
+ describe ".from_fixnum" do
181
+ it "converts given amount to cents" do
182
+ Money.from_fixnum(1).should == Money.new(1_00)
183
+ Money.from_fixnum(1).should == Money.new(1_00, "USD")
184
+ Money.from_fixnum(1, "EUR").should == Money.new(1_00, "EUR")
185
+ end
186
+
187
+ it "should respect :subunit_to_unit currency property" do
188
+ Money.from_fixnum(1, "USD").should == Money.new(1_00, "USD")
189
+ Money.from_fixnum(1, "TND").should == Money.new(1_000, "TND")
190
+ Money.from_fixnum(1, "CLP").should == Money.new(1, "CLP")
191
+ end
192
+
193
+ it "accepts a currency options" do
194
+ m = Money.from_fixnum(1)
195
+ m.currency.should == Money.default_currency
196
+
197
+ m = Money.from_fixnum(1, Money::Currency.wrap("EUR"))
198
+ m.currency.should == Money::Currency.wrap("EUR")
199
+
200
+ m = Money.from_fixnum(1, "EUR")
201
+ m.currency.should == Money::Currency.wrap("EUR")
202
+ end
203
+ end
204
+
205
+ describe ".from_float" do
206
+ it "converts given amount to cents" do
207
+ Money.from_float(1.2).should == Money.new(1_20)
208
+ Money.from_float(1.2).should == Money.new(1_20, "USD")
209
+ Money.from_float(1.2, "EUR").should == Money.new(1_20, "EUR")
210
+ end
211
+
212
+ it "respects :subunit_to_unit currency property" do
213
+ Money.from_float(1.2, "USD").should == Money.new(1_20, "USD")
214
+ Money.from_float(1.2, "TND").should == Money.new(1_200, "TND")
215
+ Money.from_float(1.2, "CLP").should == Money.new(1, "CLP")
216
+ end
217
+
218
+ it "accepts a currency options" do
219
+ m = Money.from_float(1.2)
220
+ m.currency.should == Money.default_currency
221
+
222
+ m = Money.from_float(1.2, Money::Currency.wrap("EUR"))
223
+ m.currency.should == Money::Currency.wrap("EUR")
224
+
225
+ m = Money.from_float(1.2, "EUR")
226
+ m.currency.should == Money::Currency.wrap("EUR")
227
+ end
228
+ end
229
+
230
+ describe ".from_bigdecimal" do
231
+ it "converts given amount to cents" do
232
+ Money.from_bigdecimal(BigDecimal.new("1")).should == Money.new(1_00)
233
+ Money.from_bigdecimal(BigDecimal.new("1")).should == Money.new(1_00, "USD")
234
+ Money.from_bigdecimal(BigDecimal.new("1"), "EUR").should == Money.new(1_00, "EUR")
235
+ end
236
+
237
+ it "respects :subunit_to_unit currency property" do
238
+ Money.from_bigdecimal(BigDecimal.new("1"), "USD").should == Money.new(1_00, "USD")
239
+ Money.from_bigdecimal(BigDecimal.new("1"), "TND").should == Money.new(1_000, "TND")
240
+ Money.from_bigdecimal(BigDecimal.new("1"), "CLP").should == Money.new(1, "CLP")
241
+ end
242
+
243
+ it "accepts a currency options" do
244
+ m = Money.from_bigdecimal(BigDecimal.new("1"))
245
+ m.currency.should == Money.default_currency
246
+
247
+ m = Money.from_bigdecimal(BigDecimal.new("1"), Money::Currency.wrap("EUR"))
248
+ m.currency.should == Money::Currency.wrap("EUR")
249
+
250
+ m = Money.from_bigdecimal(BigDecimal.new("1"), "EUR")
251
+ m.currency.should == Money::Currency.wrap("EUR")
252
+ end
253
+ end
254
+
255
+ describe ".from_numeric" do
256
+ it "converts given amount to cents" do
257
+ Money.from_numeric(1).should == Money.new(1_00)
258
+ Money.from_numeric(1.0).should == Money.new(1_00)
259
+ Money.from_numeric(BigDecimal.new("1")).should == Money.new(1_00)
260
+ end
261
+
262
+ it "raises ArgumentError with unsupported argument" do
263
+ expect { Money.from_numeric("100") }.to raise_error(ArgumentError)
264
+ end
265
+
266
+ it "optimizes workload" do
267
+ Money.should_receive(:from_fixnum).with(1, "USD").and_return(Money.new(1_00, "USD"))
268
+ Money.from_numeric(1, "USD").should == Money.new(1_00, "USD")
269
+ Money.should_receive(:from_bigdecimal).with(BigDecimal.new("1.0"), "USD").and_return(Money.new(1_00, "USD"))
270
+ Money.from_numeric(1.0, "USD").should == Money.new(1_00, "USD")
271
+ end
272
+
273
+ it "respects :subunit_to_unit currency property" do
274
+ Money.from_numeric(1, "USD").should == Money.new(1_00, "USD")
275
+ Money.from_numeric(1, "TND").should == Money.new(1_000, "TND")
276
+ Money.from_numeric(1, "CLP").should == Money.new(1, "CLP")
277
+ end
278
+
279
+ it "accepts a bank option" do
280
+ Money.from_numeric(1).should == Money.new(1_00)
281
+ Money.from_numeric(1).should == Money.new(1_00, "USD")
282
+ Money.from_numeric(1, "EUR").should == Money.new(1_00, "EUR")
283
+ end
284
+
285
+ it "accepts a currency options" do
286
+ m = Money.from_numeric(1)
287
+ m.currency.should == Money.default_currency
288
+
289
+ m = Money.from_numeric(1, Money::Currency.wrap("EUR"))
290
+ m.currency.should == Money::Currency.wrap("EUR")
291
+
292
+ m = Money.from_numeric(1, "EUR")
293
+ m.currency.should == Money::Currency.wrap("EUR")
294
+ end
295
+ end
296
+
297
+ describe ".extract_cents" do
298
+ it "correctly treats pipe marks '|' in input (regression test)" do
299
+ Money.extract_cents('100|0').should == Money.extract_cents('100!0')
300
+ end
301
+ end
302
+
303
+ context "given the same inputs to .parse and .from_*" do
304
+ it "gives the same results" do
305
+ 4.635.to_money.should == "4.635".to_money
306
+ end
307
+ end
308
+
309
+ end
@@ -0,0 +1,497 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Money do
6
+ describe ".new" do
7
+ it "rounds the given cents to an integer" do
8
+ Money.new(1.00, "USD").cents.should == 1
9
+ Money.new(1.01, "USD").cents.should == 1
10
+ Money.new(1.50, "USD").cents.should == 2
11
+ end
12
+
13
+ it "is associated to the singleton instance of Bank::VariableExchange by default" do
14
+ Money.new(0).bank.should be(Money::Bank::VariableExchange.instance)
15
+ end
16
+
17
+ it "handles Rationals" do
18
+ n = Rational(1)
19
+ Money.new(n).cents.should == 1
20
+ end
21
+
22
+ it "handles Floats" do
23
+ n = Float("1")
24
+ Money.new(n).cents.should == 1
25
+ end
26
+
27
+ context "infinite_precision = true" do
28
+ before do
29
+ Money.infinite_precision = true
30
+ end
31
+
32
+ after do
33
+ Money.infinite_precision = false
34
+ end
35
+
36
+ it "doesn't round cents" do
37
+ Money.new(1.01, "USD").cents.should == BigDecimal("1.01")
38
+ Money.new(1.50, "USD").cents.should == BigDecimal("1.50")
39
+ end
40
+ end
41
+ end
42
+
43
+ describe ".new_with_dollars" do
44
+ it "is synonym of #new_with_amount" do
45
+ MoneyExpectation = Class.new(Money)
46
+ def MoneyExpectation.new_with_amount *args
47
+ args
48
+ end
49
+ MoneyExpectation.new_with_dollars("expectation").should == ["expectation"]
50
+ end
51
+ end
52
+
53
+ describe ".new_with_amount" do
54
+ it "converts given amount to cents" do
55
+ Money.new_with_amount(1).should == Money.new(100)
56
+ Money.new_with_amount(1, "USD").should == Money.new(100, "USD")
57
+ Money.new_with_amount(1, "EUR").should == Money.new(100, "EUR")
58
+ end
59
+
60
+ it "respects :subunit_to_unit currency property" do
61
+ Money.new_with_amount(1, "USD").should == Money.new(1_00, "USD")
62
+ Money.new_with_amount(1, "TND").should == Money.new(1_000, "TND")
63
+ Money.new_with_amount(1, "CLP").should == Money.new(1, "CLP")
64
+ end
65
+
66
+ it "does not loose precision" do
67
+ Money.new_with_amount(1234).cents.should == 1234_00
68
+ Money.new_with_amount(100.37).cents.should == 100_37
69
+ Money.new_with_amount(BigDecimal.new('1234')).cents.should == 1234_00
70
+ end
71
+
72
+ it "accepts optional currency" do
73
+ m = Money.new_with_amount(1)
74
+ m.currency.should == Money.default_currency
75
+
76
+ m = Money.new_with_amount(1, Money::Currency.wrap("EUR"))
77
+ m.currency.should == Money::Currency.wrap("EUR")
78
+
79
+ m = Money.new_with_amount(1, "EUR")
80
+ m.currency.should == Money::Currency.wrap("EUR")
81
+ end
82
+
83
+ it "accepts optional bank" do
84
+ m = Money.new_with_amount(1)
85
+ m.bank.should == Money.default_bank
86
+
87
+ m = Money.new_with_amount(1, "EUR", bank = Object.new)
88
+ m.bank.should == bank
89
+ end
90
+
91
+ it "is associated to the singleton instance of Bank::VariableExchange by default" do
92
+ Money.new_with_amount(0).bank.should be(Money::Bank::VariableExchange.instance)
93
+ end
94
+ end
95
+
96
+ describe ".empty" do
97
+ it "creates a new Money object of 0 cents" do
98
+ Money.empty.should == Money.new(0)
99
+ end
100
+ end
101
+
102
+ describe ".ca_dollar" do
103
+ it "creates a new Money object of the given value in CAD" do
104
+ Money.ca_dollar(50).should == Money.new(50, "CAD")
105
+ end
106
+ end
107
+
108
+ describe ".us_dollar" do
109
+ it "creates a new Money object of the given value in USD" do
110
+ Money.us_dollar(50).should == Money.new(50, "USD")
111
+ end
112
+ end
113
+
114
+ describe ".euro" do
115
+ it "creates a new Money object of the given value in EUR" do
116
+ Money.euro(50).should == Money.new(50, "EUR")
117
+ end
118
+ end
119
+
120
+ describe ".add_rate" do
121
+ before do
122
+ @default_bank = Money.default_bank
123
+ Money.default_bank = Money::Bank::VariableExchange.new
124
+ end
125
+
126
+ after do
127
+ Money.default_bank = @default_bank
128
+ end
129
+
130
+ it "saves rate into current bank" do
131
+ Money.add_rate("EUR", "USD", 10)
132
+ Money.new(10_00, "EUR").exchange_to("USD").should == Money.new(100_00, "USD")
133
+ end
134
+ end
135
+
136
+ describe "#cents" do
137
+ it "is a synonym of #fractional" do
138
+ expectation = Money.new(0)
139
+ def expectation.fractional
140
+ "expectation"
141
+ end
142
+ expectation.cents.should == "expectation"
143
+ end
144
+ end
145
+
146
+ describe "#fractional" do
147
+ it "returns the amount in fractional unit" do
148
+ Money.new(1_00).fractional.should == 1_00
149
+ Money.new_with_amount(1).fractional.should == 1_00
150
+ end
151
+
152
+ it "stores fractional as an integer regardless of what is passed into the constructor" do
153
+ [ Money.new(100), 1.to_money, 1.00.to_money, BigDecimal('1.00').to_money ].each do |m|
154
+ m.fractional.should == 100
155
+ m.fractional.should be_a(Fixnum)
156
+ end
157
+ end
158
+
159
+ context "loading a serialized Money via YAML" do
160
+ it "uses BigDecimal when rounding" do
161
+ serialized = <<YAML
162
+ !ruby/object:Money
163
+ fractional: 249.5
164
+ currency: !ruby/object:Money::Currency
165
+ id: :eur
166
+ priority: 2
167
+ iso_code: EUR
168
+ name: Euro
169
+ symbol: €
170
+ alternate_symbols: []
171
+ subunit: Cent
172
+ subunit_to_unit: 100
173
+ symbol_first: true
174
+ html_entity: ! '&#x20AC;'
175
+ decimal_mark: ! ','
176
+ thousands_separator: .
177
+ iso_numeric: '978'
178
+ mutex: !ruby/object:Mutex {}
179
+ last_updated: 2012-11-23 20:41:47.454438399 +02:00
180
+ YAML
181
+ m = YAML::load serialized
182
+ m.should be_a(Money)
183
+ m.class.infinite_precision.should == false
184
+ m.fractional.should == 250 # 249.5 rounded up
185
+ m.fractional.should be_a(Integer)
186
+ end
187
+ end
188
+
189
+ context "user changes rounding_mode" do
190
+ after do
191
+ Money.rounding_mode = BigDecimal::ROUND_HALF_EVEN
192
+ end
193
+
194
+ it "respects the rounding_mode" do
195
+ Money.rounding_mode = BigDecimal::ROUND_DOWN
196
+ Money.new(1.9).fractional.should == 1
197
+
198
+ Money.rounding_mode = BigDecimal::ROUND_UP
199
+ Money.new(1.1).fractional.should == 2
200
+ end
201
+ end
202
+
203
+ context "infinite_precision = true" do
204
+ before do
205
+ Money.infinite_precision = true
206
+ end
207
+
208
+ after do
209
+ Money.infinite_precision = false
210
+ end
211
+
212
+ it "returns the amount in fractional unit" do
213
+ Money.new(1_00).fractional.should == BigDecimal("100")
214
+ Money.new_with_amount(1).fractional.should == BigDecimal("100")
215
+ end
216
+
217
+ it "stores in fractional unit as an integer regardless of what is passed into the constructor" do
218
+ [ Money.new(100), 1.to_money, 1.00.to_money, BigDecimal('1.00').to_money ].each do |m|
219
+ m.fractional.should == BigDecimal("100")
220
+ m.fractional.should be_a(BigDecimal)
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ describe "#amount" do
227
+ it "returns the amount of cents as dollars" do
228
+ Money.new(1_00).amount.should == 1
229
+ Money.new_with_amount(1).amount.should == 1
230
+ end
231
+
232
+ it "respects :subunit_to_unit currency property" do
233
+ Money.new(1_00, "USD").amount.should == 1
234
+ Money.new(1_000, "TND").amount.should == 1
235
+ Money.new(1, "CLP").amount.should == 1
236
+ end
237
+
238
+ it "does not loose precision" do
239
+ Money.new(100_37).amount.should == 100.37
240
+ Money.new_with_amount(100.37).amount.should == 100.37
241
+ end
242
+ end
243
+
244
+ describe "#dollars" do
245
+ it "is synonym of #amount" do
246
+ m = Money.new(0)
247
+
248
+ # Make a small expectation
249
+ def m.amount
250
+ 5
251
+ end
252
+
253
+ m.dollars.should == 5
254
+ end
255
+ end
256
+
257
+ describe "#currency" do
258
+ it "returns the currency object" do
259
+ Money.new(1_00, "USD").currency.should == Money::Currency.new("USD")
260
+ end
261
+ end
262
+
263
+ describe "#currency_as_string" do
264
+ it "returns the iso_code of the currency object" do
265
+ Money.new(1_00, "USD").currency_as_string.should == "USD"
266
+ Money.new(1_00, "EUR").currency_as_string.should == "EUR"
267
+ end
268
+ end
269
+
270
+ describe "#currency_as_string=" do
271
+ it "sets the currency object using the provided string" do
272
+ money = Money.new(100_00, "USD")
273
+ money.currency_as_string = "EUR"
274
+ money.currency.should == Money::Currency.new("EUR")
275
+ money.currency_as_string = "YEN"
276
+ money.currency.should == Money::Currency.new("YEN")
277
+ end
278
+ end
279
+
280
+ describe "#hash=" do
281
+ it "returns the same value for equal objects" do
282
+ Money.new(1_00, "EUR").hash.should == Money.new(1_00, "EUR").hash
283
+ Money.new(2_00, "USD").hash.should == Money.new(2_00, "USD").hash
284
+ Money.new(1_00, "EUR").hash.should_not == Money.new(2_00, "EUR").hash
285
+ Money.new(1_00, "EUR").hash.should_not == Money.new(1_00, "USD").hash
286
+ Money.new(1_00, "EUR").hash.should_not == Money.new(2_00, "USD").hash
287
+ end
288
+
289
+ it "can be used to return the intersection of Money object arrays" do
290
+ intersection = [Money.new(1_00, "EUR"), Money.new(1_00, "USD")] & [Money.new(1_00, "EUR")]
291
+ intersection.should == [Money.new(1_00, "EUR")]
292
+ end
293
+ end
294
+
295
+ describe "#symbol" do
296
+ it "works as documented" do
297
+ currency = Money::Currency.new("EUR")
298
+ currency.should_receive(:symbol).and_return("€")
299
+ Money.empty(currency).symbol.should == "€"
300
+
301
+ currency = Money::Currency.new("EUR")
302
+ currency.should_receive(:symbol).and_return(nil)
303
+ Money.empty(currency).symbol.should == "¤"
304
+ end
305
+ end
306
+
307
+ describe "#to_s" do
308
+ it "works as documented" do
309
+ Money.new(10_00).to_s.should == "10.00"
310
+ Money.new(400_08).to_s.should == "400.08"
311
+ Money.new(-237_43).to_s.should == "-237.43"
312
+ end
313
+
314
+ it "respects :subunit_to_unit currency property" do
315
+ Money.new(10_00, "BHD").to_s.should == "1.000"
316
+ Money.new(10_00, "CNY").to_s.should == "10.00"
317
+ end
318
+
319
+ it "does not have decimal when :subunit_to_unit == 1" do
320
+ Money.new(10_00, "CLP").to_s.should == "1000"
321
+ end
322
+
323
+ it "does not work when :subunit_to_unit == 5" do
324
+ Money.new(10_00, "MGA").to_s.should == "200.0"
325
+ end
326
+
327
+ it "respects :decimal_mark" do
328
+ Money.new(10_00, "BRL").to_s.should == "10,00"
329
+ end
330
+
331
+ context "infinite_precision = true" do
332
+ before do
333
+ Money.infinite_precision = true
334
+ end
335
+
336
+ after do
337
+ Money.infinite_precision = false
338
+ end
339
+
340
+ it "shows fractional cents" do
341
+ Money.new(1.05, "USD").to_s.should == "0.0105"
342
+ end
343
+
344
+ it "suppresses fractional cents when there is none" do
345
+ Money.new(1.0, "USD").to_s.should == "0.01"
346
+ end
347
+ end
348
+ end
349
+
350
+ describe "#to_d" do
351
+ it "works as documented" do
352
+ decimal = Money.new(10_00).to_d
353
+ decimal.should be_a(BigDecimal)
354
+ decimal.should == 10.0
355
+ end
356
+
357
+ it "respects :subunit_to_unit currency property" do
358
+ decimal = Money.new(10_00, "BHD").to_d
359
+ decimal.should be_a(BigDecimal)
360
+ decimal.should == 1.0
361
+ end
362
+
363
+ it "works with float :subunit_to_unit currency property" do
364
+ money = Money.new(10_00, "BHD")
365
+ money.currency.stub(:subunit_to_unit).and_return(1000.0)
366
+
367
+ decimal = money.to_d
368
+ decimal.should be_a(BigDecimal)
369
+ decimal.should == 1.0
370
+ end
371
+ end
372
+
373
+ describe "#to_f" do
374
+ it "works as documented" do
375
+ Money.new(10_00).to_f.should == 10.0
376
+ end
377
+
378
+ it "respects :subunit_to_unit currency property" do
379
+ Money.new(10_00, "BHD").to_f.should == 1.0
380
+ end
381
+ end
382
+
383
+ describe "#to_money" do
384
+ it "works as documented" do
385
+ money = Money.new(10_00, "DKK")
386
+ money.should == money.to_money
387
+ money.should == money.to_money("DKK")
388
+ money.bank.should_receive(:exchange_with).with(Money.new(10_00, Money::Currency.new("DKK")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
389
+ money.to_money("EUR").should == Money.new(200_00, "EUR")
390
+ end
391
+ end
392
+
393
+ describe "#exchange_to" do
394
+ it "exchanges the amount via its exchange bank" do
395
+ money = Money.new(100_00, "USD")
396
+ money.bank.should_receive(:exchange_with).with(Money.new(100_00, Money::Currency.new("USD")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
397
+ money.exchange_to("EUR")
398
+ end
399
+
400
+ it "exchanges the amount properly" do
401
+ money = Money.new(100_00, "USD")
402
+ money.bank.should_receive(:exchange_with).with(Money.new(100_00, Money::Currency.new("USD")), Money::Currency.new("EUR")).and_return(Money.new(200_00, Money::Currency.new('EUR')))
403
+ money.exchange_to("EUR").should == Money.new(200_00, "EUR")
404
+ end
405
+ end
406
+
407
+ describe "#allocate" do
408
+ it "takes no action when one gets all" do
409
+ Money.us_dollar(005).allocate([1.0]).should == [Money.us_dollar(5)]
410
+ end
411
+
412
+ it "keeps currencies intact" do
413
+ Money.ca_dollar(005).allocate([1]).should == [Money.ca_dollar(5)]
414
+ end
415
+
416
+ it "does not loose pennies" do
417
+ moneys = Money.us_dollar(5).allocate([0.3, 0.7])
418
+ moneys[0].should == Money.us_dollar(2)
419
+ moneys[1].should == Money.us_dollar(3)
420
+ end
421
+
422
+ it "does not loose pennies" do
423
+ moneys = Money.us_dollar(100).allocate([0.333, 0.333, 0.333])
424
+ moneys[0].cents.should == 34
425
+ moneys[1].cents.should == 33
426
+ moneys[2].cents.should == 33
427
+ end
428
+
429
+ it "requires total to be less then 1" do
430
+ expect { Money.us_dollar(0.05).allocate([0.5, 0.6]) }.to raise_error(ArgumentError)
431
+ end
432
+
433
+ context "infinite_precision = true" do
434
+ before do
435
+ Money.infinite_precision = true
436
+ end
437
+
438
+ after do
439
+ Money.infinite_precision = false
440
+ end
441
+
442
+ it "allows for fractional cents allocation" do
443
+ one_third = BigDecimal("1") / BigDecimal("3")
444
+
445
+ moneys = Money.new(100).allocate([one_third, one_third, one_third])
446
+ moneys[0].cents.should == one_third * BigDecimal("100")
447
+ moneys[1].cents.should == one_third * BigDecimal("100")
448
+ moneys[2].cents.should == one_third * BigDecimal("100")
449
+ end
450
+ end
451
+ end
452
+
453
+ describe "#split" do
454
+ it "needs at least one party" do
455
+ expect { Money.us_dollar(1).split(0) }.to raise_error(ArgumentError)
456
+ expect { Money.us_dollar(1).split(-1) }.to raise_error(ArgumentError)
457
+ end
458
+
459
+ it "gives 1 cent to both people if we start with 2" do
460
+ Money.us_dollar(2).split(2).should == [Money.us_dollar(1), Money.us_dollar(1)]
461
+ end
462
+
463
+ it "may distribute no money to some parties if there isnt enough to go around" do
464
+ Money.us_dollar(2).split(3).should == [Money.us_dollar(1), Money.us_dollar(1), Money.us_dollar(0)]
465
+ end
466
+
467
+ it "does not lose pennies" do
468
+ Money.us_dollar(5).split(2).should == [Money.us_dollar(3), Money.us_dollar(2)]
469
+ end
470
+
471
+ it "splits a dollar" do
472
+ moneys = Money.us_dollar(100).split(3)
473
+ moneys[0].cents.should == 34
474
+ moneys[1].cents.should == 33
475
+ moneys[2].cents.should == 33
476
+ end
477
+
478
+ context "infinite_precision = true" do
479
+ before do
480
+ Money.infinite_precision = true
481
+ end
482
+
483
+ after do
484
+ Money.infinite_precision = false
485
+ end
486
+
487
+ it "allows for splitting by fractional cents" do
488
+ thirty_three_and_one_third = BigDecimal("100") / BigDecimal("3")
489
+
490
+ moneys = Money.new(100).split(3)
491
+ moneys[0].cents.should == thirty_three_and_one_third
492
+ moneys[1].cents.should == thirty_three_and_one_third
493
+ moneys[2].cents.should == thirty_three_and_one_third
494
+ end
495
+ end
496
+ end
497
+ end