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.
@@ -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