money-rails 0.8.1 → 0.9.0

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