active_cart 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/README.rdoc +24 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/generators/schwacms-gallery/schwacms_gallery_generator.rb +11 -0
- data/generators/schwacms-gallery/templates/schwacms_gallery_migration.rb +26 -0
- data/install.rb +1 -0
- data/lib/active_cart/cart.rb +96 -0
- data/lib/active_cart/cart_storage.rb +71 -0
- data/lib/active_cart/order_total.rb +58 -0
- data/lib/active_cart/order_total_collection.rb +75 -0
- data/lib/active_cart.rb +12 -0
- data/test/mocks/test_cart_storage.rb +7 -0
- data/test/mocks/test_item.rb +20 -0
- data/test/mocks/test_order_total.rb +15 -0
- data/test/performance/browsing_test.rb +9 -0
- data/test/test_helper.rb +16 -0
- data/test/unit/cart_storage_test.rb +137 -0
- data/test/unit/cart_test.rb +181 -0
- data/test/unit/order_total_collection_test.rb +87 -0
- data/uninstall.rb +1 -0
- metadata +82 -0
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
== ActiveCart
|
2
|
+
|
3
|
+
ActiveCart is a Shopping Cart framework, it's not a fullyfledged cart, so you will need to do some stuff to get it to work.
|
4
|
+
|
5
|
+
The cart system has a storage engine, which means you aren't bound to a particular database (Eventually there will be different engines already built to get you started
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
gem install active_cart
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'active_cart'
|
12
|
+
|
13
|
+
@cart = ActiveCart::Cart.setup(MyStorageEngine.new) do |t|
|
14
|
+
t << ShippingOrderTotal.new
|
15
|
+
t << GstOrderTotal.new
|
16
|
+
end
|
17
|
+
|
18
|
+
In this example the ShippingOrderTotal and GstOrderTotal have been created by the developer and follow the OrderTotal interface.
|
19
|
+
|
20
|
+
== Todo
|
21
|
+
|
22
|
+
Write some actual Storage Engines and OrderTotal examples.
|
23
|
+
|
24
|
+
Copyright (c) 2010 Myles Eftos (myles@madpilot.com.au), released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = "active_cart"
|
11
|
+
gem.summary = "Shopping Cart framework gem. Supports 'storage engines' and order total plugins"
|
12
|
+
gem.description = "You can use active-cart as the basis of a shopping cart system. It's definately not complete, you need to build a website around it."
|
13
|
+
gem.email = "myles@madpilot.com.au"
|
14
|
+
gem.homepage = "http://gemcutter.org/gems/active-cart"
|
15
|
+
gem.authors = ["Myles Eftos"]
|
16
|
+
gem.version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
17
|
+
end
|
18
|
+
Jeweler::GemcutterTasks.new
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'rake/testtask'
|
24
|
+
Rake::TestTask.new(:test) do |test|
|
25
|
+
test.libs << 'lib' << 'test'
|
26
|
+
test.pattern = 'test/unit/*_test.rb'
|
27
|
+
test.verbose = true
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
require 'rcov/rcovtask'
|
32
|
+
Rcov::RcovTask.new do |test|
|
33
|
+
test.libs << 'test'
|
34
|
+
test.pattern = 'test/unit/*_test.rb'
|
35
|
+
test.verbose = true
|
36
|
+
end
|
37
|
+
rescue LoadError
|
38
|
+
task :rcov do
|
39
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
task :test => :check_dependencies
|
44
|
+
|
45
|
+
task :default => :test
|
46
|
+
|
47
|
+
require 'rake/rdoctask'
|
48
|
+
Rake::RDocTask.new do |rdoc|
|
49
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "active_cart #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class SchwacmsGalleryGenerator < Rails::Generator::NamedBase
|
2
|
+
def initialize(runtime_args, runtime_options = {})
|
3
|
+
super
|
4
|
+
end
|
5
|
+
|
6
|
+
def manifest
|
7
|
+
record do |m|
|
8
|
+
m.migration_template 'schwacms_gallery_migration.rb', 'db/migrate', :migration_file_name => 'create_schwacms_gallery'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class CreateSchwacmsGallery < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table "galleries", :force => true do |t|
|
4
|
+
t.integer "width"
|
5
|
+
t.integer "height"
|
6
|
+
t.integer "thumbnail_width"
|
7
|
+
t.integer "thumbnail_height"
|
8
|
+
t.datetime "created_at"
|
9
|
+
t.datetime "updated_at"
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table "gallery_images", :force => true do |t|
|
13
|
+
t.string "path"
|
14
|
+
t.text "description"
|
15
|
+
t.integer "gallery_id"
|
16
|
+
t.integer "position"
|
17
|
+
t.datetime "created_at"
|
18
|
+
t.datetime "updated_at"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.down
|
23
|
+
drop_table "gallery_images"
|
24
|
+
drop_table "galleries"
|
25
|
+
end
|
26
|
+
end
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ActiveCart
|
2
|
+
# The Cart class is the core class in ActiveCart. It is a singleton (so you can only have one cart per application), that gets setup initially by passing in
|
3
|
+
# a storage engine instance. Storage engines abstract away the storage of the cart, and is left as an exercise to the user. See the Storage engine docs
|
4
|
+
# for details.
|
5
|
+
#
|
6
|
+
# The Cart class also takes order_total objects, which will calculate order totals. it may include thinkgs like shipping, or gift vouchers etc. See the Order Total
|
7
|
+
# docs for details.
|
8
|
+
#
|
9
|
+
# The Cart object delegates a number of Array methods:
|
10
|
+
# :[], :<<, :[]=, :at, :clear, :collect, :map, :delete, :delete_at, :each, :each_index, :empty?, :eql?, :first, :include?, :index, :inject, :last, :length, :pop, :push, :shift, :size, :unshift
|
11
|
+
#
|
12
|
+
class Cart
|
13
|
+
attr_accessor :storage_engine, :order_total_calculators, :customer
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
# The method MUST be called before you call instance, otherwise you will receive and StandardError
|
17
|
+
# You need to supply a storage engine. An optional block can be given which allows you to add order total items.
|
18
|
+
#
|
19
|
+
# A typical initialization block might look like this
|
20
|
+
#
|
21
|
+
# @cart = Cart.new(MyAwesomeStorageEngine.new) do |o|
|
22
|
+
# o << ShippingOrderTotal.new
|
23
|
+
# o << GstOrderTotal.new
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
def initialize(storage_engine, &block)
|
27
|
+
@storage_engine = storage_engine
|
28
|
+
@order_total_calculators = OrderTotalCollection.new(self)
|
29
|
+
|
30
|
+
if block_given?
|
31
|
+
yield order_total_calculators
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
extend Forwardable
|
36
|
+
# Storage Engine Array delegators
|
37
|
+
def_delegators :@storage_engine, :[], :<<, :[]=, :at, :clear, :collect, :map, :delete, :delete_at, :each, :each_index, :empty?, :eql?, :first, :include?, :index, :inject, :last, :length, :pop, :push, :shift, :size, :unshift
|
38
|
+
|
39
|
+
# Returns a unique id for the invoice. It's upto the storage engine to generate and track these numbers
|
40
|
+
#
|
41
|
+
def invoice_id
|
42
|
+
@storage_engine.invoice_id
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the sub-total of all the items in the cart. Usually returns a float.
|
46
|
+
#
|
47
|
+
# @cart.sub_total # => 100.00
|
48
|
+
#
|
49
|
+
def quantity
|
50
|
+
@storage_engine.quantity
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the subtotal of cart, which is effectively the total of all the items multiplied by their quantites. Does NOT include order_totals
|
54
|
+
def sub_total
|
55
|
+
@storage_engine.sub_total
|
56
|
+
end
|
57
|
+
|
58
|
+
# Adds an item to the cart. If the item already exists in the cart (identified by the id of the item), then the quantity will be increased but the supplied quantity (default: 1)
|
59
|
+
#
|
60
|
+
# @cart.add_to_cart(item, 5)
|
61
|
+
# @cart.quantity # => 5
|
62
|
+
#
|
63
|
+
# @cart.add_to_cart(item, 2)
|
64
|
+
# @cart.quantity # => 7
|
65
|
+
# @cart[0].size # => 7
|
66
|
+
# @cart[1] # => nil
|
67
|
+
#
|
68
|
+
# @cart.add_to_cart(item_2, 4)
|
69
|
+
# @cart.quantity => 100
|
70
|
+
# @cart[0].size # => 7
|
71
|
+
# @cart[1].size # => 4
|
72
|
+
#
|
73
|
+
def add_to_cart(item, quantity = 1)
|
74
|
+
@storage_engine.add_to_cart(item, quantity)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Removes an item from the cart (identified by the id of the item). If the supplied quantity is greater than equal to the number in the cart, the item will be removed, otherwise the quantity will simply be decremented by the supplied amount
|
78
|
+
#
|
79
|
+
# @cart.add_to_cart(item, 5)
|
80
|
+
# @cart[0].quantity # => 5
|
81
|
+
# @cart.remove_from_cart(item, 3)
|
82
|
+
# @cart[0].quantity # => 2
|
83
|
+
# @cart.remove_from_cart(item, 2)
|
84
|
+
# @cart[0] # => nil
|
85
|
+
#
|
86
|
+
def remove_from_cart(item, quantity = 1)
|
87
|
+
@storage_engine.remove_from_cart(item, quantity)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns the total of the cart. This includes all the order_total calculations
|
91
|
+
#
|
92
|
+
def total
|
93
|
+
sub_total + order_total_calculators.total
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Mixin this module into the class you want to user as your storage class. Remember to override the invoice_id method
|
2
|
+
#
|
3
|
+
module ActiveCart
|
4
|
+
module CartStorage
|
5
|
+
# Returns the unique invoice_id for this cart instance. This MUST be overriden by the concrete class this module is mixed into, otherwise you
|
6
|
+
# will get a NotImplementedError
|
7
|
+
#
|
8
|
+
def invoice_id
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the sub-total of all the items in the cart. Usually returns a float.
|
13
|
+
#
|
14
|
+
# @cart.sub_total # => 100.00
|
15
|
+
#
|
16
|
+
def sub_total
|
17
|
+
inject(0) { |t, item| t + (item.quantity * item.price) }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the number of items in the cart. It takes into account the individual quantities of each item, eg if there are 3 items in the cart, each with a quantity of 2, this will return 6
|
21
|
+
#
|
22
|
+
def quantity
|
23
|
+
inject(0) { |t, item| t + item.quantity }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Adds an item to the cart. If the item already exists in the cart (identified by the id of the item), then the quantity will be increased but the supplied quantity (default: 1)
|
27
|
+
#
|
28
|
+
# @cart.add_to_cart(item, 5)
|
29
|
+
# @cart.quantity # => 5
|
30
|
+
#
|
31
|
+
# @cart.add_to_cart(item, 2)
|
32
|
+
# @cart.quantity # => 7
|
33
|
+
# @cart[0].quantity # => 7
|
34
|
+
# @cart[1] # => nil
|
35
|
+
#
|
36
|
+
# @cart.add_to_cart(item_2, 4)
|
37
|
+
# @cart.quantity => 100
|
38
|
+
# @cart[0].quantity # => 7
|
39
|
+
# @cart[1].quantity # => 4
|
40
|
+
#
|
41
|
+
def add_to_cart(item, quantity = 1)
|
42
|
+
if self.include?(item)
|
43
|
+
index = self.index(item)
|
44
|
+
self.at(index).quantity += quantity
|
45
|
+
else
|
46
|
+
item.quantity += quantity
|
47
|
+
self << item
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Removes an item from the cart (identified by the id of the item). If the supplied quantity is greater than equal to the number in the cart, the item will be removed, otherwise the quantity will simply be decremented by the supplied amount
|
52
|
+
#
|
53
|
+
# @cart.add_to_cart(item, 5)
|
54
|
+
# @cart[0].quantity # => 5
|
55
|
+
# @cart.remove_from_cart(item, 3)
|
56
|
+
# @cart[0].quantity # => 2
|
57
|
+
# @cart.remove_from_cart(item, 2)
|
58
|
+
# @cart[0] # => nil
|
59
|
+
#
|
60
|
+
def remove_from_cart(item, quantity = 1)
|
61
|
+
if self.include?(item)
|
62
|
+
index = self.index(item)
|
63
|
+
if (existing = self.at(index)).quantity - quantity > 0
|
64
|
+
existing.quantity = existing.quantity - quantity
|
65
|
+
else
|
66
|
+
self.delete_at(index)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Mixin this module into any classes that act as an order_total.
|
2
|
+
#
|
3
|
+
# You need to overwrite the price, name and description methods
|
4
|
+
#
|
5
|
+
module ActiveCart
|
6
|
+
module OrderTotal
|
7
|
+
|
8
|
+
# Returns a boolean depending if the order total is active in this particular cart
|
9
|
+
#
|
10
|
+
# @order_total.active? # => true
|
11
|
+
#
|
12
|
+
def active?
|
13
|
+
@active || false
|
14
|
+
end
|
15
|
+
|
16
|
+
# Make a particular order_total active
|
17
|
+
#
|
18
|
+
# @order_total.active = true
|
19
|
+
# @order_total.active? # => true
|
20
|
+
#
|
21
|
+
# @order_total.active = false
|
22
|
+
# @order_total.active? # => falese
|
23
|
+
#
|
24
|
+
def active=(active)
|
25
|
+
@active = active
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the adjustment caused by this order_total object. Takes a cart object as a parameter, allowing the object to interrogate the items in the cart.
|
29
|
+
#
|
30
|
+
# This must be overriden in the mixee class, otherwise it will throw a NotImplementedError
|
31
|
+
#
|
32
|
+
# @order.price(@cart) => 2
|
33
|
+
#
|
34
|
+
def price(cart)
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the friendly name of the order total. Can be used for display (Such as on an invoices etc)
|
39
|
+
#
|
40
|
+
# This must be overriden in the mixee class, otherwise it will throw a NotImplementedError
|
41
|
+
#
|
42
|
+
# @order.name # => 'My awesome order total class'
|
43
|
+
#
|
44
|
+
def name
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a short description of the order total. Can be used for display (Such as on an invoices etc)
|
49
|
+
#
|
50
|
+
# This must be overriden in the mixee class, otherwise it will throw a NotImplementedError
|
51
|
+
#
|
52
|
+
# @order.description # => "This example order class doesn't do much"
|
53
|
+
#
|
54
|
+
def description
|
55
|
+
raise NotImplementedError
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module ActiveCart
|
2
|
+
class OrderTotalCollection < Array
|
3
|
+
# Returns a new OrderTotalCollection
|
4
|
+
#
|
5
|
+
# You must pass in an cart object.
|
6
|
+
#
|
7
|
+
# collection = OrderTotalCollection.new(@cart)
|
8
|
+
#
|
9
|
+
# You can also pass in an array of order_total objects
|
10
|
+
#
|
11
|
+
# cart = ActiveCart::Cart.instance
|
12
|
+
# collection = OrderTotalCollection.new(cart, order_total_1, order_total_2) # => [ order_total_1, order_total_2 ]
|
13
|
+
#
|
14
|
+
def initialize(cart, *seed)
|
15
|
+
@cart = cart
|
16
|
+
seed.each do |s|
|
17
|
+
self.push(s)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Concatenation.Returns a new OrderTotalCollection built by concatenating the two OrderTotalCollections together to produce a third OrderTotalCollection. (The supplied collection can be a regular array)
|
22
|
+
#
|
23
|
+
# [ order_total_1, order_total_2, order_total_3 ] + [ order_total_4, order_total_5 ] #=> [ order_total_1, order_total_2, order_total_3, order_total_4, order_total_5 ]
|
24
|
+
#
|
25
|
+
def +(order_total_collection)
|
26
|
+
tmp = OrderTotalCollection.new(@cart)
|
27
|
+
self.each { |s| tmp << s }
|
28
|
+
order_total_collection.each { |s| tmp << s }
|
29
|
+
tmp
|
30
|
+
end
|
31
|
+
|
32
|
+
# Inserts the items before the item that is currently at the supplied index
|
33
|
+
#
|
34
|
+
# items = [ order_total_1, order_total_2 ]
|
35
|
+
# items.insert_before(1, order_total_2) #=> [ order_total_1, order_total_3, order_total_2 ]
|
36
|
+
#
|
37
|
+
def insert_before(index, *items)
|
38
|
+
items.reverse.each do |item|
|
39
|
+
self.insert(index, item)
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# Inserts the items after the item that is currently at the supplied index
|
45
|
+
#
|
46
|
+
# items = [ order_total_1, order_total_2 ]
|
47
|
+
# items.insert_after(0, order_total_2) #=> [ order_total_1, order_total_3, order_total_2 ]
|
48
|
+
#
|
49
|
+
def insert_after(index, *items)
|
50
|
+
items.each_with_index do |item, i|
|
51
|
+
self.insert(index + i + 1, item)
|
52
|
+
end
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# Allows you to reorder the order totals. Moves the item at index <em>from</em> to index <em>to</em>
|
57
|
+
#
|
58
|
+
# items = [ order_total_1, order_total_2 ]
|
59
|
+
# items.move(0, 1) #=> [ order_total_2, order_total_1 ]
|
60
|
+
#
|
61
|
+
def move(from, to)
|
62
|
+
index = self.delete_at(from)
|
63
|
+
self.insert(to, index)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Calculates the total price applied by all the order_total objects.
|
67
|
+
#
|
68
|
+
# order_total_collection = OrderTotalCollection.new(nil, order_total_1, order_total_2)
|
69
|
+
# order_total_collection.total # => 10
|
70
|
+
#
|
71
|
+
def total
|
72
|
+
self.inject(0) { |t, calculator| t + (calculator.active? ? calculator.price(@cart) : 0) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/active_cart.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'active_cart'))
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require 'forwardable'
|
5
|
+
require 'active_cart/cart_storage'
|
6
|
+
require 'active_cart/order_total'
|
7
|
+
require 'active_cart/order_total_collection'
|
8
|
+
require 'active_cart/cart'
|
9
|
+
|
10
|
+
module ActiveCart
|
11
|
+
VERSION = File.exist?('VERSION') ? File.read('VERSION') : ""
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class TestItem
|
2
|
+
attr_accessor :id, :price, :quantity
|
3
|
+
|
4
|
+
def ==(item)
|
5
|
+
self.id == item.id
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(id = 1)
|
9
|
+
self.id = id
|
10
|
+
self.quantity = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def price
|
14
|
+
@price || 2
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
"TestItem: #{self.id}: #{self.quantity}x$#{self.price}"
|
19
|
+
end
|
20
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$:.unshift(File.join(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'redgreen'
|
6
|
+
require 'test/unit'
|
7
|
+
require 'shoulda'
|
8
|
+
require 'mocha'
|
9
|
+
|
10
|
+
require 'active_cart'
|
11
|
+
require 'mocks/test_cart_storage'
|
12
|
+
require 'mocks/test_item'
|
13
|
+
require 'mocks/test_order_total'
|
14
|
+
|
15
|
+
class Test::Unit::TestCase
|
16
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CartStorageTest < Test::Unit::TestCase
|
4
|
+
context '' do
|
5
|
+
setup do
|
6
|
+
@cart_storage_engine = TestCartStorage.new
|
7
|
+
@cart = ActiveCart::Cart.new(@cart_storage_engine)
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'sub_total' do
|
11
|
+
setup do
|
12
|
+
@item_1 = TestItem.new(1)
|
13
|
+
@item_2 = TestItem.new(2)
|
14
|
+
@item_3 = TestItem.new(3)
|
15
|
+
@item_1.price = 10
|
16
|
+
@item_2.price = 12
|
17
|
+
@item_3.price = 9
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
should 'return the price of a single item in the cart' do
|
22
|
+
@cart.add_to_cart(@item_1)
|
23
|
+
assert_equal 10, @cart.sub_total
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'return the price of a single item with a quantity' do
|
27
|
+
@cart.add_to_cart(@item_2, 3)
|
28
|
+
assert_equal 36, @cart.sub_total
|
29
|
+
end
|
30
|
+
|
31
|
+
should 'return the sum of all the items in the cart' do
|
32
|
+
@cart.add_to_cart(@item_1)
|
33
|
+
@cart.add_to_cart(@item_2, 3)
|
34
|
+
assert_equal 46, @cart.sub_total
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'add to cart' do
|
39
|
+
should 'add an item to the cart if the cart is empty' do
|
40
|
+
assert @cart.empty?
|
41
|
+
item = TestItem.new
|
42
|
+
@cart.add_to_cart(item)
|
43
|
+
assert_equal 1, @cart.size
|
44
|
+
assert_equal 1, @cart[0].quantity
|
45
|
+
assert_equal 1, @cart.quantity
|
46
|
+
end
|
47
|
+
|
48
|
+
should 'increase the item quantity if the same item is added to the cart again' do
|
49
|
+
assert @cart.empty?
|
50
|
+
item = TestItem.new(1)
|
51
|
+
@cart.add_to_cart(item)
|
52
|
+
assert_equal 1, @cart.size
|
53
|
+
assert_equal 1, @cart[0].quantity
|
54
|
+
|
55
|
+
item_2 = TestItem.new(1) # Has the same id, is the same as item
|
56
|
+
@cart.add_to_cart(item_2)
|
57
|
+
assert_equal 1, @cart.size
|
58
|
+
assert_equal 2, @cart[0].quantity
|
59
|
+
assert_equal 2, @cart.quantity
|
60
|
+
end
|
61
|
+
|
62
|
+
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
|
63
|
+
assert @cart.empty?
|
64
|
+
item = TestItem.new(1)
|
65
|
+
@cart.add_to_cart(item)
|
66
|
+
assert_equal 1, @cart.size
|
67
|
+
assert_equal 1, @cart[0].quantity
|
68
|
+
|
69
|
+
item_2 = TestItem.new(1) # Has the same id, is the same as item
|
70
|
+
@cart.add_to_cart(item_2, 10)
|
71
|
+
assert_equal 1, @cart.size
|
72
|
+
assert_equal 11, @cart[0].quantity
|
73
|
+
assert_equal 11, @cart.quantity
|
74
|
+
end
|
75
|
+
|
76
|
+
should 'add another item to the cart' do
|
77
|
+
assert @cart.empty?
|
78
|
+
item = TestItem.new(1)
|
79
|
+
@cart.add_to_cart(item)
|
80
|
+
assert_equal 1, @cart.size
|
81
|
+
assert_equal 1, @cart[0].quantity
|
82
|
+
|
83
|
+
item_2 = TestItem.new(2)
|
84
|
+
@cart.add_to_cart(item_2, 10)
|
85
|
+
assert_equal 2, @cart.size
|
86
|
+
assert_equal 1, @cart[0].quantity
|
87
|
+
assert_equal 10, @cart[1].quantity
|
88
|
+
assert_equal 11, @cart.quantity
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'remove_from_cart' do
|
93
|
+
should 'remove the quantity supplied from the cart' do
|
94
|
+
item = TestItem.new(1)
|
95
|
+
@cart.add_to_cart(item, 10)
|
96
|
+
assert_equal 1, @cart.size
|
97
|
+
assert_equal 10, @cart.quantity
|
98
|
+
|
99
|
+
@cart.remove_from_cart(item)
|
100
|
+
assert_equal 1, @cart.size
|
101
|
+
assert_equal 9, @cart.quantity
|
102
|
+
end
|
103
|
+
|
104
|
+
should 'remove the item from the cart if the quantity to be removed is equal to the number in cart' do
|
105
|
+
item = TestItem.new(1)
|
106
|
+
@cart.add_to_cart(item, 10)
|
107
|
+
assert_equal 1, @cart.size
|
108
|
+
assert_equal 10, @cart.quantity
|
109
|
+
|
110
|
+
@cart.remove_from_cart(item, 10)
|
111
|
+
assert_equal 0, @cart.size
|
112
|
+
end
|
113
|
+
|
114
|
+
should 'remove the item from the cart if the quantity to be removed is greater than the number in cart' do
|
115
|
+
item = TestItem.new(1)
|
116
|
+
@cart.add_to_cart(item, 10)
|
117
|
+
assert_equal 1, @cart.size
|
118
|
+
assert_equal 10, @cart.quantity
|
119
|
+
|
120
|
+
@cart.remove_from_cart(item, 11)
|
121
|
+
assert_equal 0, @cart.size
|
122
|
+
end
|
123
|
+
|
124
|
+
should "simply return if the item doesn't exist in the cart" do
|
125
|
+
item = TestItem.new(1)
|
126
|
+
@cart.add_to_cart(item, 10)
|
127
|
+
assert_equal 1, @cart.size
|
128
|
+
assert_equal 10, @cart.quantity
|
129
|
+
|
130
|
+
item_2 = TestItem.new(2)
|
131
|
+
@cart.remove_from_cart(item_2, 10)
|
132
|
+
assert_equal 1, @cart.size
|
133
|
+
assert_equal 10, @cart.quantity
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CartTest < Test::Unit::TestCase
|
4
|
+
context '' do
|
5
|
+
context 'after setup' do
|
6
|
+
setup do
|
7
|
+
@cart_storage_engine = TestCartStorage.new
|
8
|
+
@cart = ActiveCart::Cart.new(@cart_storage_engine)
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'delegate to cart storage' do
|
12
|
+
should 'delegate []' do
|
13
|
+
@cart_storage_engine.expects(:[]).with(0)
|
14
|
+
@cart[0]
|
15
|
+
end
|
16
|
+
|
17
|
+
should 'delegate <<' do
|
18
|
+
test = TestItem.new
|
19
|
+
@cart_storage_engine.expects(:<<).with(test)
|
20
|
+
@cart << test
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'delegate []=' do
|
24
|
+
test = TestItem.new
|
25
|
+
@cart_storage_engine.expects(:[]=).with(0, test)
|
26
|
+
@cart[0] = test
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'delegate :at' do
|
30
|
+
@cart_storage_engine.expects(:at).with(1)
|
31
|
+
@cart.at(1)
|
32
|
+
end
|
33
|
+
|
34
|
+
should 'delegate :clear' do
|
35
|
+
@cart_storage_engine.expects(:clear)
|
36
|
+
@cart.clear
|
37
|
+
end
|
38
|
+
|
39
|
+
should 'delegate :collect' do
|
40
|
+
@cart_storage_engine.expects(:collect)
|
41
|
+
@cart.collect
|
42
|
+
end
|
43
|
+
|
44
|
+
should 'delegate :map' do
|
45
|
+
@cart_storage_engine.expects(:map)
|
46
|
+
@cart.map
|
47
|
+
end
|
48
|
+
|
49
|
+
should 'delegate :delete' do
|
50
|
+
test = TestItem.new
|
51
|
+
@cart_storage_engine.expects(:delete).with(test)
|
52
|
+
@cart.delete(test)
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'delegate :delete_at' do
|
56
|
+
@cart_storage_engine.expects(:delete_at).with(3)
|
57
|
+
@cart.delete_at(3)
|
58
|
+
end
|
59
|
+
|
60
|
+
should 'delegate :each' do
|
61
|
+
@cart_storage_engine.expects(:each)
|
62
|
+
@cart.each
|
63
|
+
end
|
64
|
+
|
65
|
+
should 'delegate :each_index' do
|
66
|
+
@cart_storage_engine.expects(:each_index)
|
67
|
+
@cart.each_index
|
68
|
+
end
|
69
|
+
|
70
|
+
should 'delegate :empty' do
|
71
|
+
@cart_storage_engine.expects(:empty?)
|
72
|
+
@cart.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
should 'delegate :eql?' do
|
76
|
+
@cart_storage_engine.expects(:eql?)
|
77
|
+
@cart.eql?
|
78
|
+
end
|
79
|
+
|
80
|
+
should 'delegate :first' do
|
81
|
+
@cart_storage_engine.expects(:first)
|
82
|
+
@cart.first
|
83
|
+
end
|
84
|
+
|
85
|
+
should 'delegate :include?' do
|
86
|
+
@cart_storage_engine.expects(:include?)
|
87
|
+
@cart.include?
|
88
|
+
end
|
89
|
+
|
90
|
+
should 'delegate :index' do
|
91
|
+
@cart_storage_engine.expects(:index)
|
92
|
+
@cart.index
|
93
|
+
end
|
94
|
+
|
95
|
+
should 'delegate :inject' do
|
96
|
+
@cart_storage_engine.expects(:inject)
|
97
|
+
@cart.inject
|
98
|
+
end
|
99
|
+
|
100
|
+
should 'delegate :last' do
|
101
|
+
@cart_storage_engine.expects(:last)
|
102
|
+
@cart.last
|
103
|
+
end
|
104
|
+
|
105
|
+
should 'delegate :length' do
|
106
|
+
@cart_storage_engine.expects(:length)
|
107
|
+
@cart.length
|
108
|
+
end
|
109
|
+
|
110
|
+
should 'delegate :pop' do
|
111
|
+
@cart_storage_engine.expects(:pop)
|
112
|
+
@cart.pop
|
113
|
+
end
|
114
|
+
|
115
|
+
should 'delegate :push' do
|
116
|
+
@cart_storage_engine.expects(:push)
|
117
|
+
@cart.push
|
118
|
+
end
|
119
|
+
|
120
|
+
should 'delegate :shift' do
|
121
|
+
@cart_storage_engine.expects(:shift)
|
122
|
+
@cart.shift
|
123
|
+
end
|
124
|
+
|
125
|
+
should 'delegate :size' do
|
126
|
+
@cart_storage_engine.expects(:size)
|
127
|
+
@cart.size
|
128
|
+
end
|
129
|
+
|
130
|
+
should 'delegate :unshift' do
|
131
|
+
@cart_storage_engine.expects(:unshift)
|
132
|
+
@cart.unshift
|
133
|
+
end
|
134
|
+
|
135
|
+
should 'delegate :invoice_id' do
|
136
|
+
@cart_storage_engine.expects(:invoice_id)
|
137
|
+
@cart.invoice_id
|
138
|
+
end
|
139
|
+
|
140
|
+
should 'delegate :sub_total' do
|
141
|
+
@cart_storage_engine.expects(:sub_total)
|
142
|
+
@cart.sub_total
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'setup' do
|
147
|
+
should 'take a block to add order_totals' do
|
148
|
+
@total_1 = TestOrderTotal.new(10, true)
|
149
|
+
@total_2 = TestOrderTotal.new(20, true)
|
150
|
+
|
151
|
+
@cart_storage_engine = TestCartStorage.new
|
152
|
+
@cart = ActiveCart::Cart.new(@cart_storage_engine) do |o|
|
153
|
+
o << @total_1
|
154
|
+
o << @total_2
|
155
|
+
end
|
156
|
+
assert_equal [ @total_1, @total_2 ], @cart.order_total_calculators
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'total' do
|
161
|
+
should 'sum all the items in the cart with order totals' do
|
162
|
+
@item_1 = TestItem.new(1)
|
163
|
+
@item_1.price = 10
|
164
|
+
@item_2 = TestItem.new(2)
|
165
|
+
@item_2.price = 20
|
166
|
+
@item_3 = TestItem.new(3)
|
167
|
+
@item_3.price = 30
|
168
|
+
@total_1 = TestOrderTotal.new(10, true)
|
169
|
+
@total_2 = TestOrderTotal.new(20, true)
|
170
|
+
|
171
|
+
@cart.order_total_calculators += [ @total_1, @total_2 ]
|
172
|
+
@cart.add_to_cart(@item_1)
|
173
|
+
@cart.add_to_cart(@item_2)
|
174
|
+
@cart.add_to_cart(@item_3)
|
175
|
+
|
176
|
+
assert_equal 90, @cart.total
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class OrderTotalCollectionTest < Test::Unit::TestCase
|
4
|
+
context '' do
|
5
|
+
setup do
|
6
|
+
@cart_storage_engine = TestCartStorage.new
|
7
|
+
@cart = ActiveCart::Cart.new(@cart_storage_engine)
|
8
|
+
@collection = ActiveCart::OrderTotalCollection.new(@cart)
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'insert_before' do
|
12
|
+
should 'insert an object before the item at the supplied index' do
|
13
|
+
@collection << '0'
|
14
|
+
@collection << '1'
|
15
|
+
@collection << '2'
|
16
|
+
@collection << '3'
|
17
|
+
@collection << '4'
|
18
|
+
|
19
|
+
@collection.insert_before(3, '5')
|
20
|
+
assert_equal [ '0', '1', '2', '5', '3', '4' ], @collection
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'insert all the objects before the item at the supplied index' do
|
24
|
+
@collection << '0'
|
25
|
+
@collection << '1'
|
26
|
+
@collection << '2'
|
27
|
+
@collection << '3'
|
28
|
+
@collection << '4'
|
29
|
+
|
30
|
+
@collection.insert_before(3, '5', '6', '7', '8')
|
31
|
+
assert_equal [ '0', '1', '2', '5', '6', '7', '8', '3', '4' ], @collection
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'insert_after' do
|
37
|
+
should 'insert an object after the item at the supplied index' do
|
38
|
+
@collection << '0'
|
39
|
+
@collection << '1'
|
40
|
+
@collection << '2'
|
41
|
+
@collection << '3'
|
42
|
+
@collection << '4'
|
43
|
+
|
44
|
+
@collection.insert_after(3, '5')
|
45
|
+
assert_equal [ '0', '1', '2', '3', '5', '4' ], @collection
|
46
|
+
end
|
47
|
+
|
48
|
+
should 'insert all the objects after the item at the supplied index' do
|
49
|
+
@collection << '0'
|
50
|
+
@collection << '1'
|
51
|
+
@collection << '2'
|
52
|
+
@collection << '3'
|
53
|
+
@collection << '4'
|
54
|
+
|
55
|
+
@collection.insert_after(3, '5', '6', '7', '8')
|
56
|
+
assert_equal [ '0', '1', '2', '3', '5', '6', '7', '8', '4' ], @collection
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'move' do
|
61
|
+
should 'move the item at the from index to the to index' do
|
62
|
+
@collection << '0'
|
63
|
+
@collection << '1'
|
64
|
+
@collection << '2'
|
65
|
+
@collection << '3'
|
66
|
+
@collection << '4'
|
67
|
+
|
68
|
+
@collection.move(3, 0)
|
69
|
+
assert_equal [ '3', '0', '1', '2', '4' ], @collection
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'total' do
|
74
|
+
setup do
|
75
|
+
@total_1 = TestOrderTotal.new(10, true)
|
76
|
+
@total_2 = TestOrderTotal.new(5, false)
|
77
|
+
@total_3 = TestOrderTotal.new(2, true)
|
78
|
+
@total_4 = TestOrderTotal.new(14, true)
|
79
|
+
end
|
80
|
+
|
81
|
+
should 'call price on each order_total item that are active' do
|
82
|
+
@collection += [ @total_1, @total_2, @total_3, @total_4 ]
|
83
|
+
assert_equal 26, @collection.total
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_cart
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Myles Eftos
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-27 00:00:00 +08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: You can use active-cart as the basis of a shopping cart system. It's definately not complete, you need to build a website around it.
|
17
|
+
email: myles@madpilot.com.au
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- .gitignore
|
26
|
+
- README.rdoc
|
27
|
+
- Rakefile
|
28
|
+
- VERSION
|
29
|
+
- generators/schwacms-gallery/schwacms_gallery_generator.rb
|
30
|
+
- generators/schwacms-gallery/templates/schwacms_gallery_migration.rb
|
31
|
+
- install.rb
|
32
|
+
- lib/active_cart.rb
|
33
|
+
- lib/active_cart/cart.rb
|
34
|
+
- lib/active_cart/cart_storage.rb
|
35
|
+
- lib/active_cart/order_total.rb
|
36
|
+
- lib/active_cart/order_total_collection.rb
|
37
|
+
- test/mocks/test_cart_storage.rb
|
38
|
+
- test/mocks/test_item.rb
|
39
|
+
- test/mocks/test_order_total.rb
|
40
|
+
- test/performance/browsing_test.rb
|
41
|
+
- test/test_helper.rb
|
42
|
+
- test/unit/cart_storage_test.rb
|
43
|
+
- test/unit/cart_test.rb
|
44
|
+
- test/unit/order_total_collection_test.rb
|
45
|
+
- uninstall.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://gemcutter.org/gems/active-cart
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options:
|
52
|
+
- --charset=UTF-8
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.3.5
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: Shopping Cart framework gem. Supports 'storage engines' and order total plugins
|
74
|
+
test_files:
|
75
|
+
- test/unit/order_total_collection_test.rb
|
76
|
+
- test/unit/cart_storage_test.rb
|
77
|
+
- test/unit/cart_test.rb
|
78
|
+
- test/mocks/test_cart_storage.rb
|
79
|
+
- test/mocks/test_order_total.rb
|
80
|
+
- test/mocks/test_item.rb
|
81
|
+
- test/test_helper.rb
|
82
|
+
- test/performance/browsing_test.rb
|