spree_favorite_products 1.0.0

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 (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: []