active_cart 0.0.6 → 0.0.7
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.
- 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
|