spree_favorite_products 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/Gemfile +6 -0
  2. data/LICENSE +26 -0
  3. data/README.md +43 -0
  4. data/Rakefile +15 -0
  5. data/Versionfile +11 -0
  6. data/app/.DS_Store +0 -0
  7. data/app/assets/javascripts/admin/spree_favorite_products.js +1 -0
  8. data/app/assets/javascripts/store/spree_favorite_products.js +1 -0
  9. data/app/assets/stylesheets/admin/spree_favorite_products.css +3 -0
  10. data/app/assets/stylesheets/store/spree_favorite_products.css +3 -0
  11. data/app/controllers/spree/admin/favorite_products_controller.rb +15 -0
  12. data/app/controllers/spree/favorite_products_controller.rb +31 -0
  13. data/app/models/spree/favorite.rb +10 -0
  14. data/app/models/spree/product_decorator.rb +8 -0
  15. data/app/models/spree/user_decorator.rb +4 -0
  16. data/app/overrides/.DS_Store +0 -0
  17. data/app/overrides/add_favorite_products_tab.rb +6 -0
  18. data/app/overrides/add_link_to_mark_product_as_favorite.rb +6 -0
  19. data/app/overrides/add_link_to_users_favorite_products.rb +8 -0
  20. data/app/views/spree/.DS_Store +0 -0
  21. data/app/views/spree/admin/favorite_products/index.html.erb +46 -0
  22. data/app/views/spree/admin/favorite_products/users.html.erb +32 -0
  23. data/app/views/spree/favorite_products/create.js.erb +1 -0
  24. data/app/views/spree/favorite_products/destroy.js.erb +6 -0
  25. data/app/views/spree/favorite_products/index.html.erb +25 -0
  26. data/config/locales/en.yml +5 -0
  27. data/config/routes.rb +10 -0
  28. data/db/migrate/20130705080641_create_table_favorites.rb +9 -0
  29. data/db/migrate/20130710105100_rename_favorites_to_spree_favorites.rb +7 -0
  30. data/lib/generators/spree_favorite_products/install/install_generator.rb +31 -0
  31. data/lib/spree_favorite_products.rb +2 -0
  32. data/lib/spree_favorite_products/engine.rb +22 -0
  33. data/lib/spree_favorite_products/factories.rb +6 -0
  34. data/script/rails +7 -0
  35. data/spec/controllers/spree/admin/favorite_products_controller_spec.rb +70 -0
  36. data/spec/controllers/spree/favorite_products_controller_spec.rb +181 -0
  37. data/spec/models/spree/favorite_spec.rb +31 -0
  38. data/spec/models/spree/product_decorator_spec.rb +24 -0
  39. data/spec/models/spree/user_decorator_spec.rb +6 -0
  40. data/spec/spec_helper.rb +81 -0
  41. data/spree_favorite_products.gemspec +21 -0
  42. metadata +105 -0
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Provides basic authentication functionality for testing parts of your engine
4
+ gem 'spree_auth_devise', github: 'spree/spree_auth_devise'
5
+
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2013 [name of plugin creator]
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name Spree nor the names of its contributors may be used to
13
+ endorse or promote products derived from this software without specific
14
+ prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,43 @@
1
+ SpreeFavoriteProducts
2
+ =====================
3
+
4
+ This extension adds the following features:
5
+ 1. Adds a link 'Mark as favorite' on product detail page.
6
+ 2. Favorite Products tab on header
7
+ 3. Favorite Products tab in admin section
8
+
9
+ Installation
10
+ ------------
11
+
12
+ Add spree_favorite_products to your Gemfile:
13
+
14
+ ```ruby
15
+ gem 'spree_favorite_products'
16
+ ```
17
+
18
+ Bundle your dependencies and run the installation generator:
19
+
20
+ ```shell
21
+ bundle
22
+ bundle exec rails g spree_favorite_products:install
23
+ ```
24
+
25
+ Testing
26
+ -------
27
+
28
+ Be sure to bundle your dependencies and then create a dummy test app for the specs to run against.
29
+
30
+ ```shell
31
+ bundle
32
+ bundle exec rake test_app
33
+ bundle exec rspec spec
34
+ ```
35
+
36
+ When testing your applications integration with this extension you may use it's factories.
37
+ Simply add this require statement to your spec_helper:
38
+
39
+ ```ruby
40
+ require 'spree_favorite_products/factories'
41
+ ```
42
+
43
+ Copyright (c) 2013 [name of extension creator], released under the New BSD License
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'spree/testing_support/extension_rake'
6
+
7
+ RSpec::Core::RakeTask.new
8
+
9
+ task :default => [:spec]
10
+
11
+ desc 'Generates a dummy app for testing'
12
+ task :test_app do
13
+ ENV['LIB_NAME'] = 'spree_favorite_products'
14
+ Rake::Task['extension:test_app'].invoke
15
+ end
@@ -0,0 +1,11 @@
1
+ # This file is used to designate compatibilty with different versions of Spree
2
+ # Please see http://spreecommerce.com/documentation/extensions.html#versionfile for details
3
+
4
+ # Examples
5
+ #
6
+ # '1.2.x' => { :branch => 'master' }
7
+ # '1.1.x' => { :branch => '1-1-stable' }
8
+ # '1.0.x' => { :branch => '1-0-stable' }
9
+ # '0.70.x' => { :branch => '0-70-stable' }
10
+ # '0.40.x' => { :tag => 'v1.0.0', :version => '1.0.0' }
11
+
Binary file
@@ -0,0 +1 @@
1
+ //= require admin/spree_backend
@@ -0,0 +1 @@
1
+ //= require store/spree_frontend
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require admin/spree_backend
3
+ */
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require store/spree_frontend
3
+ */
@@ -0,0 +1,15 @@
1
+ module Spree
2
+ module Admin
3
+ class FavoriteProductsController < Spree::Admin::BaseController
4
+
5
+ def index
6
+ @favorite_products = Spree::Product.favorite.page(params[:page])
7
+ end
8
+
9
+ def users
10
+ @product = Spree::Product.where(:id => params[:id]).first
11
+ @users = @product.favorite_users.page(params[:page])
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ module Spree
2
+ class FavoriteProductsController < Spree::StoreController
3
+
4
+ before_filter :authenticate_spree_user!, :only => [:index, :create, :destroy]
5
+
6
+ def index
7
+ @favorite_products = spree_current_user.favorite_products
8
+ end
9
+
10
+ def create
11
+ favorite = spree_current_user.favorites.new :product_id => params[:id]
12
+ if @success = favorite.save
13
+ @message = "Product has been successfully marked as favorite"
14
+ else
15
+ @message = favorite.errors.full_messages.to_sentence
16
+ end
17
+ respond_to do |format|
18
+ format.js
19
+ end
20
+ end
21
+
22
+ def destroy
23
+ if @product_id = Spree::Product.where(:permalink => params[:id]).first.try(:id)
24
+ @favorite_product = Spree::Favorite.where("user_id = ? and product_id = ?", spree_current_user.id, @product_id).first
25
+ @success = @favorite_product.try(:destroy)
26
+ else
27
+ @success = false
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,10 @@
1
+ module Spree
2
+ class Favorite < ActiveRecord::Base
3
+ attr_accessible :product_id
4
+ belongs_to :product
5
+ belongs_to :user
6
+ validates :user_id, :product_id, :presence => true
7
+ validates :product_id, :uniqueness => { :scope => :user_id, :message => "already marked as favorite" }
8
+ validates :product, :presence => { :message => "is invalid" }, :if => :product_id
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ Spree::Product.class_eval do
2
+ has_many :favorites
3
+ has_many :favorite_users, :through => :favorites, :class_name => 'Spree::User', :source => 'user'
4
+
5
+ def self.favorite
6
+ joins(:favorites).uniq
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ Spree::User.class_eval do
2
+ has_many :favorites
3
+ has_many :favorite_products, :through => :favorites, :class_name => 'Spree::Product', :source => 'product'
4
+ end
Binary file
@@ -0,0 +1,6 @@
1
+ Deface::Override.new(
2
+ :virtual_path => 'spree/admin/shared/_product_sub_menu',
3
+ :name => 'add_favorite_products_tab',
4
+ :insert_bottom => "ul#sub_nav",
5
+ :text => "<%= tab :favorite_products %>"
6
+ )
@@ -0,0 +1,6 @@
1
+ Deface::Override.new(
2
+ :virtual_path => 'spree/products/show',
3
+ :name => 'add_link_to_mark_product_as_favorite',
4
+ :insert_after => "div[itemprop='description']",
5
+ :text => "<%= link_to 'Mark as favorite', favorite_products_path(:id => @product.id), :method => :post, :remote => spree_user_signed_in? %>"
6
+ )
@@ -0,0 +1,8 @@
1
+ Deface::Override.new(
2
+ :virtual_path => "spree/shared/_nav_bar",
3
+ :name => "add_link_to_users_favorite_products",
4
+ :insert_before => "li#search-bar",
5
+ :text => "<% if spree_current_user %><li><%= link_to 'Favorite Products', favorite_products_path %></li><% end %>",
6
+ :sequence => { :before => "auth_shared_login_bar"}
7
+ )
8
+
Binary file
@@ -0,0 +1,46 @@
1
+ <% content_for :page_title do %>
2
+ <%= Spree.t(:listing_favorite_products) %>
3
+ <% end %>
4
+
5
+ <%= render :partial => 'spree/admin/shared/product_sub_menu' %>
6
+
7
+ <%= paginate @favorite_products %>
8
+
9
+ <% if @favorite_products.any? %>
10
+ <table class="index" id="listing_products">
11
+ <colgroup>
12
+ <col style="width: 15%;">
13
+ <col style="width: 10%;">
14
+ <col style="width: 38%;">
15
+ <col style="width: 10%;">
16
+ <col style="width: 17%;">
17
+ <col style="width: 10%;">
18
+ </colgroup>
19
+ <thead>
20
+ <tr data-hook="admin_products_index_headers">
21
+ <th><%= Spree.t(:sku) %></th>
22
+ <th colspan="2"><%= Spree.t(:name) %></th>
23
+ <th><%= Spree.t(:master_price) %></th>
24
+ <th>Favorite Users</th>
25
+ </tr>
26
+ </thead>
27
+ <tbody>
28
+ <% @favorite_products.each do |product| %>
29
+ <tr <%= "style='color: red;'" if product.deleted? %> id="<%= spree_dom_id product %>" data-hook="admin_products_index_rows" class="<%= cycle('odd', 'even') %>">
30
+ <td class="align-center"><%= product.sku rescue '' %></td>
31
+ <td class="align-center"><%= mini_image(product) %></td>
32
+ <td><%= product.try(:name) %></td>
33
+ <td class="align-center"><%= product.display_price.to_html rescue '' %></td>
34
+ <td class='align-center'><%= link_to product.favorite_users.count, users_admin_favorite_product_path(:id => product.id) %></td>
35
+ </tr>
36
+ <% end %>
37
+ </tbody>
38
+ </table>
39
+ <% else %>
40
+ <div class="no-objects-found">
41
+ <%= Spree.t(:no_results) %>
42
+ </div>
43
+ <% end %>
44
+
45
+ <%= paginate @favorite_products %>
46
+
@@ -0,0 +1,32 @@
1
+ <% content_for :page_title do %>
2
+ <%= Spree.t(:listing_favorite_products_users) %>
3
+ <% end %>
4
+
5
+ <%= render :partial => 'spree/admin/shared/product_sub_menu' %>
6
+
7
+ <%= paginate @users %>
8
+
9
+ <% if @users.any? %>
10
+ <table class="index" id="listing_favorite_products_users">
11
+ <thead>
12
+ <tr data-hook="admin_products_index_headers">
13
+ <th>Email</th>
14
+ </tr>
15
+ </thead>
16
+ <tbody>
17
+ <% @users.each do |user| %>
18
+ <tr>
19
+ <td>
20
+ <%= user.email %>
21
+ </tr>
22
+ <% end %>
23
+ </tbody>
24
+ </table>
25
+ <% else %>
26
+ <div class="no-objects-found">
27
+ <%= Spree.t(:no_results) %>
28
+ </div>
29
+ <% end %>
30
+
31
+ <%= paginate @users %>
32
+
@@ -0,0 +1 @@
1
+ alert("<%=j @message%>");
@@ -0,0 +1,6 @@
1
+ <% if @success %>
2
+ $("#favorite_product_<%= @product_id%>").remove();
3
+ alert("Successfully removed favorite product from your list")
4
+ <% else %>
5
+ alert("Could not remove product form your list")
6
+ <% end %>
@@ -0,0 +1,25 @@
1
+ <div data-hook="account_my_favorite_products" class="account-my-favorite-products commonform">
2
+ <h3><%= Spree.t(:my_favorite_products) %></h3>
3
+ <% if @favorite_products.present? %>
4
+ <table class="favorite-products order-summary">
5
+ <thead>
6
+ <tr>
7
+ <th class="favorite-product-image"><%= Spree.t(:product_image) %></th>
8
+ <th class="favorite-product-name"><%= Spree.t(:product_name) %></th>
9
+ <th class="favorite-product-action">Action</th>
10
+ </tr>
11
+ </thead>
12
+ <tbody>
13
+ <% @favorite_products.each do |product| %>
14
+ <tr class="<%= cycle('even', 'odd') %>" id="favorite_product_<%= product.id %>">
15
+ <td class="favorite-product-image"><%= link_to small_image(product), product_path(product) %></td>
16
+ <td class="favorite-product-name"><%= product.name %></td>
17
+ <td class="favorite-product-remove"><%= link_to 'Remove', favorite_product_path(product), :method => :delete, :remote => true %>
18
+ </tr>
19
+ <% end %>
20
+ </tbody>
21
+ </table>
22
+ <% else %>
23
+ <p><%= Spree.t(:you_have_no_favorite_products_yet) %></p>
24
+ <% end %>
25
+ </div>
@@ -0,0 +1,5 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: "Hello world"
@@ -0,0 +1,10 @@
1
+ Spree::Core::Engine.routes.draw do
2
+ # Add your extension routes here
3
+ namespace :admin do
4
+ resources :favorite_products, :only => :index do
5
+ get :users, :on => :member
6
+ end
7
+ end
8
+
9
+ resources :favorite_products, :only => [:index, :create, :destroy]
10
+ end
@@ -0,0 +1,9 @@
1
+ class CreateTableFavorites < ActiveRecord::Migration
2
+ def change
3
+ create_table :favorites do |t|
4
+ t.integer :user_id
5
+ t.integer :product_id
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ class RenameFavoritesToSpreeFavorites < ActiveRecord::Migration
2
+ def change
3
+ rename_table :favorites, :spree_favorites
4
+ add_index :spree_favorites, [:user_id, :product_id], :unique => true
5
+ add_index :spree_favorites, :user_id
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ module SpreeFavoriteProducts
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ class_option :auto_run_migrations, :type => :boolean, :default => false
6
+
7
+ def add_javascripts
8
+ append_file 'app/assets/javascripts/store/all.js', "//= require store/spree_favorite_products\n"
9
+ append_file 'app/assets/javascripts/admin/all.js', "//= require admin/spree_favorite_products\n"
10
+ end
11
+
12
+ def add_stylesheets
13
+ inject_into_file 'app/assets/stylesheets/store/all.css', " *= require store/spree_favorite_products\n", :before => /\*\//, :verbose => true
14
+ inject_into_file 'app/assets/stylesheets/admin/all.css', " *= require admin/spree_favorite_products\n", :before => /\*\//, :verbose => true
15
+ end
16
+
17
+ def add_migrations
18
+ run 'bundle exec rake railties:install:migrations FROM=spree_favorite_products'
19
+ end
20
+
21
+ def run_migrations
22
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
23
+ if run_migrations
24
+ run 'bundle exec rake db:migrate'
25
+ else
26
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,2 @@
1
+ require 'spree_core'
2
+ require 'spree_favorite_products/engine'
@@ -0,0 +1,22 @@
1
+ module SpreeFavoriteProducts
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'spree_favorite_products'
6
+
7
+ config.autoload_paths += %W(#{config.root}/lib)
8
+
9
+ # use rspec for tests
10
+ config.generators do |g|
11
+ g.test_framework :rspec
12
+ end
13
+
14
+ def self.activate
15
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
16
+ Rails.configuration.cache_classes ? require(c) : load(c)
17
+ end
18
+ end
19
+
20
+ config.to_prepare &method(:activate).to_proc
21
+ end
22
+ end
@@ -0,0 +1,6 @@
1
+ FactoryGirl.define do
2
+ # Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them.
3
+ #
4
+ # Example adding this to your spec_helper will load these Factories for use:
5
+ # require 'spree_favorite_products/factories'
6
+ end
@@ -0,0 +1,7 @@
1
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
2
+
3
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
4
+ ENGINE_PATH = File.expand_path('../../lib/spree_favorite_products/engine', __FILE__)
5
+
6
+ require 'rails/all'
7
+ require 'rails/engine/commands'
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Admin::FavoriteProductsController do
4
+ let(:role) { Spree::Role.create!(:name => 'user') }
5
+ let(:roles) { [role] }
6
+ let(:product) { mock_model( Spree::Product) }
7
+
8
+ before(:each) do
9
+ @user = mock_model(Spree::User, :generate_spree_api_key! => false)
10
+ @user.stub_chain(:roles, :includes).and_return([])
11
+ @user.stub(:has_spree_role?).with('admin').and_return(true)
12
+ controller.stub(:spree_user_signed_in?).and_return(true)
13
+ controller.stub(:spree_current_user).and_return(@user)
14
+ @user.stub(:roles).and_return(roles)
15
+ roles.stub(:includes).with(:permissions).and_return(roles)
16
+ controller.stub(:authorize_admin).and_return(true)
17
+ controller.stub(:authorize!).and_return(true)
18
+ end
19
+
20
+ describe "GET index" do
21
+ def send_request
22
+ get :index, :page => 1 ,:use_route => 'spree'
23
+ end
24
+
25
+ before(:each) do
26
+ @favorite_products = double('favorite_products')
27
+ @favorite_products.stub(:page).and_return(@favorite_products)
28
+ Spree::Product.stub(:favorite).and_return(@favorite_products)
29
+ end
30
+
31
+ it "returns favorite products" do
32
+ Spree::Product.should_receive(:favorite)
33
+ send_request
34
+ end
35
+
36
+ it "paginates favorite products" do
37
+ @favorite_products.should_receive(:page).with("1")
38
+ send_request
39
+ end
40
+
41
+ it "renders favorite products template" do
42
+ send_request
43
+ response.should render_template(:favorite)
44
+ end
45
+ end
46
+
47
+ describe "#users" do
48
+ before do
49
+ product.stub(:favorite_users).and_return([@user])
50
+ @products = [product]
51
+ Spree::Product.stub(:where).with(:id => product.id).and_return(@products)
52
+ end
53
+
54
+ def send_request
55
+ get :users, :use_route => 'spree', :id => product.id, :format => :js
56
+ end
57
+
58
+ it 'fetches the product' do
59
+ Spree::Product.should_receive(:where).with(:id => product.id).and_return(@products)
60
+ end
61
+
62
+ it 'fetches the users who marked the product as favorite' do
63
+ product.should_receive(:favorite_users).and_return([@user])
64
+ end
65
+
66
+ after do
67
+ send_request
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::FavoriteProductsController do
4
+
5
+ shared_examples_for "request which requires user authentication" do
6
+ it "authenticates user" do
7
+ controller.should_receive(:authenticate_spree_user!)
8
+ send_request
9
+ end
10
+ end
11
+
12
+ describe 'POST create' do
13
+ def send_request
14
+ post :create, :id => 1, :format => :js, :use_route => 'spree'
15
+ end
16
+
17
+ before(:each) do
18
+ @favorite = mock_model(Spree::Favorite, :save => true)
19
+ controller.stub(:authenticate_spree_user!).and_return(true)
20
+ Spree::Favorite.stub(:new).and_return(@favorite)
21
+ @user = mock_model(Spree::User, :favorites => Spree::Favorite, :generate_spree_api_key! => false, :last_incomplete_spree_order => nil)
22
+ controller.stub(:spree_current_user).and_return(@user)
23
+ end
24
+
25
+ it_behaves_like "request which requires user authentication"
26
+
27
+
28
+ it "creates favorite" do
29
+ Spree::Favorite.should_receive(:new).with(:product_id => 1)
30
+ send_request
31
+ end
32
+
33
+ it "saves favorite" do
34
+ @favorite.should_receive(:save)
35
+ send_request
36
+ end
37
+
38
+ context "when favorite saved successfully" do
39
+ it "renders create" do
40
+ send_request
41
+ response.should render_template(:create)
42
+ end
43
+
44
+ it "should assign success message" do
45
+ send_request
46
+ assigns(:message).should eq("Product has been successfully marked as favorite")
47
+ end
48
+ end
49
+
50
+ context "when favorite not saved sucessfully" do
51
+ before(:each) do
52
+ @favorite.stub(:save).and_return(false)
53
+ @favorite.stub_chain(:errors, :full_messages).and_return(["Product already marked as favorite"])
54
+ end
55
+
56
+ it "renders create template" do
57
+ send_request
58
+ response.should render_template(:create)
59
+ end
60
+
61
+ it "should assign error message" do
62
+ send_request
63
+ assigns(:message).should eq("Product already marked as favorite")
64
+ end
65
+ end
66
+ end
67
+
68
+ describe 'GET index' do
69
+ def send_request
70
+ get :index, :use_route => 'spree'
71
+ end
72
+
73
+ before(:each) do
74
+ @favorite_product = mock_model(Spree::Product)
75
+ @user = mock_model(Spree::User, :favorite_products => [@favorite_product], :generate_spree_api_key! => false, :last_incomplete_spree_order => nil)
76
+ controller.stub(:authenticate_spree_user!).and_return(true)
77
+ controller.stub(:spree_current_user).and_return(@user)
78
+ end
79
+
80
+ it "authenticates user" do
81
+ controller.should_receive(:authenticate_spree_user!)
82
+ send_request
83
+ end
84
+
85
+ it "finds favorite products of current user" do
86
+ @user.should_receive(:favorite_products)
87
+ send_request
88
+ end
89
+
90
+ it "assigns @favorite_products" do
91
+ send_request
92
+ assigns(:favorite_products).should eq([@favorite_product])
93
+ end
94
+ end
95
+
96
+ describe 'destroy' do
97
+ def send_request(params = {})
98
+ post :destroy, params.merge({:use_route => 'spree', :method => :delete, :format => :js})
99
+ end
100
+
101
+ before do
102
+ @favorite_product = mock_model(Spree::Favorite)
103
+ @product = mock_model(Spree::Product, :permalink => 'my_product', :id => 100)
104
+ @user = mock_model(Spree::User, :favorite_products => [@favorite_product], :generate_spree_api_key! => false, :last_incomplete_spree_order => nil)
105
+ controller.stub(:authenticate_spree_user!).and_return(true)
106
+ controller.stub(:spree_current_user).and_return(@user)
107
+ end
108
+
109
+ it_behaves_like "request which requires user authentication"
110
+
111
+ context 'when favorite product entry for the requested product exits' do
112
+ before do
113
+ Spree::Product.stub(:where).with(:permalink => @product.permalink).and_return([@product])
114
+ Spree::Favorite.stub(:where).with("user_id = ? and product_id = ?", @user.id, 100).and_return([@favorite_product])
115
+ end
116
+
117
+ it 'assigns its id to product_id' do
118
+ send_request(:id => @product.permalink)
119
+ assigns(:product_id).should eq(100)
120
+ end
121
+
122
+ it 'assigns favorite product object to favorite_product' do
123
+ send_request(:id => @product.permalink)
124
+ assigns(:favorite_product).should eq(@favorite_product)
125
+ end
126
+
127
+ it 'calls for destroy on the favorite_product' do
128
+ @favorite_product.should_receive(:destroy)
129
+ send_request(:id => @product.permalink)
130
+ end
131
+
132
+ context 'and is destroyed successfully' do
133
+ before do
134
+ @favorite_product.stub(:destroy).and_return(true)
135
+ end
136
+
137
+ it 'assigns true to success' do
138
+ send_request(:id => @product.permalink)
139
+ assigns(:success).should be_true
140
+ end
141
+ end
142
+
143
+ context 'and is not destroyed successfully' do
144
+ before do
145
+ @favorite_product.stub(:destroy).and_return(false)
146
+ end
147
+
148
+ it 'assigns false to success' do
149
+ send_request(:id => @product.permalink)
150
+ assigns(:success).should be_false
151
+ end
152
+ end
153
+ end
154
+
155
+ context 'when favorite product entry for the requested product does not exit' do
156
+ before do
157
+ Spree::Product.stub(:where).with(:permalink => @product.permalink).and_return([])
158
+ end
159
+
160
+ it 'does not assign any id to product_id' do
161
+ send_request(:id => @product.permalink)
162
+ assigns(:product_id).should be_nil
163
+ end
164
+
165
+ it 'assigns favorite product object to favorite_product' do
166
+ send_request(:id => @product.permalink)
167
+ assigns(:favorite_product).should be_nil
168
+ end
169
+
170
+ it 'calls for destroy on the favorite_product' do
171
+ send_request(:id => @product.permalink)
172
+ @favorite_product.should_not_receive(:destroy)
173
+ end
174
+
175
+ it 'assigns false to success' do
176
+ send_request(:id => @product.permalink)
177
+ assigns(:success).should be_false
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Favorite do
4
+ it { should allow_mass_assignment_of :product_id }
5
+ it { should belong_to(:product) }
6
+ it { should belong_to(:user) }
7
+ it { should validate_uniqueness_of(:product_id).scoped_to(:user_id).with_message("already marked as favorite") }
8
+ it { should validate_presence_of(:user_id) }
9
+ it { should validate_presence_of(:product_id) }
10
+
11
+ context "when product_id is present" do
12
+ before(:each) do
13
+ @favorite = Spree::Favorite.new :product_id => 'invalid product id'
14
+ end
15
+ it "checks for the presence of product" do
16
+ @favorite.valid?
17
+ @favorite.errors[:product].should eq(["is invalid"])
18
+ end
19
+ end
20
+
21
+ context "when product_id is not present" do
22
+ before(:each) do
23
+ @favorite = Spree::Favorite.new
24
+ end
25
+
26
+ it "does not validate the presence of product" do
27
+ @favorite.valid?
28
+ @favorite.errors[:product].should eq([])
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Product do
4
+ it { should have_many(:favorites) }
5
+ it { should have_many(:favorite_users).through(:favorites).class_name('Spree::User') }
6
+
7
+ describe "Spree::Product.favorite" do
8
+ before(:each) do
9
+ @favorite_product1 = Spree::Product.create! :name => 'favorite_product1', :price => 100
10
+ @favorite_product2 = Spree::Product.create! :name => 'favorite_product2', :price => 100
11
+ @product1 = Spree::Product.create! :name => 'product1', :price => 100
12
+ @product2 = Spree::Product.create! :name => 'product2', :price => 100
13
+ @user1 = Spree::User.create! :email => 'user1@example.com', :password => 'example', :password_confirmation => "example"
14
+ @user2 = Spree::User.create! :email => 'user2@example.com', :password => "example", :password_confirmation => 'example'
15
+ @user1.favorites.create! :product_id => @favorite_product1.id
16
+ @user2.favorites.create! :product_id => @favorite_product1.id
17
+ @user2.favorites.create! :product_id => @favorite_product2.id
18
+ end
19
+
20
+ it "returns favorite products" do
21
+ Spree::Product.favorite.should =~ [@favorite_product1, @favorite_product2]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::User do
4
+ it { should have_many(:favorites) }
5
+ it { should have_many(:favorite_products).through(:favorites).class_name('Spree::Product') }
6
+ end
@@ -0,0 +1,81 @@
1
+ # Run Coverage report
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_group 'Controllers', 'app/controllers'
5
+ add_group 'Helpers', 'app/helpers'
6
+ add_group 'Mailers', 'app/mailers'
7
+ add_group 'Models', 'app/models'
8
+ add_group 'Views', 'app/views'
9
+ add_group 'Libraries', 'lib'
10
+ end
11
+
12
+ # Configure Rails Environment
13
+ ENV['RAILS_ENV'] = 'test'
14
+
15
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
16
+
17
+ require 'rspec/rails'
18
+ require 'database_cleaner'
19
+ require 'ffaker'
20
+
21
+ # Requires supporting ruby files with custom matchers and macros, etc,
22
+ # in spec/support/ and its subdirectories.
23
+ Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require f }
24
+
25
+ # Requires factories defined in spree_core
26
+ require 'spree/testing_support/factories'
27
+ require 'spree/testing_support/controller_requests'
28
+ require 'spree/testing_support/authorization_helpers'
29
+ require 'spree/testing_support/url_helpers'
30
+
31
+ # Requires factories defined in lib/spree_favorite_products/factories.rb
32
+ require 'spree_favorite_products/factories'
33
+
34
+ RSpec.configure do |config|
35
+ config.include FactoryGirl::Syntax::Methods
36
+
37
+ # == URL Helpers
38
+ #
39
+ # Allows access to Spree's routes in specs:
40
+ #
41
+ # visit spree.admin_path
42
+ # current_path.should eql(spree.products_path)
43
+ config.include Spree::TestingSupport::UrlHelpers
44
+
45
+ # == Mock Framework
46
+ #
47
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
48
+ #
49
+ # config.mock_with :mocha
50
+ # config.mock_with :flexmock
51
+ # config.mock_with :rr
52
+ config.mock_with :rspec
53
+ config.color = true
54
+
55
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
56
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
57
+
58
+ # Capybara javascript drivers require transactional fixtures set to false, and we use DatabaseCleaner
59
+ # to cleanup after each test instead. Without transactional fixtures set to false the records created
60
+ # to setup a test will be unavailable to the browser, which runs under a seperate server instance.
61
+ config.use_transactional_fixtures = false
62
+
63
+ # Ensure Suite is set to use transactions for speed.
64
+ config.before :suite do
65
+ DatabaseCleaner.strategy = :transaction
66
+ DatabaseCleaner.clean_with :truncation
67
+ end
68
+
69
+ # Before each spec check if it is a Javascript test and switch between using database transactions or not where necessary.
70
+ config.before :each do
71
+ DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
72
+ DatabaseCleaner.start
73
+ end
74
+
75
+ # After each spec clean the database.
76
+ config.after :each do
77
+ DatabaseCleaner.clean
78
+ end
79
+
80
+ config.fail_fast = ENV['FAIL_FAST'] || false
81
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ Gem::Specification.new do |s|
3
+ s.platform = Gem::Platform::RUBY
4
+ s.name = 'spree_favorite_products'
5
+ s.version = '1.0.0'
6
+ s.summary = 'users can mark product as favorite'
7
+ s.description = 'This extension adds the following features: 1. Adds a link Mark as favorite on product detail page. 2. Favorite Products tab on header 3. Favorite Products tab in admin section'
8
+ s.required_ruby_version = '>= 1.9.3'
9
+
10
+ s.author = 'Mohit Bansal'
11
+ s.email = 'info@vinsol.com'
12
+ s.homepage = 'http://vinsol.com'
13
+ s.license = "MIT"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ #s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.require_path = 'lib'
18
+ s.requirements << 'none'
19
+
20
+ s.add_dependency 'spree_core', '~> 2.0.3'
21
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spree_favorite_products
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mohit Bansal
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: spree_core
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.0.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.0.3
30
+ description: ! 'This extension adds the following features: 1. Adds a link Mark as
31
+ favorite on product detail page. 2. Favorite Products tab on header 3. Favorite
32
+ Products tab in admin section'
33
+ email: info@vinsol.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - Gemfile
39
+ - LICENSE
40
+ - README.md
41
+ - Rakefile
42
+ - Versionfile
43
+ - app/.DS_Store
44
+ - app/assets/javascripts/admin/spree_favorite_products.js
45
+ - app/assets/javascripts/store/spree_favorite_products.js
46
+ - app/assets/stylesheets/admin/spree_favorite_products.css
47
+ - app/assets/stylesheets/store/spree_favorite_products.css
48
+ - app/controllers/spree/admin/favorite_products_controller.rb
49
+ - app/controllers/spree/favorite_products_controller.rb
50
+ - app/models/spree/favorite.rb
51
+ - app/models/spree/product_decorator.rb
52
+ - app/models/spree/user_decorator.rb
53
+ - app/overrides/.DS_Store
54
+ - app/overrides/add_favorite_products_tab.rb
55
+ - app/overrides/add_link_to_mark_product_as_favorite.rb
56
+ - app/overrides/add_link_to_users_favorite_products.rb
57
+ - app/views/spree/.DS_Store
58
+ - app/views/spree/admin/favorite_products/index.html.erb
59
+ - app/views/spree/admin/favorite_products/users.html.erb
60
+ - app/views/spree/favorite_products/create.js.erb
61
+ - app/views/spree/favorite_products/destroy.js.erb
62
+ - app/views/spree/favorite_products/index.html.erb
63
+ - config/locales/en.yml
64
+ - config/routes.rb
65
+ - db/migrate/20130705080641_create_table_favorites.rb
66
+ - db/migrate/20130710105100_rename_favorites_to_spree_favorites.rb
67
+ - lib/generators/spree_favorite_products/install/install_generator.rb
68
+ - lib/spree_favorite_products.rb
69
+ - lib/spree_favorite_products/engine.rb
70
+ - lib/spree_favorite_products/factories.rb
71
+ - script/rails
72
+ - spec/controllers/spree/admin/favorite_products_controller_spec.rb
73
+ - spec/controllers/spree/favorite_products_controller_spec.rb
74
+ - spec/models/spree/favorite_spec.rb
75
+ - spec/models/spree/product_decorator_spec.rb
76
+ - spec/models/spree/user_decorator_spec.rb
77
+ - spec/spec_helper.rb
78
+ - spree_favorite_products.gemspec
79
+ homepage: http://vinsol.com
80
+ licenses:
81
+ - MIT
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: 1.9.3
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements:
99
+ - none
100
+ rubyforge_project:
101
+ rubygems_version: 1.8.25
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: users can mark product as favorite
105
+ test_files: []