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.
Files changed (72) hide show
  1. data/README +18 -0
  2. data/Rakefile +33 -0
  3. data/app/controllers/shop_bunny/application_controller.rb +5 -0
  4. data/app/controllers/shop_bunny/carts_controller.rb +5 -0
  5. data/app/models/{cart_item.rb → shop_bunny/cart_item.rb} +9 -13
  6. data/app/models/{coupon.rb → shop_bunny/coupon.rb} +14 -18
  7. data/app/models/shop_bunny/coupon_use.rb +7 -0
  8. data/app/views/{carts → shop_bunny/carts}/show.html.erb +20 -25
  9. data/config/locales/shop_bunny.en.yml +10 -0
  10. data/config/routes.rb +3 -62
  11. data/db/development.sqlite3 +0 -0
  12. data/db/migrate/20110202235446_add_raw_item_to_cart_items.rb +1 -1
  13. data/db/test.sqlite3 +0 -0
  14. data/lib/generators/install/USAGE +8 -0
  15. data/lib/generators/install/install_generator.rb +23 -0
  16. data/{config/initializers/shop_bunny.rb → lib/generators/install/templates/initializer.rb} +0 -0
  17. data/lib/generators/{shop_bunny → install}/templates/shopbunny_model_template.rb +0 -0
  18. data/lib/shop_bunny/cart_controller_module.rb +6 -14
  19. data/lib/shop_bunny/cart_module.rb +21 -43
  20. data/{config/initializers/add_cart_finder_to_controllers.rb → lib/shop_bunny/controller_helpers.rb} +5 -5
  21. data/lib/shop_bunny/engine.rb +10 -5
  22. data/lib/shop_bunny/version.rb +3 -0
  23. data/lib/shop_bunny.rb +6 -1
  24. data/spec/controllers/shop_bunny/carts_controller_spec.rb +97 -0
  25. data/spec/dummy/Rakefile +7 -0
  26. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  27. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  28. data/{app → spec/dummy/app}/controllers/application_controller.rb +0 -0
  29. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  30. data/{app → spec/dummy/app}/models/cart.rb +1 -1
  31. data/{app → spec/dummy/app}/models/item.rb +1 -2
  32. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  33. data/spec/dummy/config/application.rb +50 -0
  34. data/spec/dummy/config/boot.rb +10 -0
  35. data/spec/dummy/config/database.yml +25 -0
  36. data/spec/dummy/config/environment.rb +5 -0
  37. data/spec/dummy/config/environments/development.rb +30 -0
  38. data/spec/dummy/config/environments/production.rb +60 -0
  39. data/spec/dummy/config/environments/test.rb +39 -0
  40. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  41. data/spec/dummy/config/initializers/inflections.rb +10 -0
  42. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  43. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  44. data/spec/dummy/config/initializers/session_store.rb +8 -0
  45. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  46. data/spec/dummy/config/locales/en.yml +5 -0
  47. data/spec/dummy/config/routes.rb +3 -0
  48. data/spec/dummy/config.ru +4 -0
  49. data/{db → spec/dummy/db}/migrate/20100915073016_create_items.rb +0 -0
  50. data/spec/dummy/db/schema.rb +61 -0
  51. data/spec/dummy/public/404.html +26 -0
  52. data/spec/dummy/public/422.html +26 -0
  53. data/spec/dummy/public/500.html +26 -0
  54. data/spec/dummy/public/favicon.ico +0 -0
  55. data/spec/dummy/script/rails +6 -0
  56. data/spec/models/cart_spec.rb +344 -0
  57. data/spec/models/shop_bunny/cart_item_spec.rb +56 -0
  58. data/spec/models/shop_bunny/coupon_spec.rb +177 -0
  59. data/spec/models/shop_bunny/coupon_use_spec.rb +6 -0
  60. data/spec/requests/shop_bunny/shopping_spec.rb +119 -0
  61. data/spec/shop_bunny_test.rb +7 -0
  62. data/spec/spec_helper.rb +28 -0
  63. data/spec/support/blueprints/cart_items.rb +4 -0
  64. data/spec/support/blueprints/carts.rb +2 -0
  65. data/spec/support/blueprints/coupons.rb +50 -0
  66. data/spec/support/blueprints/items.rb +4 -0
  67. metadata +189 -24
  68. data/app/controllers/carts_controller.rb +0 -4
  69. data/app/models/coupon_use.rb +0 -15
  70. data/config/initializers/machinist.rb +0 -19
  71. data/lib/generators/shop_bunny/install_generator.rb +0 -18
  72. 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
