money-rails 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module MoneyRails
2
- VERSION = "0.8.1"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.require_path = "lib"
28
28
 
29
- s.add_dependency "money", "~> 5.1.0"
29
+ s.add_dependency "money", "~> 6.0.0"
30
30
  s.add_dependency "activesupport", ">= 3.0"
31
31
  s.add_dependency "railties", ">= 3.0"
32
32
 
@@ -5,11 +5,14 @@ class Sub < Product; end
5
5
  if defined? ActiveRecord
6
6
  describe MoneyRails::ActiveRecord::Monetizable do
7
7
  describe "monetize" do
8
- before :each do
9
- @product = Product.create(:price_cents => 3000, :discount => 150,
10
- :bonus_cents => 200, :optional_price => 100,
11
- :sale_price_amount => 1200)
12
- @service = Service.create(:charge_cents => 2000, :discount_cents => 120)
8
+ let(:product) do
9
+ Product.create(:price_cents => 3000, :discount => 150,
10
+ :bonus_cents => 200, :optional_price => 100,
11
+ :sale_price_amount => 1200)
12
+ end
13
+
14
+ let(:service) do
15
+ Service.create(:charge_cents => 2000, :discount_cents => 120)
13
16
  end
14
17
 
15
18
  it "should be inherited by subclasses" do
@@ -17,114 +20,184 @@ if defined? ActiveRecord
17
20
  end
18
21
 
19
22
  it "attaches a Money object to model field" do
20
- @product.price.should be_an_instance_of(Money)
21
- @product.discount_value.should be_an_instance_of(Money)
22
- @product.bonus.should be_an_instance_of(Money)
23
+ product.price.should be_an_instance_of(Money)
24
+ product.discount_value.should be_an_instance_of(Money)
25
+ product.bonus.should be_an_instance_of(Money)
23
26
  end
24
27
 
25
28
  it "returns the expected money amount as a Money object" do
26
- @product.price.should == Money.new(3000, "USD")
29
+ product.price.should == Money.new(3000, "USD")
27
30
  end
28
31
 
29
32
  it "assigns the correct value from a Money object" do
30
- @product.price = Money.new(3210, "USD")
31
- @product.save.should be_true
32
- @product.price_cents.should == 3210
33
+ product.price = Money.new(3210, "USD")
34
+ product.save.should be_true
35
+ product.price_cents.should == 3210
33
36
  end
34
37
 
35
38
  it "assigns the correct value from a Money object using create" do
