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.
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