shop_bunny 0.8.5 → 0.8.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README +18 -0
- data/Rakefile +33 -0
- data/app/controllers/shop_bunny/application_controller.rb +5 -0
- data/app/controllers/shop_bunny/carts_controller.rb +5 -0
- data/app/models/{cart_item.rb → shop_bunny/cart_item.rb} +9 -13
- data/app/models/{coupon.rb → shop_bunny/coupon.rb} +14 -18
- data/app/models/shop_bunny/coupon_use.rb +7 -0
- data/app/views/{carts → shop_bunny/carts}/show.html.erb +20 -25
- data/config/locales/shop_bunny.en.yml +10 -0
- data/config/routes.rb +3 -62
- data/db/development.sqlite3 +0 -0
- data/db/migrate/20110202235446_add_raw_item_to_cart_items.rb +1 -1
- data/db/test.sqlite3 +0 -0
- data/lib/generators/install/USAGE +8 -0
- data/lib/generators/install/install_generator.rb +23 -0
- data/{config/initializers/shop_bunny.rb → lib/generators/install/templates/initializer.rb} +0 -0
- data/lib/generators/{shop_bunny → install}/templates/shopbunny_model_template.rb +0 -0
- data/lib/shop_bunny/cart_controller_module.rb +6 -14
- data/lib/shop_bunny/cart_module.rb +21 -43
- data/{config/initializers/add_cart_finder_to_controllers.rb → lib/shop_bunny/controller_helpers.rb} +5 -5
- data/lib/shop_bunny/engine.rb +10 -5
- data/lib/shop_bunny/version.rb +3 -0
- data/lib/shop_bunny.rb +6 -1
- data/spec/controllers/shop_bunny/carts_controller_spec.rb +97 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/{app → spec/dummy/app}/controllers/application_controller.rb +0 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/{app → spec/dummy/app}/models/cart.rb +1 -1
- data/{app → spec/dummy/app}/models/item.rb +1 -2
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +50 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config.ru +4 -0
- data/{db → spec/dummy/db}/migrate/20100915073016_create_items.rb +0 -0
- data/spec/dummy/db/schema.rb +61 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/models/cart_spec.rb +344 -0
- data/spec/models/shop_bunny/cart_item_spec.rb +56 -0
- data/spec/models/shop_bunny/coupon_spec.rb +177 -0
- data/spec/models/shop_bunny/coupon_use_spec.rb +6 -0
- data/spec/requests/shop_bunny/shopping_spec.rb +119 -0
- data/spec/shop_bunny_test.rb +7 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/blueprints/cart_items.rb +4 -0
- data/spec/support/blueprints/carts.rb +2 -0
- data/spec/support/blueprints/coupons.rb +50 -0
- data/spec/support/blueprints/items.rb +4 -0
- metadata +189 -24
- data/app/controllers/carts_controller.rb +0 -4
- data/app/models/coupon_use.rb +0 -15
- data/config/initializers/machinist.rb +0 -19
- data/lib/generators/shop_bunny/install_generator.rb +0 -18
- data/lib/generators/shop_bunny/templates/shopbunny_controller_template.rb +0 -3
data/README
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
ShopBunny doesn't taste like chicken.
|
2
|
+
|
3
|
+
=== Installation
|
4
|
+
1. Running the generator
|
5
|
+
Currently ShopBunny needs a "Cart" model in the host application.
|
6
|
+
You can install both plus all necessary migrations and an initializerusing the generator:
|
7
|
+
rails generate shop_bunny:install
|
8
|
+
|
9
|
+
2. Mounting the app
|
10
|
+
Add this to your routes file:
|
11
|
+
mount ShopBunny::Engine => '/'
|
12
|
+
You can access the shopping cart at '/cart'
|
13
|
+
|
14
|
+
3. Setup you "Item" class
|
15
|
+
A Cart has man "items". To setup the class of "item" to "Product" you'd have to set the following inside
|
16
|
+
'config/initializers/shop_bunny, which was installed by the generator above:
|
17
|
+
config.item_model_class_name = 'Product'
|
18
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'ShopBunny'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
24
|
+
load 'rails/tasks/engine.rake'
|
25
|
+
|
26
|
+
|
27
|
+
Bundler::GemHelper.install_tasks
|
28
|
+
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec)
|
31
|
+
|
32
|
+
task :default => :spec
|
33
|
+
task :test => :spec
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class CartItem < ActiveRecord::Base
|
1
|
+
class ShopBunny::CartItem < ActiveRecord::Base
|
2
2
|
belongs_to :cart
|
3
3
|
# belongs_to :item, :class_name => ShopBunny.item_model_class_name
|
4
4
|
|
@@ -8,12 +8,9 @@ class CartItem < ActiveRecord::Base
|
|
8
8
|
|
9
9
|
before_validation :set_default_quantity
|
10
10
|
after_update :destroy_if_empty
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
# TODO attr_accessible :quantity
|
16
|
-
|
11
|
+
after_save :update_automatic_coupons
|
12
|
+
after_destroy :update_automatic_coupons
|
13
|
+
|
17
14
|
def total_price
|
18
15
|
quantity * item.price
|
19
16
|
end
|
@@ -45,7 +42,11 @@ class CartItem < ActiveRecord::Base
|
|
45
42
|
end
|
46
43
|
|
47
44
|
private
|
48
|
-
|
45
|
+
|
46
|
+
def update_automatic_coupons
|
47
|
+
cart.update_automatic_coupons! if cart
|
48
|
+
end
|
49
|
+
|
49
50
|
def set_default_quantity
|
50
51
|
self.quantity ||= 0
|
51
52
|
end
|
@@ -63,9 +64,4 @@ class CartItem < ActiveRecord::Base
|
|
63
64
|
ShopBunny.cart_item_enhancements.each do |enhancement|
|
64
65
|
include enhancement
|
65
66
|
end
|
66
|
-
|
67
|
-
protected
|
68
|
-
def touch_cart
|
69
|
-
cart.touch if cart
|
70
|
-
end
|
71
67
|
end
|
@@ -1,51 +1,47 @@
|
|
1
|
-
class Coupon < ActiveRecord::Base
|
1
|
+
class ShopBunny::Coupon < ActiveRecord::Base
|
2
2
|
InvalidEvent = Class.new(NoMethodError)
|
3
|
-
has_many :coupon_uses, :dependent => :destroy
|
3
|
+
has_many :coupon_uses, :dependent => :destroy, :class_name => 'ShopBunny::CouponUse'
|
4
4
|
has_many :carts, :through => :coupon_uses
|
5
5
|
belongs_to :bonus_article, :class_name => ShopBunny.item_model_class_name
|
6
6
|
|
7
7
|
validates_presence_of :code
|
8
8
|
validates_uniqueness_of :code
|
9
9
|
validates_presence_of :title
|
10
|
-
|
10
|
+
|
11
11
|
after_initialize { self.state ||= 'inactive' }
|
12
|
-
|
13
|
-
after_destroy :touch_cart
|
14
|
-
|
15
|
-
# TODO Add self destruction when coupon has expired?
|
16
|
-
|
12
|
+
|
17
13
|
scope :valid, lambda {{:conditions => ['(coupons.valid_from IS NULL OR coupons.valid_from <= ?) AND (coupons.valid_until IS NULL OR coupons.valid_until >= ?) AND coupons.state = ?', Time.now, Time.now, 'active']}}
|
18
14
|
scope :automatically_added_over, lambda {|value| {:conditions => ['value_of_automatic_add <= ?', value]}}
|
19
|
-
|
15
|
+
|
20
16
|
def expired?
|
21
17
|
not_yet_valid? || has_expired?
|
22
18
|
end
|
23
|
-
|
24
|
-
# FIXME rename?
|
19
|
+
|
25
20
|
def not_yet_valid?
|
26
21
|
Time.now < self.valid_from if self.valid_from
|
27
22
|
end
|
28
|
-
|
23
|
+
|
29
24
|
def has_expired?
|
30
25
|
Time.now > self.valid_until if self.valid_until
|
31
26
|
end
|
32
27
|
|
28
|
+
def used_up?
|
29
|
+
max_uses && max_uses <= coupon_uses.count
|
30
|
+
end
|
31
|
+
|
33
32
|
def redeemable?
|
34
|
-
!not_yet_valid? && !has_expired? && state == 'active'
|
33
|
+
!not_yet_valid? && !has_expired? && state == 'active' && !used_up?
|
35
34
|
end
|
36
35
|
|
37
36
|
def activate!
|
38
37
|
raise InvalidEvent unless state == 'inactive'
|
39
38
|
self.state = 'active'
|
39
|
+
save!
|
40
40
|
end
|
41
41
|
|
42
42
|
def redeem!
|
43
43
|
raise InvalidEvent unless redeemable?
|
44
44
|
self.state = 'redeemed'
|
45
|
-
|
46
|
-
|
47
|
-
protected
|
48
|
-
def touch_cart
|
49
|
-
carts.each {|cart| cart.touch}
|
45
|
+
save!
|
50
46
|
end
|
51
47
|
end
|
@@ -11,14 +11,14 @@
|
|
11
11
|
<tr>
|
12
12
|
<th><%= item.id %></th>
|
13
13
|
<th><%= item.price %></th>
|
14
|
-
<th><%= link_to("Add to cart", add_item_to_cart_path(:item_id => item.id)) %></th>
|
14
|
+
<th id="<%= dom_id(item) %>"><%= link_to("Add to cart", add_item_to_cart_path(:item_id => item.id)) %></th>
|
15
15
|
</tr>
|
16
16
|
<% end %>
|
17
17
|
</table>
|
18
18
|
</table>
|
19
19
|
</div>
|
20
20
|
|
21
|
-
<div
|
21
|
+
<div id="cart">
|
22
22
|
<div class="items">
|
23
23
|
<%= form_for @cart, :url => cart_path do |f| %>
|
24
24
|
<h1>Shopping Cart</h1>
|
@@ -35,20 +35,20 @@
|
|
35
35
|
<td><%= cart_item.object.item.id %></td>
|
36
36
|
<td><%= cart_item.object.item.price %></td>
|
37
37
|
<td><%= cart_item.text_field :quantity %></td>
|
38
|
-
<td><%= link_to
|
38
|
+
<td><%= link_to "Remove item #{cart_item.object.item.id}", remove_item_from_cart_path(:item_id => cart_item.object.item.id) %></td>
|
39
39
|
</tr>
|
40
40
|
<% end %>
|
41
41
|
</table>
|
42
42
|
|
43
43
|
<table class="cashpoint">
|
44
44
|
<tr>
|
45
|
-
<td>Sum:
|
45
|
+
<td>Sum: <span id="price-item-sum"><%= number_to_currency @cart.item_sum %></span></td>
|
46
46
|
</tr>
|
47
47
|
<tr>
|
48
|
-
<td>Shippin costs:
|
48
|
+
<td>Shippin costs: <span id="price-shipping-costs"><%= number_to_currency @cart.shipping_costs %></span></td>
|
49
49
|
</tr>
|
50
50
|
<tr>
|
51
|
-
<td>Total:
|
51
|
+
<td>Total: <span id="price-total"><%= number_to_currency @cart.total %></span></td>
|
52
52
|
</tr>
|
53
53
|
</table>
|
54
54
|
|
@@ -58,29 +58,24 @@
|
|
58
58
|
|
59
59
|
<div class="coupons">
|
60
60
|
<%= form_for @cart, :url => cart_path do |f| %>
|
61
|
-
<h1>Available coupons</h2>
|
62
|
-
<ul>
|
63
|
-
<% Coupon.limit(10).all.each do |coupon| %>
|
64
|
-
<li><%= coupon.code %></ti>
|
65
|
-
<% end %>
|
66
|
-
</ul>
|
67
|
-
|
68
61
|
<h2>Your coupons</h2>
|
69
|
-
|
70
|
-
|
71
|
-
|
62
|
+
<% if @cart.coupons.empty? %>
|
63
|
+
No coupons added
|
64
|
+
<% else %>
|
65
|
+
<ul id="your_coupons">
|
66
|
+
<% @cart.coupons.each do |coupon| %>
|
67
|
+
<li><%= coupon.title %></ti>
|
68
|
+
<% end %>
|
69
|
+
</ul>
|
70
|
+
<% end %>
|
71
|
+
<ul id="error_messages">
|
72
|
+
<% @cart.errors.full_messages.each do |err| %>
|
73
|
+
<li><%= err %></li>
|
72
74
|
<% end %>
|
73
75
|
</ul>
|
74
|
-
|
75
|
-
<%# TODO Show error messages %>
|
76
|
-
<%= @cart.errors if @cart.errors.any? %>
|
77
|
-
</p>
|
78
|
-
<%= f.label :coupon_code, 'Got a code?' %>
|
76
|
+
<%= f.label :coupon_code, 'Coupon code' %>
|
79
77
|
<%= f.text_field :coupon_code %>
|
80
|
-
<%= f.submit :value => "Add
|
78
|
+
<%= f.submit :value => "Add coupon" %>
|
81
79
|
<% end %>
|
82
80
|
</div>
|
83
|
-
<div class="checkout">
|
84
|
-
<%= link_to "checkout", :controller => "carts", :action => :checkout %>
|
85
|
-
</div>
|
86
81
|
</div>
|
data/config/routes.rb
CHANGED
@@ -1,65 +1,6 @@
|
|
1
|
-
|
1
|
+
ShopBunny::Engine.routes.draw do
|
2
2
|
resource :cart, :only => [:show, :update] do
|
3
|
-
|
4
|
-
|
5
|
-
get :add_item, :as => 'add_item_to'
|
6
|
-
get :remove_item, :as => 'remove_item_from'
|
3
|
+
get :add_item, :as => 'add_item_to'
|
4
|
+
get :remove_item, :as => 'remove_item_from'
|
7
5
|
end
|
8
|
-
|
9
|
-
# The priority is based upon order of creation:
|
10
|
-
# first created -> highest priority.
|
11
|
-
|
12
|
-
# Sample of regular route:
|
13
|
-
# match 'products/:id' => 'catalog#view'
|
14
|
-
# Keep in mind you can assign values other than :controller and :action
|
15
|
-
|
16
|
-
# Sample of named route:
|
17
|
-
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
|
18
|
-
# This route can be invoked with purchase_url(:id => product.id)
|
19
|
-
|
20
|
-
# Sample resource route (maps HTTP verbs to controller actions automatically):
|
21
|
-
# resources :products
|
22
|
-
|
23
|
-
# Sample resource route with options:
|
24
|
-
# resources :products do
|
25
|
-
# member do
|
26
|
-
# get 'short'
|
27
|
-
# post 'toggle'
|
28
|
-
# end
|
29
|
-
#
|
30
|
-
# collection do
|
31
|
-
# get 'sold'
|
32
|
-
# end
|
33
|
-
# end
|
34
|
-
|
35
|
-
# Sample resource route with sub-resources:
|
36
|
-
# resources :products do
|
37
|
-
# resources :comments, :sales
|
38
|
-
# resource :seller
|
39
|
-
# end
|
40
|
-
|
41
|
-
# Sample resource route with more complex sub-resources
|
42
|
-
# resources :products do
|
43
|
-
# resources :comments
|
44
|
-
# resources :sales do
|
45
|
-
# get 'recent', :on => :collection
|
46
|
-
# end
|
47
|
-
# end
|
48
|
-
|
49
|
-
# Sample resource route within a namespace:
|
50
|
-
# namespace :admin do
|
51
|
-
# # Directs /admin/products/* to Admin::ProductsController
|
52
|
-
# # (app/controllers/admin/products_controller.rb)
|
53
|
-
# resources :products
|
54
|
-
# end
|
55
|
-
|
56
|
-
# You can have the root of your site routed with "root"
|
57
|
-
# just remember to delete public/index.html.
|
58
|
-
# root :to => "welcome#index"
|
59
|
-
|
60
|
-
# See how all your routes lay out with "rake routes"
|
61
|
-
|
62
|
-
# This is a legacy wild controller route that's not recommended for RESTful applications.
|
63
|
-
# Note: This route will make all actions in every controller accessible via GET requests.
|
64
|
-
# match ':controller(/:action(/:id(.:format)))'
|
65
6
|
end
|
Binary file
|
@@ -2,7 +2,7 @@ class AddRawItemToCartItems < ActiveRecord::Migration
|
|
2
2
|
def self.up
|
3
3
|
add_column :cart_items, :raw_item, :text
|
4
4
|
|
5
|
-
CartItem.all.each do |e|
|
5
|
+
ShopBunny::CartItem.all.each do |e|
|
6
6
|
if e.read_attribute(:item_id)
|
7
7
|
i = ShopBunny.item_model_class_name.constantize.find_by_id(e.read_attribute(:item_id))
|
8
8
|
if i
|
data/db/test.sqlite3
ADDED
Binary file
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ShopBunny
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
include Rails::Generators::Migration
|
4
|
+
source_root File.expand_path("../../../../", __FILE__)
|
5
|
+
class_option :model, :type => :boolean, :default => true, :desc => "copy model templatefile."
|
6
|
+
|
7
|
+
def copy_files
|
8
|
+
puts Dir['.']
|
9
|
+
copy_file 'lib/generators/install/templates/initializer.rb', 'config/initializers/shop_bunny.rb'
|
10
|
+
copy_file 'lib/generators/install/templates/shopbunny_model_template.rb', 'app/models/cart.rb' if options.model?
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_migrations
|
14
|
+
Dir["#{self.class.source_root}/migrations/*.rb"].sort.each do |filepath|
|
15
|
+
name = File.basename(filepath)
|
16
|
+
migration_template "migrations/#{name}", "db/migrate/#{name.gsub(/^\d+_/,'')}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# TODO Write a generator test
|
File without changes
|
File without changes
|
@@ -18,33 +18,25 @@ module ShopBunny
|
|
18
18
|
end
|
19
19
|
@cart.add_item(@item, :quantity => quantity) if @item
|
20
20
|
respond_with @cart do |format|
|
21
|
-
format.html { redirect_to
|
21
|
+
format.html { redirect_to cart_path }
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
def remove_item
|
26
26
|
@cart.remove_item(@item) if @item
|
27
27
|
respond_with @cart do |format|
|
28
|
-
format.html { redirect_to
|
28
|
+
format.html { redirect_to cart_path }
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
32
|
-
def checkout
|
33
|
-
end
|
34
31
|
|
35
32
|
def update
|
36
33
|
@cart.update_attributes(params[:cart])
|
37
|
-
@cart.update_automatic_coupons!
|
38
34
|
respond_with @cart do |format|
|
39
|
-
format.html {
|
40
|
-
if @cart.errors.
|
41
|
-
|
35
|
+
format.html {
|
36
|
+
if @cart.errors.empty?
|
37
|
+
redirect_to cart_path
|
42
38
|
else
|
43
|
-
|
44
|
-
# url_for(@cart) returns "/cart.x" when cart is a singular resource
|
45
|
-
# (see routes.rb). This is a known limitation of Rails.
|
46
|
-
# See https://rails.lighthouseapp.com/projects/8994/tickets/4168
|
47
|
-
redirect_to :action => :show
|
39
|
+
render :show
|
48
40
|
end
|
49
41
|
}
|
50
42
|
end
|
@@ -3,9 +3,9 @@ module ShopBunny
|
|
3
3
|
|
4
4
|
def self.included(clazz)
|
5
5
|
clazz.send(:attr_accessor, :coupon_code)
|
6
|
-
clazz.send(:has_many, :cart_items, :dependent => :destroy)
|
7
|
-
clazz.send(:has_many, :coupon_uses, :dependent => :destroy)
|
8
|
-
clazz.send(:has_many, :coupons, :through => :coupon_uses)
|
6
|
+
clazz.send(:has_many, :cart_items, :dependent => :destroy, :class_name => 'ShopBunny::CartItem')
|
7
|
+
clazz.send(:has_many, :coupon_uses, :dependent => :destroy, :class_name => 'ShopBunny::CouponUse')
|
8
|
+
clazz.send(:has_many, :coupons, :through => :coupon_uses, :uniq => true)
|
9
9
|
clazz.send(:accepts_nested_attributes_for, :cart_items, :allow_destroy => true )
|
10
10
|
clazz.send(:before_save, :update_coupons)
|
11
11
|
clazz.send(:attr_accessible, :coupon_code, :cart_items_attributes)
|
@@ -53,28 +53,21 @@ module ShopBunny
|
|
53
53
|
|
54
54
|
# Calculates the total sum and applies the coupons discount!
|
55
55
|
def total
|
56
|
-
|
57
56
|
[0, items_with_coupons + shipping_costs].max
|
58
57
|
end
|
59
58
|
|
60
|
-
#
|
59
|
+
# Adds one or options[:quantity] number of items to the cart or increases it's quantity.
|
61
60
|
def add_item(item,options = {})
|
62
|
-
|
63
|
-
cart_item = self.cart_items.select {|e| e.item.id == item.id}.first
|
61
|
+
cart_item = find_cart_item(item)
|
64
62
|
cart_item ||= self.cart_items.build(:item => item)
|
65
|
-
cart_item.quantity += options[:quantity]
|
63
|
+
cart_item.quantity += options[:quantity] || 1
|
66
64
|
cart_item.save!
|
67
|
-
|
68
|
-
update_automatic_coupons!
|
69
|
-
|
70
|
-
self.save!
|
71
65
|
self.reload
|
72
|
-
cart_item
|
73
66
|
end
|
74
67
|
|
75
|
-
#
|
68
|
+
# Decreases the quantity of an item in the cart by 1 or options[:quantity]
|
76
69
|
def remove_item(item,options = {})
|
77
|
-
cart_item =
|
70
|
+
cart_item = find_cart_item(item)
|
78
71
|
if cart_item
|
79
72
|
options[:quantity] ||= cart_item.quantity
|
80
73
|
if cart_item
|
@@ -83,24 +76,16 @@ module ShopBunny
|
|
83
76
|
self.reload
|
84
77
|
end
|
85
78
|
end
|
86
|
-
|
87
|
-
update_automatic_coupons!
|
88
|
-
|
89
|
-
cart_item
|
90
79
|
end
|
91
80
|
|
92
|
-
#
|
81
|
+
# Sets the quantity of the item to options[:quantity]
|
93
82
|
def update_item(item,options)
|
94
|
-
cart_item =
|
83
|
+
cart_item = find_cart_item(item)
|
95
84
|
if cart_item
|
96
85
|
cart_item.quantity = options[:quantity]
|
97
86
|
cart_item.save!
|
98
87
|
self.reload
|
99
88
|
end
|
100
|
-
|
101
|
-
update_automatic_coupons!
|
102
|
-
|
103
|
-
cart_item
|
104
89
|
end
|
105
90
|
|
106
91
|
def shipping_cost_calculator
|
@@ -117,17 +102,12 @@ module ShopBunny
|
|
117
102
|
cart_items.empty? && bonus_items.empty?
|
118
103
|
end
|
119
104
|
|
120
|
-
# Remove all items from the cart
|
105
|
+
# Remove all items and coupons from the cart
|
121
106
|
def clear!
|
122
|
-
cart_items.each {|i| i.destroy}
|
123
107
|
cart_items.clear
|
124
|
-
|
125
|
-
coupon_uses.each {|u| u.destroy}
|
126
|
-
coupon_uses.clear
|
127
108
|
coupons.clear
|
128
109
|
end
|
129
110
|
|
130
|
-
# Make
|
131
111
|
def as_json(options={})
|
132
112
|
{
|
133
113
|
:cart => attributes.
|
@@ -144,23 +124,21 @@ module ShopBunny
|
|
144
124
|
end
|
145
125
|
|
146
126
|
def update_automatic_coupons!
|
147
|
-
coupon_uses.
|
148
|
-
use.destroy if use.coupon.value_of_automatic_add
|
149
|
-
end
|
150
|
-
|
151
|
-
save
|
152
|
-
reload
|
153
|
-
|
127
|
+
coupon_uses.joins(:coupon).where('coupons.value_of_automatic_add IS NOT NULL').destroy_all
|
154
128
|
Coupon.valid.automatically_added_over(item_sum).each do |coupon|
|
155
129
|
coupons << coupon
|
156
130
|
end
|
157
131
|
end
|
158
132
|
|
159
133
|
protected
|
134
|
+
def find_cart_item(item)
|
135
|
+
self.cart_items.detect {|e| e.item.id == item.id}
|
136
|
+
end
|
137
|
+
|
160
138
|
def update_coupons
|
161
139
|
Array(@coupon_code).each { |code|
|
162
140
|
coupon = Coupon.find_by_code(code)
|
163
|
-
coupons << coupon if coupon &&
|
141
|
+
coupons << coupon if coupon && coupon.redeemable?
|
164
142
|
}
|
165
143
|
end
|
166
144
|
|
@@ -168,11 +146,11 @@ module ShopBunny
|
|
168
146
|
Array(@coupon_code).each { |code|
|
169
147
|
coupon = Coupon.find_by_code(code)
|
170
148
|
if coupon.nil?
|
171
|
-
errors.add(:coupon_code,
|
149
|
+
errors.add(:coupon_code, :unknown)
|
172
150
|
elsif coupon.expired?
|
173
|
-
errors.add(:coupon_code,
|
174
|
-
elsif coupon.
|
175
|
-
errors.add(:coupon_code,
|
151
|
+
errors.add(:coupon_code, :expired)
|
152
|
+
elsif coupon.used_up? && !coupons.include?(coupon)
|
153
|
+
errors.add(:coupon_code, :used_up)
|
176
154
|
end
|
177
155
|
}
|
178
156
|
end
|
data/{config/initializers/add_cart_finder_to_controllers.rb → lib/shop_bunny/controller_helpers.rb}
RENAMED
@@ -1,6 +1,6 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
1
|
+
module ShopBunny
|
2
|
+
# These methods are included in ApplicationController
|
3
|
+
module ControllerHelpers
|
4
4
|
# The default behaviour is to map a Cart to a session variable 'cart_id'.
|
5
5
|
# You might want to overwrite this method to authorize a user.
|
6
6
|
# TODO This could result in a mass of empty carts. A Problem?
|
@@ -18,9 +18,9 @@ module ActionController
|
|
18
18
|
session[:cart_id] = @cart.id
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def clear_cart
|
23
23
|
session[:cart_id] = nil
|
24
24
|
end
|
25
25
|
end
|
26
|
-
end
|
26
|
+
end
|
data/lib/shop_bunny/engine.rb
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
-
require 'rails'
|
2
|
-
|
3
1
|
module ShopBunny
|
4
2
|
class Engine < Rails::Engine
|
3
|
+
isolate_namespace ShopBunny
|
5
4
|
|
6
5
|
# Config defaults
|
7
6
|
config.mount_at = '/'
|
8
|
-
|
7
|
+
|
9
8
|
# Check the gem config
|
10
|
-
initializer "
|
9
|
+
initializer "shop_bunny.check_mount_point" do |app|
|
11
10
|
# make sure mount_at ends with trailing slash
|
12
11
|
config.mount_at += '/' unless config.mount_at.last == '/'
|
13
|
-
end
|
12
|
+
end
|
13
|
+
|
14
|
+
initializer "shop_bunny.add_controller_helpers" do |app|
|
15
|
+
ActiveSupport.on_load(:action_controller) do
|
16
|
+
include ShopBunny::ControllerHelpers
|
17
|
+
end
|
18
|
+
end
|
14
19
|
end
|
15
20
|
end
|
data/lib/shop_bunny.rb
CHANGED
@@ -4,6 +4,7 @@ module ShopBunny
|
|
4
4
|
require 'shop_bunny/shipping_cost_calculator'
|
5
5
|
require 'shop_bunny/cart_module'
|
6
6
|
require 'shop_bunny/cart_controller_module'
|
7
|
+
require 'shop_bunny/controller_helpers'
|
7
8
|
|
8
9
|
mattr_accessor :item_model_class_name
|
9
10
|
@@item_model_class_name = 'Item'
|
@@ -13,7 +14,11 @@ module ShopBunny
|
|
13
14
|
|
14
15
|
mattr_accessor :cart_item_enhancements
|
15
16
|
@@cart_item_enhancements ||= []
|
16
|
-
|
17
|
+
|
18
|
+
def self.table_name_prefix
|
19
|
+
''
|
20
|
+
end
|
21
|
+
|
17
22
|
# This is the default way to setup ShopBunny and is used in the
|
18
23
|
# initializer which gets generated by `rails generate shop_bunny:install`
|
19
24
|
def self.setup
|