shop_bunny 0.8.5 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|