active_cart 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/active_cart.rb +2 -0
- data/lib/active_cart/acts_as_cart.rb +59 -4
- data/lib/exceptions/out_of_stock.rb +4 -0
- data/test/blueprints.rb +6 -0
- data/test/fixtures/item.rb +2 -0
- data/test/schema.rb +5 -0
- data/test/test_helper.rb +1 -0
- data/test/unit/acts_as_cart_test.rb +109 -0
- metadata +5 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.7
|
data/lib/active_cart.rb
CHANGED
@@ -13,10 +13,12 @@ module ActiveCart
|
|
13
13
|
module ClassMethods
|
14
14
|
# acts_as_cart - Turns an ActiveRecord model in to a cart. It can take a hash of options
|
15
15
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
16
|
+
# state_column: The database column that stores the persistent state machine state. Default: state
|
17
|
+
# invoice_id_column: The column that stores the invoice id. Default: invoice_id
|
18
|
+
# cart_items: The model that represents the items for this cart. Is associated as a has_many. Default: cart_items
|
19
|
+
# order_totals: The model that represents order totals for this cart. It is associated as a has_many. Default: order_totals
|
20
|
+
#
|
21
|
+
# Example
|
20
22
|
#
|
21
23
|
# class Cart < ActiveModel::Base
|
22
24
|
# acts_as_cart
|
@@ -26,6 +28,14 @@ module ActiveCart
|
|
26
28
|
#
|
27
29
|
# You can create custom acts_as_state_machine (aasm) states and events after declaring acts_as_cart
|
28
30
|
#
|
31
|
+
# NOTE: this is a STORAGE ENGINE, so you need to create it (by finding by id) then pass the result in to ActiveCart::Cart.new. It might look something like this
|
32
|
+
# (Most likely in ApplicationController):
|
33
|
+
#
|
34
|
+
# if session[:cart_id]
|
35
|
+
# engine = Cart.find(session[:cart_id])
|
36
|
+
# @cart = ActiveCart.new(engine) if engine
|
37
|
+
# end
|
38
|
+
#
|
29
39
|
def acts_as_cart(options = {})
|
30
40
|
cattr_accessor :aac_config
|
31
41
|
|
@@ -42,13 +52,46 @@ module ActiveCart
|
|
42
52
|
#include AASM::Persistence::ActiveRecordPersistence
|
43
53
|
include ActiveCart::CartStorage
|
44
54
|
|
55
|
+
#:nodoc
|
45
56
|
def invoice_id
|
46
57
|
read_attribute(self.aac_config[:invoice_id_column])
|
47
58
|
end
|
48
59
|
|
60
|
+
#:nodoc
|
49
61
|
def state
|
50
62
|
read_attribute(self.aac_config[:state_column])
|
51
63
|
end
|
64
|
+
|
65
|
+
#:nodoc
|
66
|
+
def find_cart_item(item)
|
67
|
+
self.send(:cart_items).find(:first, :conditions => [ 'original_id = ? AND original_type = ?', item.id, item.class.to_s ])
|
68
|
+
end
|
69
|
+
|
70
|
+
#:nodoc
|
71
|
+
def add_to_cart(item, quantity = 1)
|
72
|
+
cart_item = find_cart_item(item)
|
73
|
+
if cart_item
|
74
|
+
cart_item.quantity += quantity
|
75
|
+
cart_item.save!
|
76
|
+
else
|
77
|
+
cart_item = self.send(:cart_items).create!(self.aac_config[:cart_items].to_s.classify.constantize.new_from_item(item).attributes.merge(:quantity => quantity, :original_id => item.id, :original_type => item.class.to_s))
|
78
|
+
end
|
79
|
+
self.reload
|
80
|
+
end
|
81
|
+
|
82
|
+
#:nodoc
|
83
|
+
def remove_from_cart(item, quantity = 1)
|
84
|
+
cart_item = find_cart_item(item)
|
85
|
+
if cart_item
|
86
|
+
if cart_item.quantity - quantity > 0
|
87
|
+
cart_item.quantity = cart_item.quantity - quantity
|
88
|
+
cart_item.save!
|
89
|
+
else
|
90
|
+
cart_item.destroy
|
91
|
+
end
|
92
|
+
end
|
93
|
+
self.reload
|
94
|
+
end
|
52
95
|
end
|
53
96
|
|
54
97
|
aasm_column self.aac_config[:state_column]
|
@@ -80,6 +123,8 @@ module ActiveCart
|
|
80
123
|
# acts_as_cart uses the 'original' polymorphic attribute to store a reference to the original Item object. The compound attribute gets nullified if the original Item gets
|
81
124
|
# deleted.
|
82
125
|
#
|
126
|
+
# When adding an item to a cart, you should pass in the actual concrete item, not the cart_item - the model will take care of the conversion.
|
127
|
+
#
|
83
128
|
# For complex carts with multiple item types, you will probably need to use STI, as it's basically impossible to use a polymorphic relationship (If someone can
|
84
129
|
# suggest a better way, I'm all ears). That said, there is no easy way to model complex carts, so I'll leave this as an exercise for the reader.
|
85
130
|
#
|
@@ -132,6 +177,16 @@ module ActiveCart
|
|
132
177
|
read_attribute(self.aaci_config[:price_column])
|
133
178
|
end
|
134
179
|
end
|
180
|
+
|
181
|
+
# Creates a new cart_item item for the passed in concrete item
|
182
|
+
#
|
183
|
+
# The default copies all the common attributes from the passed in item to new cart_item (Except id). Override it if you want to do something special.
|
184
|
+
#
|
185
|
+
def new_from_item(item)
|
186
|
+
cart_item = self.new
|
187
|
+
item.class.columns.each { |col| cart_item.send((col.to_s + "=").to_sym, item.send(col)) if cart_item.respond_to?((col.to_s + "=").to_sym) }
|
188
|
+
cart_item
|
189
|
+
end
|
135
190
|
|
136
191
|
belongs_to self.aaci_config[:cart], :foreign_key => self.aaci_config[:foreign_key]
|
137
192
|
belongs_to :original, :polymorphic => true
|
data/test/blueprints.rb
CHANGED
@@ -6,11 +6,17 @@ Cart.blueprint do
|
|
6
6
|
state { 'shopping' }
|
7
7
|
end
|
8
8
|
|
9
|
+
Item.blueprint do
|
10
|
+
name { Faker::Lorem.words(2).join(' ') }
|
11
|
+
price { (rand(9999) + 1).to_i / 100 }
|
12
|
+
end
|
13
|
+
|
9
14
|
CartItem.blueprint do
|
10
15
|
cart { Cart.make }
|
11
16
|
name { Faker::Lorem.words(2).join(' ') }
|
12
17
|
quantity { rand(10).to_i }
|
13
18
|
price { (rand(9999) + 1).to_i / 100 }
|
19
|
+
original { Item.make }
|
14
20
|
end
|
15
21
|
|
16
22
|
OrderTotal.blueprint do
|
data/test/schema.rb
CHANGED
@@ -5,6 +5,11 @@ ActiveRecord::Schema.define :version => 0 do
|
|
5
5
|
t.string :dummy # Not needed in real carts - used sa a dummy field for testing
|
6
6
|
end
|
7
7
|
|
8
|
+
create_table :items, :force => true do |t|
|
9
|
+
t.string :name
|
10
|
+
t.float :price
|
11
|
+
end
|
12
|
+
|
8
13
|
create_table :cart_items, :force => true do |t|
|
9
14
|
t.integer :cart_id
|
10
15
|
t.string :name
|
data/test/test_helper.rb
CHANGED
@@ -350,5 +350,114 @@ class ActsAsCartTest < ActiveSupport::TestCase
|
|
350
350
|
end
|
351
351
|
end
|
352
352
|
end
|
353
|
+
|
354
|
+
context 'add to cart' do
|
355
|
+
should 'add an item to the cart if the cart is empty' do
|
356
|
+
assert @cart.empty?
|
357
|
+
item = Item.make
|
358
|
+
@cart.add_to_cart(item)
|
359
|
+
assert_equal 1, @cart.size
|
360
|
+
assert_equal 1, @cart[0].quantity
|
361
|
+
assert_equal 1, @cart.quantity
|
362
|
+
assert_equal CartItem, @cart[0].class
|
363
|
+
assert_equal item.id, @cart[0].original.id
|
364
|
+
end
|
365
|
+
|
366
|
+
should 'increase the item quantity if the same item is added to the cart again' do
|
367
|
+
assert @cart.empty?
|
368
|
+
item = Item.make
|
369
|
+
@cart.add_to_cart(item)
|
370
|
+
assert_equal 1, @cart.size
|
371
|
+
assert_equal 1, @cart[0].quantity
|
372
|
+
|
373
|
+
item_2 = Item.find(item.id) # Has the same id, is the same as item
|
374
|
+
@cart.add_to_cart(item_2)
|
375
|
+
assert_equal 1, @cart.size
|
376
|
+
assert_equal 2, @cart[0].quantity
|
377
|
+
assert_equal 2, @cart.quantity
|
378
|
+
assert_equal CartItem, @cart[0].class
|
379
|
+
assert_equal item.id, @cart[0].original.id
|
380
|
+
end
|
381
|
+
|
382
|
+
should 'increase the item quantity by the supplied number if the same item is add to the cart again and a quantity is supplied' do
|
383
|
+
assert @cart.empty?
|
384
|
+
item = Item.make
|
385
|
+
@cart.add_to_cart(item)
|
386
|
+
assert_equal 1, @cart.size
|
387
|
+
assert_equal 1, @cart[0].quantity
|
388
|
+
|
389
|
+
item_2 = Item.find(item.id) # Has the same id, is the same as item
|
390
|
+
@cart.add_to_cart(item_2, 10)
|
391
|
+
assert_equal 1, @cart.size
|
392
|
+
assert_equal 11, @cart[0].quantity
|
393
|
+
assert_equal 11, @cart.quantity
|
394
|
+
end
|
395
|
+
|
396
|
+
should 'add another item to the cart' do
|
397
|
+
assert @cart.empty?
|
398
|
+
item = Item.make
|
399
|
+
@cart.add_to_cart(item)
|
400
|
+
assert_equal 1, @cart.size
|
401
|
+
assert_equal 1, @cart[0].quantity
|
402
|
+
|
403
|
+
item_2 = Item.make
|
404
|
+
@cart.add_to_cart(item_2, 10)
|
405
|
+
assert_equal 2, @cart.size
|
406
|
+
assert_equal 1, @cart[0].quantity
|
407
|
+
assert_equal 10, @cart[1].quantity
|
408
|
+
assert_equal 11, @cart.quantity
|
409
|
+
assert_equal CartItem, @cart[0].class
|
410
|
+
assert_equal item.id, @cart[0].original.id
|
411
|
+
assert_equal CartItem, @cart[1].class
|
412
|
+
assert_equal item_2.id, @cart[1].original.id
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
context 'remove_from_cart' do
|
417
|
+
should 'remove the quantity supplied from the cart' do
|
418
|
+
item = Item.make
|
419
|
+
@cart.add_to_cart(item, 10)
|
420
|
+
assert_equal 1, @cart.size
|
421
|
+
assert_equal 10, @cart.quantity
|
422
|
+
|
423
|
+
@cart.remove_from_cart(item)
|
424
|
+
assert_equal 1, @cart.size
|
425
|
+
assert_equal 9, @cart.quantity
|
426
|
+
end
|
427
|
+
|
428
|
+
should 'remove the item from the cart if the quantity to be removed is equal to the number in cart' do
|
429
|
+
item = Item.make
|
430
|
+
@cart.add_to_cart(item, 10)
|
431
|
+
assert_equal 1, @cart.size
|
432
|
+
assert_equal 10, @cart.quantity
|
433
|
+
|
434
|
+
@cart.remove_from_cart(item, 10)
|
435
|
+
assert_equal 0, @cart.size
|
436
|
+
end
|
437
|
+
|
438
|
+
should 'remove the item from the cart if the quantity to be removed is greater than the number in cart' do
|
439
|
+
item = Item.make
|
440
|
+
@cart.add_to_cart(item, 10)
|
441
|
+
assert_equal 1, @cart.size
|
442
|
+
assert_equal 10, @cart.quantity
|
443
|
+
|
444
|
+
@cart.remove_from_cart(item, 11)
|
445
|
+
assert_equal 0, @cart.size
|
446
|
+
|
447
|
+
assert_not_nil Item.find(item.id)
|
448
|
+
end
|
449
|
+
|
450
|
+
should "simply return if the item doesn't exist in the cart" do
|
451
|
+
item = Item.make
|
452
|
+
@cart.add_to_cart(item, 10)
|
453
|
+
assert_equal 1, @cart.size
|
454
|
+
assert_equal 10, @cart.quantity
|
455
|
+
|
456
|
+
item_2 = Item.make
|
457
|
+
@cart.remove_from_cart(item_2, 10)
|
458
|
+
assert_equal 1, @cart.size
|
459
|
+
assert_equal 10, @cart.quantity
|
460
|
+
end
|
461
|
+
end
|
353
462
|
end
|
354
463
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_cart
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Myles Eftos
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-03-01 00:00:00 +08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -95,9 +95,11 @@ files:
|
|
95
95
|
- lib/active_cart/order_total.rb
|
96
96
|
- lib/active_cart/order_total_collection.rb
|
97
97
|
- lib/active_cart/storage_engines/memory.rb
|
98
|
+
- lib/exceptions/out_of_stock.rb
|
98
99
|
- test/blueprints.rb
|
99
100
|
- test/fixtures/cart.rb
|
100
101
|
- test/fixtures/cart_item.rb
|
102
|
+
- test/fixtures/item.rb
|
101
103
|
- test/fixtures/order_total.rb
|
102
104
|
- test/mocks/test_cart_storage.rb
|
103
105
|
- test/mocks/test_item.rb
|
@@ -152,6 +154,7 @@ test_files:
|
|
152
154
|
- test/fixtures/cart_item.rb
|
153
155
|
- test/fixtures/order_total.rb
|
154
156
|
- test/fixtures/cart.rb
|
157
|
+
- test/fixtures/item.rb
|
155
158
|
- test/blueprints.rb
|
156
159
|
- test/test_helper.rb
|
157
160
|
- test/performance/browsing_test.rb
|