From 7fe1aab9038eeb75433533d75586c5b16806936f Mon Sep 17 00:00:00 2001 From: Andrew Spinks Date: Thu, 8 Aug 2013 11:17:26 +1000 Subject: [PATCH 1/3] Simplest version of a cart that supports multiple orders. --- app/models/cart.rb | 12 ++++++++++ app/models/spree/order_decorator.rb | 1 + db/migrate/20130807230834_add_cart.rb | 9 ++++++++ db/schema.rb | 7 +++++- spec/models/cart_spec.rb | 32 +++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 app/models/cart.rb create mode 100644 db/migrate/20130807230834_add_cart.rb create mode 100644 spec/models/cart_spec.rb diff --git a/app/models/cart.rb b/app/models/cart.rb new file mode 100644 index 0000000000..32a0ad33da --- /dev/null +++ b/app/models/cart.rb @@ -0,0 +1,12 @@ +class Cart < ActiveRecord::Base + has_many :orders, :class_name => 'Spree::Order' + belongs_to :user, :class_name => Spree.user_class + + def add_variant variant, quantity + if orders.empty? + order = Spree::Order.create + order.add_variant(variant, quantity) + orders << order + end + end +end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 458b3eb7df..a5f5f87c0d 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -3,6 +3,7 @@ require 'open_food_web/distribution_change_validator' Spree::Order.class_eval do belongs_to :order_cycle belongs_to :distributor, :class_name => 'Enterprise' + belongs_to :cart before_validation :shipping_address_from_distributor validate :products_available_from_new_distribution, :if => lambda { distributor_id_changed? || order_cycle_id_changed? } diff --git a/db/migrate/20130807230834_add_cart.rb b/db/migrate/20130807230834_add_cart.rb new file mode 100644 index 0000000000..38d94a7c8e --- /dev/null +++ b/db/migrate/20130807230834_add_cart.rb @@ -0,0 +1,9 @@ +class AddCart < ActiveRecord::Migration + def change + create_table :carts do |t| + t.integer :user_id + end + + add_column :spree_orders, :cart_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index c1b850281c..ac8c7f6946 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,11 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130807002915) do +ActiveRecord::Schema.define(:version => 20130807230834) do + + create_table "carts", :force => true do |t| + t.integer "user_id" + end create_table "cms_blocks", :force => true do |t| t.integer "page_id", :null => false @@ -447,6 +451,7 @@ ActiveRecord::Schema.define(:version => 20130807002915) do t.string "currency" t.string "last_ip_address" t.integer "order_cycle_id" + t.integer "cart_id" end add_index "spree_orders", ["number"], :name => "index_orders_on_number" diff --git a/spec/models/cart_spec.rb b/spec/models/cart_spec.rb new file mode 100644 index 0000000000..1c8997f15b --- /dev/null +++ b/spec/models/cart_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe Cart do + + describe "associations" do + it { should have_many(:orders) } + end + + describe 'adding a product' do + + let(:product) { create(:product) } + + it 'when there are no orders in the cart, create one when a product is added' do + subject.add_variant product.master, 3 + + subject.orders.size.should == 1 + subject.orders.first.line_items.first.product.should == product + end + + it 'should create an order when a product from a new distributor is added' + + it 'should create an order when a product from a new order cycle is added' + + it 'should create line items in an order for added product, when in the same distributor' + + it 'should create line items in an order for added product, when in the same distributor and order cycle' + + it 'should not create line items in an order, if the product is in a different order cycle to the order' + + it 'should not create line items in an order, if the product is in a different distributor to the order' + end +end From ac37dff94643733c1f71218d5f819e531114b3d5 Mon Sep 17 00:00:00 2001 From: Andrew Spinks Date: Thu, 8 Aug 2013 14:43:16 +1000 Subject: [PATCH 2/3] Add controller for cart and beginnings of an API. --- app/controllers/cart_controller.rb | 21 +++++++++++ app/views/cart/show.v1.rabl | 6 +++ app/views/orders/index.v1.rabl | 3 ++ app/views/orders/show.v1.rabl | 4 ++ spec/controllers/cart_controller_spec.rb | 47 ++++++++++++++++++++++++ 5 files changed, 81 insertions(+) create mode 100644 app/controllers/cart_controller.rb create mode 100644 app/views/cart/show.v1.rabl create mode 100644 app/views/orders/index.v1.rabl create mode 100644 app/views/orders/show.v1.rabl create mode 100644 spec/controllers/cart_controller_spec.rb diff --git a/app/controllers/cart_controller.rb b/app/controllers/cart_controller.rb new file mode 100644 index 0000000000..b76e5afe0a --- /dev/null +++ b/app/controllers/cart_controller.rb @@ -0,0 +1,21 @@ +class CartController < Spree::Api::BaseController + respond_to :json + + def new + @cart = Cart.new(current_api_user) + if @cart.save + respond_with(@cart, :status => 201) + else + invalid_resource!(@cart) + end + end + + def show + @cart = Cart.find(params[:id]) + respond_with(@cart) + end + + def add_product + end + +end diff --git a/app/views/cart/show.v1.rabl b/app/views/cart/show.v1.rabl new file mode 100644 index 0000000000..5ab0a564ea --- /dev/null +++ b/app/views/cart/show.v1.rabl @@ -0,0 +1,6 @@ +object @cart +attributes :id + +node( :orders ) do |p| + partial '/orders/index', object: p.orders +end diff --git a/app/views/orders/index.v1.rabl b/app/views/orders/index.v1.rabl new file mode 100644 index 0000000000..2e64d188bc --- /dev/null +++ b/app/views/orders/index.v1.rabl @@ -0,0 +1,3 @@ +collection @orders + +extends "orders/show" \ No newline at end of file diff --git a/app/views/orders/show.v1.rabl b/app/views/orders/show.v1.rabl new file mode 100644 index 0000000000..d3812cb9b8 --- /dev/null +++ b/app/views/orders/show.v1.rabl @@ -0,0 +1,4 @@ +object @order +attributes :id + +node( :distributor ) { |p| p.distributor.blank? ? "" : p.distributor.name } diff --git a/spec/controllers/cart_controller_spec.rb b/spec/controllers/cart_controller_spec.rb new file mode 100644 index 0000000000..0b354f593c --- /dev/null +++ b/spec/controllers/cart_controller_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' +require 'spree/api/testing_support/helpers' + +describe CartController do + include Spree::Api::TestingSupport::Helpers + render_views + + let(:current_api_user) { stub_model(Spree.user_class, :email => "spree@example.com") } + let!(:product1) { FactoryGirl.create(:product) } + let!(:cart) { Cart.create(user: current_api_user) } + + before do + stub_authentication! + Spree.user_class.stub :find_by_spree_api_key => current_api_user + end + + context "as a normal user" do + + + context 'with an existing cart' do + + it "retrieves an empty cart" do + spree_get :show, {id: cart, :format => :json } + + json_response["id"].should == cart.id + json_response['orders'].size.should == 0 + end + + context 'with an order' do + + let(:order) { FactoryGirl.create(:order_with_totals_and_distributor) } + + before(:each) do + cart.orders << order + cart.save! + end + + it "retrieves a cart with a single order and line item" do + spree_get :show, {id: cart, :format => :json } + + json_response['orders'].size.should == 1 + json_response['orders'].first['distributor'].should == order.distributor.name + end + end + end + end +end From bc57364c7e17a24297d166fbc4623938ad279fa7 Mon Sep 17 00:00:00 2001 From: Andrew Spinks Date: Thu, 8 Aug 2013 18:21:36 +1000 Subject: [PATCH 3/3] Add simple angular cart to the main page. --- app/assets/javascripts/store/all.js | 2 + .../store/controllers/cart.js.coffee | 18 ++++++ .../store/factories/cart.js.coffee | 12 ++++ app/controllers/cart_controller.rb | 21 ------- .../open_food_web/cart_controller.rb | 25 ++++++++ app/overrides/add_multi_cart_to_home.rb | 9 +++ app/views/cart/show.v1.rabl | 6 -- app/views/open_food_web/cart/_show.html.haml | 8 +++ app/views/open_food_web/cart/show.v1.rabl | 6 ++ app/views/open_food_web/orders/index.v1.rabl | 3 + .../{ => open_food_web}/orders/show.v1.rabl | 0 app/views/orders/index.v1.rabl | 3 - app/views/spree/shared/_multi_cart.html.haml | 3 + config/routes.rb | 4 ++ lib/open_food_web/feature_toggle.rb | 2 +- spec/controllers/cart_controller_spec.rb | 62 +++++++++---------- 16 files changed, 122 insertions(+), 62 deletions(-) create mode 100644 app/assets/javascripts/store/controllers/cart.js.coffee create mode 100644 app/assets/javascripts/store/factories/cart.js.coffee delete mode 100644 app/controllers/cart_controller.rb create mode 100644 app/controllers/open_food_web/cart_controller.rb create mode 100644 app/overrides/add_multi_cart_to_home.rb delete mode 100644 app/views/cart/show.v1.rabl create mode 100644 app/views/open_food_web/cart/_show.html.haml create mode 100644 app/views/open_food_web/cart/show.v1.rabl create mode 100644 app/views/open_food_web/orders/index.v1.rabl rename app/views/{ => open_food_web}/orders/show.v1.rabl (100%) delete mode 100644 app/views/orders/index.v1.rabl create mode 100644 app/views/spree/shared/_multi_cart.html.haml diff --git a/app/assets/javascripts/store/all.js b/app/assets/javascripts/store/all.js index 723f03af31..4da67e3567 100644 --- a/app/assets/javascripts/store/all.js +++ b/app/assets/javascripts/store/all.js @@ -9,5 +9,7 @@ //= require store/spree_core //= require store/spree_auth //= require store/spree_promo +//= require shared/angular +//= require shared/angular-resource //= require_tree . diff --git a/app/assets/javascripts/store/controllers/cart.js.coffee b/app/assets/javascripts/store/controllers/cart.js.coffee new file mode 100644 index 0000000000..240cb791f4 --- /dev/null +++ b/app/assets/javascripts/store/controllers/cart.js.coffee @@ -0,0 +1,18 @@ +'use strict' + +angular.module('store', ['ngResource']). + controller 'CartCtrl', ($scope, $window, CartFactory) -> + + $scope.loadCart = -> + $scope.cart = CartFactory.load(1) + + $scope.addVariant = (variant, quantity) -> + + .config(['$httpProvider', ($httpProvider) -> + $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content') + ]) + + + + + diff --git a/app/assets/javascripts/store/factories/cart.js.coffee b/app/assets/javascripts/store/factories/cart.js.coffee new file mode 100644 index 0000000000..d0d0d8ddd1 --- /dev/null +++ b/app/assets/javascripts/store/factories/cart.js.coffee @@ -0,0 +1,12 @@ +'use strict' + +angular.module('store'). + factory 'CartFactory', ($resource, $window, $http) -> + Cart = $resource '/open_food_web/cart/:cart_id.json', {}, + { 'show': { method: 'GET'} } + + load: (id, callback) -> + Cart.show {cart_id: id}, (cart) -> + callback(cart) + + diff --git a/app/controllers/cart_controller.rb b/app/controllers/cart_controller.rb deleted file mode 100644 index b76e5afe0a..0000000000 --- a/app/controllers/cart_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -class CartController < Spree::Api::BaseController - respond_to :json - - def new - @cart = Cart.new(current_api_user) - if @cart.save - respond_with(@cart, :status => 201) - else - invalid_resource!(@cart) - end - end - - def show - @cart = Cart.find(params[:id]) - respond_with(@cart) - end - - def add_product - end - -end diff --git a/app/controllers/open_food_web/cart_controller.rb b/app/controllers/open_food_web/cart_controller.rb new file mode 100644 index 0000000000..7b6a37ce0e --- /dev/null +++ b/app/controllers/open_food_web/cart_controller.rb @@ -0,0 +1,25 @@ +module OpenFoodWeb + class CartController < ApplicationController + respond_to :json + + # before_filter :authorize_read!, :except => [:index, :search, :create] + + def new + @cart = Cart.new(current_api_user) + if @cart.save + respond_with(@cart, :status => 201) + else + invalid_resource!(@cart) + end + end + + def show + @cart = Cart.find(params[:id]) + respond_with(@cart) + end + + def add_product + end + + end +end \ No newline at end of file diff --git a/app/overrides/add_multi_cart_to_home.rb b/app/overrides/add_multi_cart_to_home.rb new file mode 100644 index 0000000000..e11cfaa728 --- /dev/null +++ b/app/overrides/add_multi_cart_to_home.rb @@ -0,0 +1,9 @@ +Deface::Override.new(:virtual_path => "spree/products/index", + :insert_after => "[data-hook='homepage_products']", + :partial => 'spree/shared/multi_cart.html', + :name => 'multi_cart_home') + +Deface::Override.new(:virtual_path => "spree/home/index", + :insert_after => "[data-hook='homepage_products']", + :partial => 'spree/shared/multi_cart.html', + :name => 'multi_cart_products') diff --git a/app/views/cart/show.v1.rabl b/app/views/cart/show.v1.rabl deleted file mode 100644 index 5ab0a564ea..0000000000 --- a/app/views/cart/show.v1.rabl +++ /dev/null @@ -1,6 +0,0 @@ -object @cart -attributes :id - -node( :orders ) do |p| - partial '/orders/index', object: p.orders -end diff --git a/app/views/open_food_web/cart/_show.html.haml b/app/views/open_food_web/cart/_show.html.haml new file mode 100644 index 0000000000..7ca89c6633 --- /dev/null +++ b/app/views/open_food_web/cart/_show.html.haml @@ -0,0 +1,8 @@ +/ %script = Spree.api_key = raw(try_spree_current_user.try(:spree_api_key).to_s.inspect) + +Hello +%div{ 'ng-app' => 'store', 'ng-controller' => 'CartCtrl', 'ng-init' => "loadCart();" } + {{cart}} + %ul + %li(ng-repeat="order in cart.orders") + {{order.distributor}} diff --git a/app/views/open_food_web/cart/show.v1.rabl b/app/views/open_food_web/cart/show.v1.rabl new file mode 100644 index 0000000000..0e61a17162 --- /dev/null +++ b/app/views/open_food_web/cart/show.v1.rabl @@ -0,0 +1,6 @@ +object @cart +attributes :id + +node( :orders ) do |p| + partial '/open_food_web/orders/index', object: p.orders +end diff --git a/app/views/open_food_web/orders/index.v1.rabl b/app/views/open_food_web/orders/index.v1.rabl new file mode 100644 index 0000000000..b2f25229df --- /dev/null +++ b/app/views/open_food_web/orders/index.v1.rabl @@ -0,0 +1,3 @@ +collection @orders + +extends "open_food_web/orders/show" \ No newline at end of file diff --git a/app/views/orders/show.v1.rabl b/app/views/open_food_web/orders/show.v1.rabl similarity index 100% rename from app/views/orders/show.v1.rabl rename to app/views/open_food_web/orders/show.v1.rabl diff --git a/app/views/orders/index.v1.rabl b/app/views/orders/index.v1.rabl deleted file mode 100644 index 2e64d188bc..0000000000 --- a/app/views/orders/index.v1.rabl +++ /dev/null @@ -1,3 +0,0 @@ -collection @orders - -extends "orders/show" \ No newline at end of file diff --git a/app/views/spree/shared/_multi_cart.html.haml b/app/views/spree/shared/_multi_cart.html.haml new file mode 100644 index 0000000000..346ec729f0 --- /dev/null +++ b/app/views/spree/shared/_multi_cart.html.haml @@ -0,0 +1,3 @@ + +- if OpenFoodWeb::FeatureToggle.enabled? :multi_cart + = render :partial => 'open_food_web/cart/show' \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index e5363e7dd8..33b2f77639 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -23,6 +23,10 @@ Openfoodweb::Application.routes.draw do get "new_landing_page", :controller => 'home', :action => "new_landing_page" + namespace :open_food_web do + resources :cart + end + # Mount Spree's routes mount Spree::Core::Engine, :at => '/' end diff --git a/lib/open_food_web/feature_toggle.rb b/lib/open_food_web/feature_toggle.rb index 2be0c1542f..ecb5fd1e1d 100644 --- a/lib/open_food_web/feature_toggle.rb +++ b/lib/open_food_web/feature_toggle.rb @@ -4,13 +4,13 @@ module OpenFoodWeb features[feature] end - private def self.features {eaterprises: true, local_organics: false, order_cycles: false, + multi_cart: false, enterprises_distributor_info_rich_text: false} end end diff --git a/spec/controllers/cart_controller_spec.rb b/spec/controllers/cart_controller_spec.rb index 0b354f593c..2657c37509 100644 --- a/spec/controllers/cart_controller_spec.rb +++ b/spec/controllers/cart_controller_spec.rb @@ -1,47 +1,47 @@ require 'spec_helper' require 'spree/api/testing_support/helpers' -describe CartController do - include Spree::Api::TestingSupport::Helpers - render_views +module OpenFoodWeb + describe CartController do + render_views - let(:current_api_user) { stub_model(Spree.user_class, :email => "spree@example.com") } - let!(:product1) { FactoryGirl.create(:product) } - let!(:cart) { Cart.create(user: current_api_user) } + let(:user) { FactoryGirl.create(:user) } + let(:product1) { FactoryGirl.create(:product) } + let(:cart) { Cart.create(user: user) } - before do - stub_authentication! - Spree.user_class.stub :find_by_spree_api_key => current_api_user - end + before do + end - context "as a normal user" do + context "as a normal user" do + context 'with an existing cart' do - context 'with an existing cart' do + it "retrieves an empty cart" do + get :show, {id: cart, :format => :json } + json_response = JSON.parse(response.body) - it "retrieves an empty cart" do - spree_get :show, {id: cart, :format => :json } - - json_response["id"].should == cart.id - json_response['orders'].size.should == 0 - end - - context 'with an order' do - - let(:order) { FactoryGirl.create(:order_with_totals_and_distributor) } - - before(:each) do - cart.orders << order - cart.save! + json_response['id'].should == cart.id + json_response['orders'].size.should == 0 end - it "retrieves a cart with a single order and line item" do - spree_get :show, {id: cart, :format => :json } + context 'with an order' do - json_response['orders'].size.should == 1 - json_response['orders'].first['distributor'].should == order.distributor.name + let(:order) { FactoryGirl.create(:order_with_totals_and_distributor) } + + before(:each) do + cart.orders << order + cart.save! + end + + it "retrieves a cart with a single order and line item" do + get :show, {id: cart, :format => :json } + json_response = JSON.parse(response.body) + + json_response['orders'].size.should == 1 + json_response['orders'].first['distributor'].should == order.distributor.name + end end end end end -end +end \ No newline at end of file