the_cart 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/the_cart.rb +20 -0
- data/lib/the_cart/configuration.rb +9 -0
- data/lib/the_cart/item.rb +73 -0
- data/lib/the_cart/shopper.rb +81 -0
- data/lib/the_cart/version.rb +3 -0
- data/spec/fixtures/product.rb +27 -0
- data/spec/fixtures/shopper.rb +13 -0
- data/spec/lib/shopper_spec.rb +86 -0
- data/spec/lib/the_cart_spec.rb +16 -0
- data/spec/spec_helper.rb +10 -0
- data/the_cart.gemspec +25 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c017ad342b406e5843d8a8e15e9f3a18954acafe
|
4
|
+
data.tar.gz: 2a4411c25b66d1b1f011c1c6b3bdb33b3929ddd8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: de6f8754228fdaa47a3f07fce2e2b93de9419131b7e7c905bca9beddbf413a4ffd4a4cedb53b5af1cf6910ace52d5f76f4c84f33185a287bf30ff18f03083aa2
|
7
|
+
data.tar.gz: 24fe4f0b419d8f50567c7b48e4ec4ae209ad0eb5b4253f20cc6eb9ef8887a9b436eab0925d5501989bef15fa40e0b05a41693701447afc2b450bb5ff67021297
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Elad Meidar
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# TheCart
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'the_cart'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install the_cart
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( http://github.com/<my-github-username>/the_cart/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/the_cart.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "the_cart/version"
|
2
|
+
require 'the_cart/configuration'
|
3
|
+
require 'the_cart/shopper'
|
4
|
+
require 'the_cart/item'
|
5
|
+
|
6
|
+
require 'redis'
|
7
|
+
module TheCart
|
8
|
+
|
9
|
+
attr_reader :configuration
|
10
|
+
|
11
|
+
def self.configure(&block)
|
12
|
+
@configuration = TheCart::Configuration.new
|
13
|
+
yield(@configuration)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.redis
|
17
|
+
@configuration.redis ||= Redis.new
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module TheCart
|
2
|
+
module Item
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
|
7
|
+
include InstanceMethods
|
8
|
+
extend ClassMethods
|
9
|
+
|
10
|
+
after_save :update_cart_details
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
# Mark the list of fields to track in redis for fast access.
|
17
|
+
def cartify_item(options = {})
|
18
|
+
|
19
|
+
configuration = {
|
20
|
+
track: [:id],
|
21
|
+
price_field: :price
|
22
|
+
}.merge(options)
|
23
|
+
|
24
|
+
configuration[:track] << :id
|
25
|
+
@price_column = configuration[:price_field]
|
26
|
+
|
27
|
+
self.cart_fields = configuration[:track]
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def price_column
|
32
|
+
@price_column ||= :price
|
33
|
+
end
|
34
|
+
# Set trackable fields
|
35
|
+
def cart_fields=(new_field_list)
|
36
|
+
@cart_fields = new_field_list
|
37
|
+
end
|
38
|
+
|
39
|
+
# Retrieve trackable fields
|
40
|
+
def cart_fields
|
41
|
+
@cart_fields ||= []
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
module InstanceMethods
|
47
|
+
|
48
|
+
def update_cart_details
|
49
|
+
TheCart.redis.multi do
|
50
|
+
attr_hash = {}
|
51
|
+
|
52
|
+
self.class.cart_fields.each do |field|
|
53
|
+
attr_hash[field] = self.send(field)
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_hash["price"] = self.send(self.class.price_column)
|
57
|
+
attr_hash["class"] = self.class.name
|
58
|
+
|
59
|
+
TheCart.redis.hmset cart_item_id_key, *attr_hash.to_a
|
60
|
+
end
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def cart_item_id_key
|
68
|
+
"the_cart:#{self.id}:item_details"
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module TheCart
|
2
|
+
module Shopper
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
include InstanceMethods
|
7
|
+
extend ClassMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
|
13
|
+
def cart
|
14
|
+
|
15
|
+
TheCart.redis.zrange(self.cart_id_key, 0, -1, {withscores: true}).map { |item|
|
16
|
+
item_id, quantity = item
|
17
|
+
hash = TheCart.redis.hgetall "the_cart:#{item_id}:item_details"
|
18
|
+
hash["quantity"] = quantity.to_i
|
19
|
+
hash
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def item_in_cart?(item)
|
24
|
+
TheCart.redis.zrank(self.cart_id_key, item.id) != nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_item_to_cart(item)
|
28
|
+
if item_in_cart?(item)
|
29
|
+
TheCart.redis.zincrby self.cart_id_key, 1, item.id
|
30
|
+
else
|
31
|
+
TheCart.redis.zadd self.cart_id_key, 1, item.id
|
32
|
+
end
|
33
|
+
TheCart.redis.expire self.cart_id_key, self.class.cart_configuration[:expire_cart_in].to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove_item_from_cart(item)
|
37
|
+
TheCart.redis.zrem self.cart_id_key, item.id
|
38
|
+
TheCart.redis.expire self.cart_id_key, self.class.cart_configuration[:expire_cart_in].to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
def cart_count
|
42
|
+
total = 0
|
43
|
+
TheCart.redis.zrange(self.cart_id_key, 0, -1, {withscores: true}).map { |item|
|
44
|
+
item_id, quantity = item
|
45
|
+
total += quantity.to_i
|
46
|
+
}
|
47
|
+
total
|
48
|
+
end
|
49
|
+
|
50
|
+
def cart_total
|
51
|
+
total = 0
|
52
|
+
self.cart.map {|item| total += item["price"].to_f * item["quantity"].to_i }
|
53
|
+
total
|
54
|
+
end
|
55
|
+
|
56
|
+
def clear_cart!
|
57
|
+
TheCart.redis.del self.cart_id_key
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def cart_id_key
|
63
|
+
"the_cart:#{self.id}:cart"
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
module ClassMethods
|
69
|
+
|
70
|
+
def cartify(options = {})
|
71
|
+
@the_cart_configuration = {
|
72
|
+
expire_cart_in: 24
|
73
|
+
}.merge(options)
|
74
|
+
end
|
75
|
+
|
76
|
+
def cart_configuration
|
77
|
+
@the_cart_configuration
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Product
|
2
|
+
|
3
|
+
attr_accessor :id, :name, :price
|
4
|
+
|
5
|
+
def Product.after_save(method)
|
6
|
+
@after_save_method = method
|
7
|
+
end
|
8
|
+
|
9
|
+
include TheCart::Item
|
10
|
+
|
11
|
+
cartify_item track: [:id, :name]
|
12
|
+
|
13
|
+
|
14
|
+
def initialize(id, name, price)
|
15
|
+
@id = id
|
16
|
+
@name = name
|
17
|
+
@price = price
|
18
|
+
end
|
19
|
+
|
20
|
+
def Product.after_save_method
|
21
|
+
@after_save_method
|
22
|
+
end
|
23
|
+
|
24
|
+
def save
|
25
|
+
self.send(Product.after_save_method)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fixtures/shopper'
|
3
|
+
require 'fixtures/product'
|
4
|
+
|
5
|
+
describe TheCart::Shopper do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
TheCart.configure do |c|
|
9
|
+
c.redis = Redis.new
|
10
|
+
end
|
11
|
+
|
12
|
+
@shopper = Shopper.new(2)
|
13
|
+
@shopper.clear_cart!
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have an empty cart" do
|
17
|
+
@shopper.cart.should eq([])
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have 0 items" do
|
21
|
+
@shopper.cart_count.should eq(0)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
describe "adding an item" do
|
26
|
+
before(:all) do
|
27
|
+
@shopper.clear_cart!
|
28
|
+
@product = Product.new(1, "Computer", 500)
|
29
|
+
@product_not_buying = Product.new(6, "Doll", 25)
|
30
|
+
@product.save
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should add an item" do
|
34
|
+
@shopper.add_item_to_cart(@product)
|
35
|
+
@shopper.cart_count.should eq(1)
|
36
|
+
@shopper.cart.first.should eq({"id"=>"1", "name"=>"Computer", "price"=>"500", "class"=>"Product", "quantity" => 1})
|
37
|
+
end
|
38
|
+
|
39
|
+
it "Should return true if item in cart" do
|
40
|
+
@shopper.item_in_cart?(@product).should eq(true)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "Should return false if item not in cart" do
|
44
|
+
@shopper.item_in_cart?(@product_not_buying).should eq(false)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "Should remove an item" do
|
48
|
+
@shopper.remove_item_from_cart(@product)
|
49
|
+
@shopper.cart.count.should eq(0)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "should return cart total" do
|
54
|
+
before(:all) do
|
55
|
+
@shopper.clear_cart!
|
56
|
+
@product = Product.new(1, "Computer", 500)
|
57
|
+
@product_expensive = Product.new(2, "Apple Computer", 1500)
|
58
|
+
|
59
|
+
@product_expensive.save
|
60
|
+
@product.save
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should return 0 for empty cart" do
|
64
|
+
@shopper.cart_total.should eq(0)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should return combined prices for products in cart" do
|
68
|
+
@shopper.add_item_to_cart(@product)
|
69
|
+
@shopper.add_item_to_cart(@product_expensive)
|
70
|
+
|
71
|
+
@shopper.cart_count.should eq(2)
|
72
|
+
@shopper.cart_total.should eq(2000.0)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should calculate quantity in total price" do
|
76
|
+
@shopper.clear_cart!
|
77
|
+
|
78
|
+
@shopper.add_item_to_cart(@product)
|
79
|
+
@shopper.add_item_to_cart(@product)
|
80
|
+
@shopper.add_item_to_cart(@product_expensive)
|
81
|
+
|
82
|
+
@shopper.cart_count.should eq(3)
|
83
|
+
@shopper.cart_total.should eq(2500.0)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/the_cart.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'the_cart/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "the_cart"
|
8
|
+
spec.version = TheCart::VERSION
|
9
|
+
spec.authors = ["Elad Meidar"]
|
10
|
+
spec.email = ["elad@eizesus.com"]
|
11
|
+
spec.summary = "Cart implementation in Redis"
|
12
|
+
spec.description = "Online cart implementation in redis"
|
13
|
+
spec.homepage = "https://github.com/ShinobiDevs/the_cart"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency 'rspec'
|
24
|
+
spec.add_dependency 'redis'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: the_cart
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Elad Meidar
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: redis
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Online cart implementation in redis
|
70
|
+
email:
|
71
|
+
- elad@eizesus.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- .rspec
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- lib/the_cart.rb
|
83
|
+
- lib/the_cart/configuration.rb
|
84
|
+
- lib/the_cart/item.rb
|
85
|
+
- lib/the_cart/shopper.rb
|
86
|
+
- lib/the_cart/version.rb
|
87
|
+
- spec/fixtures/product.rb
|
88
|
+
- spec/fixtures/shopper.rb
|
89
|
+
- spec/lib/shopper_spec.rb
|
90
|
+
- spec/lib/the_cart_spec.rb
|
91
|
+
- spec/spec_helper.rb
|
92
|
+
- the_cart.gemspec
|
93
|
+
homepage: https://github.com/ShinobiDevs/the_cart
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 2.2.0
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: Cart implementation in Redis
|
117
|
+
test_files:
|
118
|
+
- spec/fixtures/product.rb
|
119
|
+
- spec/fixtures/shopper.rb
|
120
|
+
- spec/lib/shopper_spec.rb
|
121
|
+
- spec/lib/the_cart_spec.rb
|
122
|
+
- spec/spec_helper.rb
|