36
- @product = Product.create(:price => Money.new(3210, "USD"), :discount => 150,
39
+ product = Product.create(:price => Money.new(3210, "USD"), :discount => 150,
37
40
  :bonus_cents => 200, :optional_price => 100)
38
- @product.valid?.should be_true
39
- @product.price_cents.should == 3210
41
+ product.valid?.should be_true
42
+ product.price_cents.should == 3210
40
43
  end
41
44
 
42
45
  it "updates correctly from a Money object using update_attributes" do
43
- @product.update_attributes(:price => Money.new(215, "USD")).should be_true
44
- @product.price_cents.should == 215
46
+ product.update_attributes(:price => Money.new(215, "USD")).should be_true
47
+ product.price_cents.should == 215
45
48
  end
46
49
 
47
50
  it "respects :as argument" do
48
- @product.discount_value.should == Money.new(150, "USD")
51
+ product.discount_value.should == Money.new(150, "USD")
49
52
  end
50
53
 
51
54
  it "uses numericality validation" do
52
- @product.price_cents = "foo"
53
- @product.save.should be_false
55
+ product.price_cents = "foo"
56
+ product.save.should be_false
54
57
 
55
- @product.price_cents = 2000
56
- @product.save.should be_true
58
+ product.price_cents = 2000
59
+ product.save.should be_true
60
+ end
61
+
62
+ it "skips numericality validation when disabled" do
63
+ product.invalid_price_cents = 'not_valid'
64
+ product.save.should be_true
57
65
  end
58
66
 
59
67
  it "respects numericality validation when using update_attributes" do
60
- @product.update_attributes(:price_cents => "some text").should be_false
61
- @product.update_attributes(:price_cents => 2000).should be_true
68
+ product.update_attributes(:price_cents => "some text").should be_false
69
+ product.update_attributes(:price_cents => 2000).should be_true
62
70
  end
63
71
 
64
72
  it "uses numericality validation on money attribute" do
65
- @product.price = "some text"
66
- @product.save.should be_false
73
+ product.price = "some text"
74
+ product.save.should be_false
67
75
 
68
- @product.price = Money.new(320, "USD")
69
- @product.save.should be_true
76
+ product.price = Money.new(320, "USD")
77
+ product.save.should be_true
70
78
 
71
- @product.sale_price = "12.34"
72
- @product.sale_price_currency_code = 'EUR'
73
- @product.valid?.should be_true
79
+ product.sale_price = "12.34"
80
+ product.sale_price_currency_code = 'EUR'
81
+ product.valid?.should be_true
74
82
  end
75
83
 
76
84
  it "fails validation with the proper error message if money value is invalid decimal" do
77
- @product.price = "12.23.24"
78
- @product.save.should be_false
79
- @product.errors[:price].first.should match(/Must be a valid/)
85
+ product.price = "12.23.24"
86
+ product.save.should be_false
87
+ product.errors[:price].first.should match(/Must be a valid/)
80
88
  end
81
89
 
82
90
  it "fails validation with the proper error message if money value is nothing but periods" do
83
- @product.price = "..."
84
- @product.save.should be_false
85
- @product.errors[:price].first.should match(/Must be a valid/)
91
+ product.price = "..."
92
+ product.save.should be_false
93
+ product.errors[:price].first.should match(/Must be a valid/)
86
94
  end
87
95
 
88
96
  it "fails validation with the proper error message if money value has invalid thousands part" do
89
- @product.price = "12,23.24"
90
- @product.save.should be_false
91
- @product.errors[:price].first.should match(/Must be a valid/)
97
+ product.price = "12,23.24"
98
+ product.save.should be_false
99
+ product.errors[:price].first.should match(/Must be a valid/)
100
+ end
101
+
102
+ it "passes validation if money value is a Float and the currency decimal mark is not period" do
103
+ # The corresponding String would be "12,34" euros
104
+ service.discount = 12.34
105
+ service.save.should be_true
106
+ end
107
+
108
+ it "passes validation if money value is a Float" do
109
+ product.price = 12.34
110
+ product.save.should be_true
111
+ end
112
+
113
+ it "passes validation if money value is an Integer" do
114
+ product.price = 12
115
+ product.save.should be_true
92
116
  end
93
117
 
94
118
  it "fails validation with the proper error message using numericality validations" do
95
- @product.price_in_a_range = "-123"
96
- @product.valid?.should be_false
97
- @product.errors[:price_in_a_range].first.should match(/Must be greater than zero and less than \$10k/)
119
+ product.price_in_a_range = "-12"
120
+ product.valid?.should be_false
121
+ product.errors[:price_in_a_range].first.should match(/Must be greater than zero and less than \$100/)
122
+
123
+ product.price_in_a_range = Money.new(-1200, "USD")
124
+ product.valid?.should be_false
125
+ product.errors[:price_in_a_range].first.should match(/Must be greater than zero and less than \$100/)
126
+
127
+ product.price_in_a_range = "0"
128
+ product.valid?.should be_false
129
+ product.errors[:price_in_a_range].first.should match(/Must be greater than zero and less than \$100/)
130
+
131
+ product.price_in_a_range = "12"
132
+ product.valid?.should be_true
133
+
134
+ product.price_in_a_range = Money.new(1200, "USD")
135
+ product.valid?.should be_true
136
+
137
+ product.price_in_a_range = "101"
138
+ product.valid?.should be_false
139
+ product.errors[:price_in_a_range].first.should match(/Must be greater than zero and less than \$100/)
140
+
141
+ product.price_in_a_range = Money.new(10100, "USD")
142
+ product.valid?.should be_false
143
+ product.errors[:price_in_a_range].first.should match(/Must be greater than zero and less than \$100/)
144
+ end
145
+
146
+ it "fails validation with the proper error message on the cents field " do
147
+ product.price_in_a_range = "-12"
148
+ product.valid?.should be_false
149
+ product.errors[:price_in_a_range_cents].first.should match(/greater than 0/)
98
150
 
99
- @product.price_in_a_range = "123"
151
+ product.price_in_a_range = "0"
152
+ product.valid?.should be_false
153
+ product.errors[:price_in_a_range_cents].first.should match(/greater than 0/)
100
154
 
101
- @product.valid?.should be_true
155
+ product.price_in_a_range = "12"
156
+ product.valid?.should be_true
102
157
 
103
- @product.price_in_a_range = "10001"
104
- @product.valid?.should be_false
105
- @product.errors[:price_in_a_range].first.should match(/Must be greater than zero and less than \$10k/)
158
+ product.price_in_a_range = "101"
159
+ product.valid?.should be_false
160
+ product.errors[:price_in_a_range_cents].first.should match(/less than or equal to 10000/)
161
+ end
162
+
163
+ it "fails validation when a non number string is given" do
164
+ product = Product.create(:price_in_a_range => "asd")
165
+ product.valid?.should be_false
166
+ product.errors[:price_in_a_range].first.should match(/greater than zero/)
167
+
168
+ product = Product.create(:price_in_a_range => "asd23")
169
+ product.valid?.should be_false
170
+ product.errors[:price_in_a_range].first.should match(/greater than zero/)
171
+
172
+ product = Product.create(:price => "asd")
173
+ product.valid?.should be_false
174
+ product.errors[:price].first.should match(/is not a number/)
175
+
176
+ product = Product.create(:price => "asd23")
177
+ product.valid?.should be_false
178
+ product.errors[:price].first.should match(/is not a number/)
106
179
  end
107
180
 
108
181
  it "passes validation when amount contains spaces (99 999 999.99)" do
109
- @product.price = "99 999 999.99"
110
- @product.should be_valid
111
- @product.price_cents.should == 9999999999
182
+ product.price = "99 999 999.99"
183
+ product.should be_valid
184
+ product.price_cents.should == 9999999999
112
185
  end
113
186
 
114
187
  it "passes validation when amount contains underscores (99_999_999.99)" do
115
- @product.price = "99_999_999.99"
116
- @product.should be_valid
117
- @product.price_cents.should == 9999999999
188
+ product.price = "99_999_999.99"
189
+ product.should be_valid
190
+ product.price_cents.should == 9999999999
118
191
  end
119
192
 
120
193
  it "passes validation if money value has correct format" do
121
- @product.price = "12,230.24"
122
- @product.save.should be_true
194
+ product.price = "12,230.24"
195
+ product.save.should be_true
123
196
  end
124
197
 
125
198
  it "respects numericality validation when using update_attributes on money attribute" do
126
- @product.update_attributes(:price => "some text").should be_false
127
- @product.update_attributes(:price => Money.new(320, 'USD')).should be_true
199
+ product.update_attributes(:price => "some text").should be_false
200
+ product.update_attributes(:price => Money.new(320, 'USD')).should be_true
128
201
  end
129
202
 
130
203
  it "uses i18n currency format when validating" do
@@ -156,142 +229,145 @@ if defined? ActiveRecord
156
229
  end
157
230
 
158
231
  it "doesn't allow nil by default" do
159
- @product.price_cents = nil
160
- @product.save.should be_false
232
+ product.price_cents = nil
233
+ product.save.should be_false
161
234
  end
162
235
 
163
236
  it "allows nil if optioned" do
164
- @product.optional_price = nil
165
- @product.save.should be_true
166
- @product.optional_price.should be_nil
237
+ product.optional_price = nil
238
+ product.save.should be_true
239
+ product.optional_price.should be_nil
167
240
  end
168
241
 
169
242
  it "doesn't raise exception if validation is used and nil is not allowed" do
170
- expect { @product.price = nil }.to_not raise_error
243
+ expect { product.price = nil }.to_not raise_error
171
244
  end
172
245
 
173
246
  it "doesn't save nil values if validation is used and nil is not allowed" do
174
- @product.price = nil
175
- @product.save
176
- @product.price_cents.should_not be_nil
247
+ product.price = nil
248
+ product.save
249
+ product.price_cents.should_not be_nil
177
250
  end
178
251
 
179
252
  it "resets money_before_type_cast attr every time a save operation occurs" do
180
253
  v = Money.new(100, :usd)
181
- @product.price = v
182
- @product.price_money_before_type_cast.should == v
183
- @product.save
184
- @product.price_money_before_type_cast.should be_nil
185
- @product.price = 10
186
- @product.price_money_before_type_cast.should == 10
187
- @product.save
188
- @product.price_money_before_type_cast.should be_nil
189
- @product.bonus = ""
190
- @product.bonus_money_before_type_cast.should == ""
191
- @product.save.should be_false
192
- @product.bonus_money_before_type_cast.should be_nil
254
+ product.price = v
255
+ product.price_money_before_type_cast.should == v
256
+ product.save
257
+ product.price_money_before_type_cast.should be_nil
258
+ product.price = 10
259
+ product.price_money_before_type_cast.should == 10
260
+ product.save
261
+ product.price_money_before_type_cast.should be_nil
262
+ end
263
+
264
+ it "does not reset money_before_type_cast attr if save operation fails" do
265
+ product.bonus = ""
266
+ product.bonus_money_before_type_cast.should == ""
267
+ product.save.should be_false
268
+ product.bonus_money_before_type_cast.should == ""
193
269
  end
194
270
 
195
271
  it "uses Money default currency if :with_currency has not been used" do
196
- @service.discount.currency.should == Money::Currency.find(:eur)
272
+ service.discount.currency.should == Money::Currency.find(:eur)
197
273
  end
198
274
 
199
275
  it "overrides default currency with the currency registered for the model" do
200
- @product.price.currency.should == Money::Currency.find(:usd)
276
+ product.price.currency.should == Money::Currency.find(:usd)
201
277
  end
202
278
 
203
279
  it "overrides default currency with the value of :with_currency argument" do
204
- @service.charge.currency.should == Money::Currency.find(:usd)
205
- @product.bonus.currency.should == Money::Currency.find(:gbp)
280
+ service.charge.currency.should == Money::Currency.find(:usd)
281
+ product.bonus.currency.should == Money::Currency.find(:gbp)
206
282
  end
207
283
 
208
284
  it "assigns correctly Money objects to the attribute" do
209
- @product.price = Money.new(2500, :USD)
210
- @product.save.should be_true
211
- @product.price.cents.should == 2500
212
- @product.price.currency_as_string.should == "USD"
285
+ product.price = Money.new(2500, :USD)
286
+ product.save.should be_true
287
+ product.price.cents.should == 2500
288
+ product.price.currency_as_string.should == "USD"
213
289
  end
214
290
 
215
291
  it "assigns correctly Fixnum objects to the attribute" do
216
- @product.price = 25
217
- @product.save.should be_true
218
- @product.price.cents.should == 2500
219
- @product.price.currency_as_string.should == "USD"
292
+ product.price = 25
293
+ product.save.should be_true
294
+ product.price.cents.should == 2500
295
+ product.price.currency_as_string.should == "USD"
220
296
 
221
- @service.discount = 2
222
- @service.save.should be_true
223
- @service.discount.cents.should == 200
224
- @service.discount.currency_as_string.should == "EUR"
297
+ service.discount = 2
298
+ service.save.should be_true
299
+ service.discount.cents.should == 200
300
+ service.discount.currency_as_string.should == "EUR"
225
301
  end
226
302
 
227
303
  it "assigns correctly String objects to the attribute" do
228
- @product.price = "25"
229
- @product.save.should be_true
230
- @product.price.cents.should == 2500
231
- @product.price.currency_as_string.should == "USD"
304
+ product.price = "25"
305
+ product.save.should be_true
306
+ product.price.cents.should == 2500
307
+ product.price.currency_as_string.should == "USD"
232
308
 
233
- @service.discount = "2"
234
- @service.save.should be_true
235
- @service.discount.cents.should == 200
236
- @service.discount.currency_as_string.should == "EUR"
309
+ service.discount = "2"
310
+ service.save.should be_true
311
+ service.discount.cents.should == 200
312
+ service.discount.currency_as_string.should == "EUR"
237
313
  end
238
314
 
239
315
  it "overrides default, model currency with the value of :with_currency in fixnum assignments" do
240
- @product.bonus = 25
241
- @product.save.should be_true
242
- @product.bonus.cents.should == 2500
243
- @product.bonus.currency_as_string.should == "GBP"
316
+ product.bonus = 25
317
+ product.save.should be_true
318
+ product.bonus.cents.should == 2500
319
+ product.bonus.currency_as_string.should == "GBP"
244
320
 
245
- @service.charge = 2
246
- @service.save.should be_true
247
- @service.charge.cents.should == 200
248
- @service.charge.currency_as_string.should == "USD"
321
+ service.charge = 2
322
+ service.save.should be_true
323
+ service.charge.cents.should == 200
324
+ service.charge.currency_as_string.should == "USD"
249
325
  end
250
326
 
251
327
  it "overrides default, model currency with the value of :with_currency in string assignments" do
252
- @product.bonus = "25"
253
- @product.save.should be_true
254
- @product.bonus.cents.should == 2500
255
- @product.bonus.currency_as_string.should == "GBP"
328
+ product.bonus = "25"
329
+ product.save.should be_true
330
+ product.bonus.cents.should == 2500
331
+ product.bonus.currency_as_string.should == "GBP"
256
332
 
257
- @service.charge = "2"
258
- @service.save.should be_true
259
- @service.charge.cents.should == 200
260
- @service.charge.currency_as_string.should == "USD"
333
+ service.charge = "2"
334
+ service.save.should be_true
335
+ service.charge.cents.should == 200
336
+ service.charge.currency_as_string.should == "USD"
261
337
  end
262
338
 
263
339
  it "overrides default currency with model currency, in fixnum assignments" do
264
- @product.discount_value = 5
265
- @product.save.should be_true
266
- @product.discount_value.cents.should == 500
267
- @product.discount_value.currency_as_string.should == "USD"
340
+ product.discount_value = 5
341
+ product.save.should be_true
342
+ product.discount_value.cents.should == 500
343
+ product.discount_value.currency_as_string.should == "USD"
268
344
  end
269
345
 
270
346
  it "overrides default currency with model currency, in string assignments" do
271
- @product.discount_value = "5"
272
- @product.save.should be_true
273
- @product.discount_value.cents.should == 500
274
- @product.discount_value.currency_as_string.should == "USD"
347
+ product.discount_value = "5"
348
+ product.save.should be_true
349
+ product.discount_value.cents.should == 500
350
+ product.discount_value.currency_as_string.should == "USD"
275
351
  end
276
352
 
277
353
  it "falls back to default currency, in fixnum assignments" do
278
- @service.discount = 5
279
- @service.save.should be_true
280
- @service.discount.cents.should == 500
281
- @service.discount.currency_as_string.should == "EUR"
354
+ service.discount = 5
355
+ service.save.should be_true
356
+ service.discount.cents.should == 500
357
+ service.discount.currency_as_string.should == "EUR"
282
358
  end
283
359
 
284
360
  it "falls back to default currency, in string assignments" do
285
- @service.discount = "5"
286
- @service.save.should be_true
287
- @service.discount.cents.should == 500
288
- @service.discount.currency_as_string.should == "EUR"
361
+ service.discount = "5"
362
+ service.save.should be_true
363
+ service.discount.cents.should == 500
364
+ service.discount.currency_as_string.should == "EUR"
289
365
  end
290
366
 
291
367
  it "sets field to nil, in nil assignments if allow_nil is set" do
292
- @product.optional_price = nil
293
- @product.save.should be_true
294
- @product.optional_price.should be_nil
368
+ product.optional_price = nil
369
+ product.save.should be_true
370
+ product.optional_price.should be_nil
295
371
  end
296
372
 
297
373
  it "sets field to nil, in instantiation if allow_nil is set" do
@@ -303,17 +379,9 @@ if defined? ActiveRecord
303
379
  end
304
380
 
305
381
  it "sets field to nil, in blank assignments if allow_nil is set" do
306
- @product.optional_price = ""
307
- @product.save.should be_true
308
- @product.optional_price.should be_nil
309
- end
310
-
311
- context "for column with currency:" do
312
- it "is overridden by instance currency" do
313
- product = Product.create(:price_cents => 5320, :discount => 350, :bonus_cents => 320)
314
- product.stub(:currency) { "EUR" }
315
- product.bonus.currency_as_string.should == "EUR"
316
- end
382
+ product.optional_price = ""
383
+ product.save.should be_true
384
+ product.optional_price.should be_nil
317
385
  end
318
386
 
319
387
  context "for column with model currency:" do
@@ -329,11 +397,22 @@ if defined? ActiveRecord
329
397
  end
330
398
 
331
399
  context "for model with currency column:" do
332
- before :each do
333
- @transaction = Transaction.create(:amount_cents => 2400, :tax_cents => 600,
334
- :currency => :usd)
335
- @dummy_product1 = DummyProduct.create(:price_cents => 2400, :currency => :usd)
336
- @dummy_product2 = DummyProduct.create(:price_cents => 2600) # nil currency
400
+ let(:transaction) do
401
+ Transaction.create(:amount_cents => 2400, :tax_cents => 600,
402
+ :currency => :usd)
403
+ end
404
+
405
+ let(:dummy_product) do
406
+ DummyProduct.create(:price_cents => 2400, :currency => :usd)
407
+ end
408
+
409
+ let(:dummy_product_with_nil_currency) do
410
+ DummyProduct.create(:price_cents => 2600) # nil currency
411
+ end
412
+
413
+ let(:dummy_product_with_invalid_currency) do
414
+ # invalid currency
415
+ DummyProduct.create(:price_cents => 2600, :currency => :foo)
337
416
  end
338
417
 
339
418
  it "serializes correctly the currency to a new instance of model" do
@@ -345,19 +424,25 @@ if defined? ActiveRecord
345
424
  end
346
425
 
347
426
  it "overrides default currency with the value of row currency" do
348
- @transaction.amount.currency.should == Money::Currency.find(:usd)
427
+ transaction.amount.currency.should == Money::Currency.find(:usd)
349
428
  end
350
429
 
351
430
  it "overrides default currency with the currency registered for the model" do
352
- @dummy_product2.price.currency.should == Money::Currency.find(:gbp)
431
+ dummy_product_with_nil_currency.price.currency.should ==
432
+ Money::Currency.find(:gbp)
433
+ end
434
+
435
+ it "overrides default currency with the currency registered for the model if currency is invalid" do
436
+ dummy_product_with_invalid_currency.price.currency.should ==
437
+ Money::Currency.find(:gbp)
353
438
  end
354
439
 
355
440
  it "overrides default and model currency with the row currency" do
356
- @dummy_product1.price.currency.should == Money::Currency.find(:usd)
441
+ dummy_product.price.currency.should == Money::Currency.find(:usd)
357
442
  end
358
443
 
359
444
  it "constructs the money attribute from the stored mapped attribute values" do
360
- @transaction.amount.should == Money.new(2400, :usd)
445
+ transaction.amount.should == Money.new(2400, :usd)
361
446
  end
362
447
 
363
448
  it "instantiates correctly Money objects from the mapped attributes" do
@@ -366,19 +451,19 @@ if defined? ActiveRecord
366
451
  end
367
452
 
368
453
  it "assigns correctly Money objects to the attribute" do
369
- @transaction.amount = Money.new(2500, :eur)
370
- @transaction.save.should be_true
371
- @transaction.amount.cents.should == Money.new(2500, :eur).cents
372
- @transaction.amount.currency_as_string.should == "EUR"
454
+ transaction.amount = Money.new(2500, :eur)
455
+ transaction.save.should be_true
456
+ transaction.amount.cents.should == Money.new(2500, :eur).cents
457
+ transaction.amount.currency_as_string.should == "EUR"
373
458
  end
374
459
 
375
460
  it "uses default currency if a non Money object is assigned to the attribute" do
376
- @transaction.amount = 234
377
- @transaction.amount.currency_as_string.should == "USD"
461
+ transaction.amount = 234
462
+ transaction.amount.currency_as_string.should == "USD"
378
463
  end
379
464
 
380
465
  it "constructs the money object from the mapped method value" do
381
- @transaction.total.should == Money.new(3000, :usd)
466
+ transaction.total.should == Money.new(3000, :usd)
382
467
  end
383
468
 
384
469
  end