@@ -0,0 +1,5 @@
1
+ module ShopBunny
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module ShopBunny
2
+ class CartsController < ApplicationController
3
+ include ShopBunny::CartControllerModule
4
+ end
5
+ end
@@ -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
- after_save :touch_cart
13
- after_destroy :touch_cart
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
- after_save :touch_cart
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
- end
46
-
47
- protected
48
- def touch_cart
49
- carts.each {|cart| cart.touch}
45
+ save!
50
46
  end
51
47
  end
@@ -0,0 +1,7 @@
1
+ class ShopBunny::CouponUse < ActiveRecord::Base
2
+ belongs_to :cart
3
+ belongs_to :coupon, :class_name => 'ShopBunny::Coupon'
4
+
5
+ validates_presence_of :cart_id
6
+ validates_presence_of :coupon_id
7
+ 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 class="shopping-cart">
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 'Remove', remove_item_from_cart_path(:item_id => cart_item.object.item.id) %></td>
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: <%= @cart.item_sum %></td>
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: <%= @cart.shipping_costs %></td>
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: <%= @cart.total %></td>
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
- <ul>
70
- <% @cart.coupons.each do |coupon| %>
71
- <li><%= coupon.title %></ti>
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
- <p>
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 code" %>
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>
@@ -0,0 +1,10 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ models:
5
+ cart:
6
+ attributes:
7
+ coupon_code:
8
+ unknown: "The provided code is unknown or invalid"
9
+ expired: "The provided code has expired"
10
+ used_up: "The number of uses for this code has been exceeded"
data/config/routes.rb CHANGED
@@ -1,65 +1,6 @@
1
- Rails.application.routes.draw do
1
+ ShopBunny::Engine.routes.draw do
2
2
  resource :cart, :only => [:show, :update] do
3
- match ':action'
4
- get :checkout
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,8 @@
1
+ Description:
2
+ Install ShopBunny assets
3
+
4
+ Example:
5
+ rails generate shop_bunny:install
6
+
7
+ Make sure to look at config/initializers/shop_bunny.rb after you run the generator.
8
+
@@ -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
@@ -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 :action => :show }
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 :action => :show }
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.any?
41
- render 'show'
35
+ format.html {
36
+ if @cart.errors.empty?
37
+ redirect_to cart_path
42
38
  else
43
- # We have to redirect by hand here, because
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
- #increases the quantity of an article. creates a new one if it doesn't exist
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
- options[:quantity] ||= 1
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
- #removes a quantity of an article specified by :article_id, returns nil if no article has been found
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 = self.cart_items.select {|e| e.item.id == item.id}.first
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
- #sets the quantity of an article specified by :article_id, returns nil if no article has been found
81
+ # Sets the quantity of the item to options[:quantity]
93
82
  def update_item(item,options)
94
- cart_item = self.cart_items.select {|e| e.item.id == item.id}.first
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.each do |use|
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 && !coupons.include?(coupon) && (coupon.max_uses.nil? || coupon.max_uses >= coupon.coupon_uses.count)
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, "is unknown")
149
+ errors.add(:coupon_code, :unknown)
172
150
  elsif coupon.expired?
173
- errors.add(:coupon_code, "is expired")
174
- elsif coupon.max_uses && coupon.max_uses < coupon.coupon_uses.count
175
- errors.add(:coupon_code, "is overused")
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
@@ -1,6 +1,6 @@
1
- module ActionController
2
- class Base
3
- protected
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
@@ -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 "check config" do |app|
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
@@ -0,0 +1,3 @@
1
+ module ShopBunny
2
+ VERSION = "0.8.6"
3
+ 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