cartman 0.4.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +19 -12
- data/lib/cartman.rb +1 -0
- data/lib/cartman/cart.rb +46 -14
- data/lib/cartman/configuration.rb +1 -1
- data/lib/cartman/item.rb +11 -1
- data/lib/cartman/item_collection.rb +19 -0
- data/lib/cartman/version.rb +1 -1
- data/spec/cart_spec.rb +57 -26
- data/spec/item_collection_spec.rb +24 -0
- data/spec/item_spec.rb +12 -4
- metadata +7 -4
data/README.md
CHANGED
@@ -27,14 +27,16 @@ Cartman has a few (read 3) configuration options you can set, most likely in an
|
|
27
27
|
# config/initializers/cartman.rb
|
28
28
|
Cartman.config do
|
29
29
|
cart_expires_in 604800 # one week, in seconds. This is the default
|
30
|
-
|
30
|
+
unit_cost_field :unit_cost # for cart totaling
|
31
31
|
quantity_field :quantity # for quantity totaling
|
32
32
|
redis Redis.new # set the redis connection here
|
33
33
|
end
|
34
34
|
```
|
35
35
|
|
36
36
|
- The `cart_expires_in` setting will let you set how long a cart should live. If no items are added to the cart before the time expires, the cart will be cleared. If you want to disable cart expiration, set this to `-1`.
|
37
|
-
- `
|
37
|
+
- `unit_cost_field` lets you tell Cartman where you're storing the unit_cost of each item, so that you can use the `cost` method on the item, and the `total` method on `Cart`.
|
38
|
+
- `quantity_field` lets you tell Cartman where you're storing the quantity of each item. The `Item#cost` method uses this along with the `unit_cost` field to determine the cost.
|
39
|
+
- `redis` lets you set the redis connection you want to use. Note that this is not redis connection options, this is an actual instance of `Redis`.
|
38
40
|
|
39
41
|
## Usage
|
40
42
|
|
@@ -42,7 +44,8 @@ To create a new shopping cart, just call `Cartman::Cart.new(user.id)`. The para
|
|
42
44
|
|
43
45
|
The returned Items come back as `Cartman::Item` instances, which have a few special methods to be aware of:
|
44
46
|
|
45
|
-
- `
|
47
|
+
- `cost` - which will return the `:unit_cost` multiplied by the `:quantity`.
|
48
|
+
- `destroy` - which will remove the item from the cart
|
46
49
|
- `cart` - which will return the parent cart, think ActiveRecord association
|
47
50
|
- `_id` - which will return the id of the item, if you need that for whatever reason
|
48
51
|
- `_key` - which will return the redis key the data is stored in. Probably won't need that, but it's there.
|
@@ -51,17 +54,21 @@ The returned Items come back as `Cartman::Item` instances, which have a few spec
|
|
51
54
|
The `Cart` object also has some handy methods that you should be aware of:
|
52
55
|
|
53
56
|
- `add_item(data)` - which is the life blood of Cartman. This method takes a hash of data you would like to store with the item. Here's a few suggestions of keys you may want in your hash:
|
54
|
-
- `:id` - to store the ID of the
|
55
|
-
- `:type` - to store the class of the
|
56
|
-
- `:
|
57
|
-
- `:quantity` - which if you use will let you use the `Cart#quantity`
|
58
|
-
- `remove_item(item) - which, you guessed it, removes an item. This method takes an Item object, not a hash.
|
57
|
+
- `:id` - (*required*) to store the ID of the model you're adding
|
58
|
+
- `:type` - (*required*) to store the class of the model you're adding.
|
59
|
+
- `:unit_cost` - This field is required if you want to take advantage of the `Item#cost` method, and the `Cart#total` method.
|
60
|
+
- `:quantity` - which if you use will let you use the `Cart#quantity` and `Cart#total` methods without any extra configuration.
|
61
|
+
- `remove_item(item)` - which, you guessed it, removes an item. This method takes an Item object, not a hash.
|
59
62
|
- `contains?(Product)` - This is a biggie. It will tell you if a certain item is in the cart. And the way it works is you pass it an object, like an instance of a Product model, and it will examine the class, and the id, and look to see if it's in the cart already. This method only works if the `:id` and `:type` keys are set in the item's data hash.
|
63
|
+
- `find(Product)` - This will return the `Item` object that represents the object passed in. It works like `contains?` and uses class, and id. It only works if the `:id` and `:type` keys are set in the item's data hash.
|
64
|
+
- `items` - this returns a magic array of all the items. I call it magic because you can call on it:
|
65
|
+
- `each_with_object` - which will act like a regular `each` call, but the block will yield the `Item` and the object it represents by using the `:type` and `:id` keys.
|
66
|
+
- `total` - will sum all of the `cost` return values of all of the items in the cart. For this to work, the `:unit_cost` and `:quantity` fields need to be set for all items.
|
60
67
|
- `count` - which will give you the total number of items in the cart. Faster than `cart.items.size` because it doesn't load all of the item data from redis.
|
61
|
-
- `quantity` - which will return the total quantity of all the items. The quantity field is set in the config block, by default it's
|
62
|
-
- `ttl` - will tell you how many seconds until the cart expires. It will return -1 if the cart will never expire
|
63
|
-
- `touch` - which will reset the ttl back to whatever expiration length is set in the config. Touch is automatically called after `add_item` and `remove_item
|
64
|
-
- `destroy!` - which will delete the cart, and all the line_items out of it
|
68
|
+
- `quantity` - which will return the total quantity of all the items. The quantity field is set in the config block, by default it's `:quantity`
|
69
|
+
- `ttl` - will tell you how many seconds until the cart expires. It will return -1 if the cart will never expire.
|
70
|
+
- `touch` - which will reset the ttl back to whatever expiration length is set in the config. Touch is automatically called after `add_item` and `remove_item`.
|
71
|
+
- `destroy!` - which will delete the cart, and all the line_items out of it.
|
65
72
|
- `reassign(id)` - this method will reassign the cart's unique identifier. So to access this cart at a later time after reassigning, you would put `Cart.new(reassigned_id)`. This is useful for having a cart for an unsigned in user. You can use their session ID while they're unauthenticated, and then when they sign in, you can reassign the cart to the user's ID. NOTE: Reassigning will overwrite any cart in it's way.
|
66
73
|
|
67
74
|
Lets walk through an example implementation with a Rails app that has a User model and a Product model.
|
data/lib/cartman.rb
CHANGED
data/lib/cartman/cart.rb
CHANGED
@@ -8,11 +8,13 @@ module Cartman
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def add_item(options)
|
11
|
+
raise "Must specify both :id and :type" unless options.has_key?(:id) && options.has_key?(:type)
|
11
12
|
line_item_id = @@redis.incr CART_LINE_ITEM_ID_KEY
|
12
|
-
@@redis.
|
13
|
-
|
14
|
-
|
13
|
+
@@redis.pipelined do
|
14
|
+
@@redis.mapped_hmset("cartman:line_item:#{line_item_id}", options)
|
15
|
+
@@redis.sadd key, line_item_id
|
15
16
|
@@redis.sadd index_key, "#{options[:type]}:#{options[:id]}"
|
17
|
+
@@redis.set index_key_for(options), line_item_id
|
16
18
|
end
|
17
19
|
touch
|
18
20
|
get_item(line_item_id)
|
@@ -21,23 +23,28 @@ module Cartman
|
|
21
23
|
def remove_item(item)
|
22
24
|
@@redis.del "cartman:line_item:#{item._id}"
|
23
25
|
@@redis.srem key, item._id
|
24
|
-
|
25
|
-
|
26
|
-
|
26
|
+
@@redis.srem index_key, "#{item.type}:#{item.id}"
|
27
|
+
@@redis.del index_key_for(item)
|
28
|
+
index_keys.each do |key|
|
29
|
+
@@redis.del key
|
27
30
|
end
|
28
31
|
touch
|
29
32
|
end
|
30
33
|
|
31
34
|
def items
|
32
|
-
|
33
|
-
get_item(item_id)
|
34
|
-
}
|
35
|
+
ItemCollection.new(line_item_ids.collect{ |item_id| get_item(item_id)})
|
35
36
|
end
|
36
37
|
|
37
38
|
def contains?(object)
|
38
39
|
@@redis.sismember index_key, "#{object.class}:#{object.id}"
|
39
40
|
end
|
40
41
|
|
42
|
+
def find(object)
|
43
|
+
if contains?(object)
|
44
|
+
get_item(@@redis.get(index_key_for(object)).to_i)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
41
48
|
def count
|
42
49
|
@@redis.scard key
|
43
50
|
end
|
@@ -50,7 +57,7 @@ module Cartman
|
|
50
57
|
|
51
58
|
def total
|
52
59
|
items.collect { |item|
|
53
|
-
(item.
|
60
|
+
(item.cost * 100).to_i
|
54
61
|
}.inject{|sum,cost| sum += cost} / 100.0
|
55
62
|
end
|
56
63
|
|
@@ -62,6 +69,8 @@ module Cartman
|
|
62
69
|
keys = line_item_keys
|
63
70
|
keys << key
|
64
71
|
keys << index_key
|
72
|
+
keys << index_keys
|
73
|
+
keys.flatten!
|
65
74
|
@@redis.pipelined do
|
66
75
|
keys.each do |key|
|
67
76
|
@@redis.del key
|
@@ -72,7 +81,11 @@ module Cartman
|
|
72
81
|
def touch
|
73
82
|
keys_to_expire = line_item_keys
|
74
83
|
keys_to_expire << key
|
75
|
-
|
84
|
+
if @@redis.exists index_key
|
85
|
+
keys_to_expire << index_key
|
86
|
+
keys_to_expire << index_keys
|
87
|
+
keys_to_expire.flatten!
|
88
|
+
end
|
76
89
|
@@redis.pipelined do
|
77
90
|
keys_to_expire.each do |item|
|
78
91
|
@@redis.expire item, Cartman::Configuration.cart_expires_in
|
@@ -82,10 +95,14 @@ module Cartman
|
|
82
95
|
|
83
96
|
def reassign(new_id)
|
84
97
|
if @@redis.exists key
|
98
|
+
new_index_keys = items.collect { |item|
|
99
|
+
index_key_for(item, new_id)
|
100
|
+
}
|
85
101
|
@@redis.rename key, key(new_id)
|
86
|
-
end
|
87
|
-
if @@redis.exists index_key
|
88
102
|
@@redis.rename index_key, index_key(new_id)
|
103
|
+
index_keys.zip(new_index_keys).each do |key, value|
|
104
|
+
@@redis.rename key, value
|
105
|
+
end
|
89
106
|
end
|
90
107
|
@uid = new_id
|
91
108
|
end
|
@@ -100,6 +117,21 @@ module Cartman
|
|
100
117
|
key(id) + ":index"
|
101
118
|
end
|
102
119
|
|
120
|
+
def index_keys(id=@uid)
|
121
|
+
@@redis.keys "#{index_key(id)}:*"
|
122
|
+
end
|
123
|
+
|
124
|
+
def index_key_for(object, id=@uid)
|
125
|
+
case object
|
126
|
+
when Hash
|
127
|
+
index_key(id) + ":#{object[:type]}:#{object[:id]}"
|
128
|
+
when Item
|
129
|
+
index_key(id) + ":#{object.type}:#{object.id}"
|
130
|
+
else
|
131
|
+
index_key(id) + ":#{object.class}:#{object.id}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
103
135
|
def line_item_ids
|
104
136
|
@@redis.smembers key
|
105
137
|
end
|
@@ -109,7 +141,7 @@ module Cartman
|
|
109
141
|
end
|
110
142
|
|
111
143
|
def get_item(id)
|
112
|
-
Item.new(id, @uid, @@redis.hgetall("cartman:line_item:#{id}").inject({}){|
|
144
|
+
Item.new(id, @uid, @@redis.hgetall("cartman:line_item:#{id}").inject({}){|hash,(k,v)| hash[k.to_sym] = v; hash})
|
113
145
|
end
|
114
146
|
end
|
115
147
|
end
|
@@ -4,7 +4,7 @@ module Cartman
|
|
4
4
|
class Configuration
|
5
5
|
@@configuration = {
|
6
6
|
cart_expires_in: 604800, # one week
|
7
|
-
|
7
|
+
unit_cost_field: :unit_cost, # for cart totaling and costing
|
8
8
|
quantity_field: :quantity, # for cart totaling
|
9
9
|
redis: Redis.new, # Redis connection
|
10
10
|
}
|
data/lib/cartman/item.rb
CHANGED
@@ -10,14 +10,24 @@ module Cartman
|
|
10
10
|
@id
|
11
11
|
end
|
12
12
|
|
13
|
+
def cost
|
14
|
+
unit_cost = (@data.fetch(Cartman::Configuration.unit_cost_field).to_f * 100).to_i
|
15
|
+
quantity = @data.fetch(Cartman::Configuration.quantity_field).to_i
|
16
|
+
(unit_cost * quantity) / 100.0
|
17
|
+
end
|
18
|
+
|
13
19
|
def cart
|
14
20
|
@cart ||= Cart.new(@cart_id)
|
15
21
|
end
|
16
22
|
|
17
|
-
def
|
23
|
+
def destroy
|
18
24
|
cart.remove_item(self)
|
19
25
|
end
|
20
26
|
|
27
|
+
def ==(item)
|
28
|
+
@id == item._id
|
29
|
+
end
|
30
|
+
|
21
31
|
def _key
|
22
32
|
"cartman:line_item:#{@id}"
|
23
33
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Cartman
|
4
|
+
class ItemCollection < DelegateClass(Array)
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(array)
|
8
|
+
@results = array
|
9
|
+
super(@results)
|
10
|
+
end
|
11
|
+
|
12
|
+
def each_with_object
|
13
|
+
@results.each do |result|
|
14
|
+
yield result, eval(result.type).send(:find, result.id)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/cartman/version.rb
CHANGED
data/spec/cart_spec.rb
CHANGED
@@ -2,6 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Cartman do
|
4
4
|
describe Cartman::Cart do
|
5
|
+
let(:Bottle) { Struct.new(:id) }
|
5
6
|
let(:cart) { Cartman::Cart.new(1) }
|
6
7
|
|
7
8
|
before(:each) do
|
@@ -16,7 +17,7 @@ describe Cartman do
|
|
16
17
|
|
17
18
|
describe "#add_item" do
|
18
19
|
before(:each) do
|
19
|
-
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12,
|
20
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, quantity: 2)
|
20
21
|
end
|
21
22
|
|
22
23
|
it "creates a line item key" do
|
@@ -37,21 +38,21 @@ describe Cartman do
|
|
37
38
|
Cartman.config.redis.sismember("cartman:cart:1:index", "Bottle:17").should be_true
|
38
39
|
end
|
39
40
|
|
40
|
-
it "should
|
41
|
-
cart.add_item(id: 18, name: "Cordeux", unit_cost: 92.12,
|
42
|
-
|
43
|
-
|
41
|
+
it "should squack if type and/or ID are not set" do
|
42
|
+
expect { cart.add_item(id: 18, name: "Cordeux", unit_cost: 92.12, quantity: 2) }.to raise_error("Must specify both :id and :type")
|
43
|
+
expect { cart.add_item(type: "Bottle", name: "Cordeux", unit_cost: 92.12, quantity: 2) }.to raise_error("Must specify both :id and :type")
|
44
|
+
expect { cart.add_item(name: "Cordeux", unit_cost: 92.12, quantity: 2) }.to raise_error("Must specify both :id and :type")
|
44
45
|
end
|
45
46
|
|
46
47
|
it "should return an Item" do
|
47
|
-
item = cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12,
|
48
|
+
item = cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12, quantity: 2)
|
48
49
|
item.class.should eq(Cartman::Item)
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
52
53
|
describe "#remove_item" do
|
53
54
|
it "should remove the id from the set, and delete the line_item key" do
|
54
|
-
item = cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12,
|
55
|
+
item = cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, quantity: 2)
|
55
56
|
item_id = item._id
|
56
57
|
cart.remove_item(item)
|
57
58
|
Cartman.config.redis.sismember(cart.send(:key), item_id).should be_false
|
@@ -61,12 +62,13 @@ describe Cartman do
|
|
61
62
|
|
62
63
|
describe "#items" do
|
63
64
|
before(:each) do
|
64
|
-
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12,
|
65
|
-
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12,
|
65
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, quantity: 2)
|
66
|
+
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12, quantity: 2)
|
66
67
|
end
|
67
68
|
|
68
|
-
it "should return an
|
69
|
-
cart.items.
|
69
|
+
it "should return an ItemCollection of Items" do
|
70
|
+
cart.items.class.should be(Cartman::ItemCollection)
|
71
|
+
cart.items.first.class.should be(Cartman::Item)
|
70
72
|
cart.items.first.id.should eq("17")
|
71
73
|
cart.items.first.name.should eq("Bordeux")
|
72
74
|
end
|
@@ -78,8 +80,8 @@ describe Cartman do
|
|
78
80
|
end
|
79
81
|
|
80
82
|
before(:each) do
|
81
|
-
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12,
|
82
|
-
cart.add_item(id: 34, name: "Cabernet", unit_cost: 92.12,
|
83
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, quantity: 2)
|
84
|
+
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12, quantity: 2)
|
83
85
|
end
|
84
86
|
|
85
87
|
it "should be able to tell you that an item in the cart is present" do
|
@@ -98,18 +100,36 @@ describe Cartman do
|
|
98
100
|
end
|
99
101
|
end
|
100
102
|
|
103
|
+
describe "#find(item)" do
|
104
|
+
|
105
|
+
before(:each) do
|
106
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, quantity: 2)
|
107
|
+
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12, quantity: 2)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should take some object, and return the Item that corresponds to it" do
|
111
|
+
cart.find(Bottle.new(17)).quantity.should eq("2")
|
112
|
+
cart.find(Bottle.new(17)).name.should eq("Bordeux")
|
113
|
+
cart.find(Bottle.new(34)).name.should eq("Cabernet")
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should return nil if the Item is not in the cart" do
|
117
|
+
cart.find(Bottle.new(23)).should be(nil)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
101
121
|
describe "#count" do
|
102
122
|
it "should return the number of items in the cart" do
|
103
|
-
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12,
|
104
|
-
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12,
|
123
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, quantity: 2)
|
124
|
+
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12, quantity: 2)
|
105
125
|
cart.count.should eq(2)
|
106
126
|
end
|
107
127
|
end
|
108
128
|
|
109
129
|
describe "#quantity" do
|
110
130
|
it "should return the sum of the default quantity field" do
|
111
|
-
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12,
|
112
|
-
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12,
|
131
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, quantity: 2)
|
132
|
+
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12, quantity: 2)
|
113
133
|
cart.quantity.should eq(4)
|
114
134
|
end
|
115
135
|
|
@@ -117,26 +137,32 @@ describe Cartman do
|
|
117
137
|
Cartman.config do
|
118
138
|
quantity_field :qty
|
119
139
|
end
|
120
|
-
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12,
|
121
|
-
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12,
|
140
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, qty: 2)
|
141
|
+
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12, qty: 2)
|
122
142
|
cart.quantity.should eq(4)
|
143
|
+
Cartman.config do
|
144
|
+
quantity_field :quantity
|
145
|
+
end
|
123
146
|
end
|
124
147
|
end
|
125
148
|
|
126
149
|
describe "#total" do
|
127
150
|
it "should total the default costs field" do
|
128
|
-
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12,
|
129
|
-
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12,
|
151
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, quantity: 2)
|
152
|
+
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost: 92.12, quantity: 2)
|
130
153
|
cart.total.should eq(368.48)
|
131
154
|
end
|
132
155
|
|
133
156
|
it "should total whatever cost field the user sets" do
|
134
157
|
Cartman.config do
|
135
|
-
|
158
|
+
unit_cost_field :unit_cost_in_cents
|
136
159
|
end
|
137
|
-
cart.add_item(id: 17, type: "Bottle", name: "Bordeux",
|
138
|
-
cart.add_item(id: 34, type: "Bottle", name: "Cabernet",
|
160
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost_in_cents: 9212, quantity: 2)
|
161
|
+
cart.add_item(id: 34, type: "Bottle", name: "Cabernet", unit_cost_in_cents: 9212, quantity: 2)
|
139
162
|
cart.total.should eq(36848)
|
163
|
+
Cartman.config do
|
164
|
+
unit_cost_field :unit_cost
|
165
|
+
end
|
140
166
|
end
|
141
167
|
end
|
142
168
|
|
@@ -149,6 +175,7 @@ describe Cartman do
|
|
149
175
|
Cartman.config.redis.exists("cartman:line_item:1").should be_false
|
150
176
|
Cartman.config.redis.exists("cartman:line_item:2").should be_false
|
151
177
|
Cartman.config.redis.exists("cartman:cart:1:index").should be_false
|
178
|
+
Cartman.config.redis.exists("cartman:cart:1:index:Bottle:17").should be_false
|
152
179
|
end
|
153
180
|
end
|
154
181
|
|
@@ -157,6 +184,8 @@ describe Cartman do
|
|
157
184
|
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, cost_in_cents: 18424, quantity: 2)
|
158
185
|
cart.touch
|
159
186
|
cart.ttl.should eq(Cartman.config.cart_expires_in)
|
187
|
+
Cartman.config.redis.ttl("cartman:cart:1:index").should eq(Cartman.config.cart_expires_in)
|
188
|
+
Cartman.config.redis.ttl("cartman:cart:1:index:Bottle:17").should eq(Cartman.config.cart_expires_in)
|
160
189
|
end
|
161
190
|
end
|
162
191
|
|
@@ -167,12 +196,14 @@ describe Cartman do
|
|
167
196
|
end
|
168
197
|
|
169
198
|
it "should rename the key, and index_key if it exists" do
|
170
|
-
cart.add_item(id: 17, name: "Bordeux", unit_cost: 92.12, cost_in_cents: 18424, quantity: 2)
|
199
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, cost_in_cents: 18424, quantity: 2)
|
171
200
|
cart.reassign(2)
|
172
201
|
Cartman.config.redis.exists("cartman:cart:1").should be_false
|
173
202
|
Cartman.config.redis.exists("cartman:cart:1:index").should be_false
|
203
|
+
Cartman.config.redis.exists("cartman:cart:1:index:Bottle:17").should be_false
|
174
204
|
Cartman.config.redis.exists("cartman:cart:2").should be_true
|
175
|
-
Cartman.config.redis.exists("cartman:cart:2:index").should
|
205
|
+
Cartman.config.redis.exists("cartman:cart:2:index").should be_true
|
206
|
+
Cartman.config.redis.exists("cartman:cart:2:index:Bottle:17").should be_true
|
176
207
|
cart.send(:key)[-1].should eq("2")
|
177
208
|
cart.add_item(id: 18, type: "Bottle", name: "Bordeux", unit_cost: 92.12, cost_in_cents: 18424, quantity: 2)
|
178
209
|
cart.reassign(1)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cartman do
|
4
|
+
describe Cartman::ItemCollection do
|
5
|
+
before(:all) do
|
6
|
+
end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
Cartman.config.redis.flushdb
|
10
|
+
Object.send(:remove_const, :Bottle) if defined?(Bottle)
|
11
|
+
Bottle = double
|
12
|
+
Bottle.stub(:find).and_return(bottle)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#each_with_object" do
|
16
|
+
let(:bottle) { double("Bottle") }
|
17
|
+
it "should be magical" do
|
18
|
+
cart = Cartman::Cart.new(1)
|
19
|
+
cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, cost: 184.24, quantity: 2)
|
20
|
+
expect { |b| cart.items.each_with_object(&b)}.to yield_successive_args([cart.items.first, bottle])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/item_spec.rb
CHANGED
@@ -3,13 +3,13 @@ require 'spec_helper'
|
|
3
3
|
describe Cartman do
|
4
4
|
describe Cartman::Item do
|
5
5
|
let(:cart) { Cartman::Cart.new(1) }
|
6
|
-
let(:item) { cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12,
|
6
|
+
let(:item) { cart.add_item(id: 17, type: "Bottle", name: "Bordeux", unit_cost: 92.12, quantity: 2) }
|
7
7
|
|
8
8
|
describe "data getters and setters" do
|
9
9
|
it "should let me get data stored for the item" do
|
10
10
|
item.id.should eq("17")
|
11
11
|
item.type.should eq("Bottle")
|
12
|
-
item.cost.should eq(
|
12
|
+
item.cost.should eq(184.24)
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should let me modify data stored for the item" do
|
@@ -26,10 +26,18 @@ describe Cartman do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
describe "#
|
29
|
+
describe "#cost" do
|
30
|
+
it "should be equal to the unit_cost multiplied by the quantity" do
|
31
|
+
item.cost.should eq(184.24)
|
32
|
+
item.quantity = 3
|
33
|
+
item.cost.should eq(276.36)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#destroy" do
|
30
38
|
it "should remove the item from the cart" do
|
31
39
|
item_id = item._id
|
32
|
-
item.
|
40
|
+
item.destroy
|
33
41
|
Cartman.config.redis.sismember(cart.send(:key), item_id).should be_false
|
34
42
|
Cartman.config.redis.exists("cartman:line_item:#{item_id}").should be_false
|
35
43
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cartman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
@@ -61,8 +61,10 @@ files:
|
|
61
61
|
- lib/cartman/cart.rb
|
62
62
|
- lib/cartman/configuration.rb
|
63
63
|
- lib/cartman/item.rb
|
64
|
+
- lib/cartman/item_collection.rb
|
64
65
|
- lib/cartman/version.rb
|
65
66
|
- spec/cart_spec.rb
|
67
|
+
- spec/item_collection_spec.rb
|
66
68
|
- spec/item_spec.rb
|
67
69
|
- spec/spec_helper.rb
|
68
70
|
homepage: http://github.com/willcosgrove/cartman
|
@@ -79,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
81
|
version: '0'
|
80
82
|
segments:
|
81
83
|
- 0
|
82
|
-
hash:
|
84
|
+
hash: -4477531500718157343
|
83
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
86
|
none: false
|
85
87
|
requirements:
|
@@ -88,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
90
|
version: '0'
|
89
91
|
segments:
|
90
92
|
- 0
|
91
|
-
hash:
|
93
|
+
hash: -4477531500718157343
|
92
94
|
requirements: []
|
93
95
|
rubyforge_project:
|
94
96
|
rubygems_version: 1.8.23
|
@@ -97,5 +99,6 @@ specification_version: 3
|
|
97
99
|
summary: Doing shopping carts like a boss since 2012
|
98
100
|
test_files:
|
99
101
|
- spec/cart_spec.rb
|
102
|
+
- spec/item_collection_spec.rb
|
100
103
|
- spec/item_spec.rb
|
101
104
|
- spec/spec_helper.rb
|