solidus_favorite_products 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +18 -0
  6. data/LICENSE +26 -0
  7. data/README.md +60 -0
  8. data/Rakefile +15 -0
  9. data/app/.DS_Store +0 -0
  10. data/app/assets/javascripts/spree/backend/solidus_favorite_products.js +0 -0
  11. data/app/assets/javascripts/spree/frontend/solidus_favorite_products.js +0 -0
  12. data/app/assets/stylesheets/spree/backend/solidus_favorite_products.css +0 -0
  13. data/app/assets/stylesheets/spree/frontend/solidus_favorite_products.css +0 -0
  14. data/app/controllers/spree/admin/favorite_products_controller.rb +21 -0
  15. data/app/controllers/spree/admin/products_controller_decorator.rb +6 -0
  16. data/app/controllers/spree/admin/users_controller_decorator.rb +5 -0
  17. data/app/controllers/spree/favorite_products_controller.rb +42 -0
  18. data/app/models/spree/app_configuration_decorator.rb +4 -0
  19. data/app/models/spree/favorite.rb +12 -0
  20. data/app/models/spree/product_decorator.rb +8 -0
  21. data/app/models/spree/user_decorator.rb +8 -0
  22. data/app/overrides/.DS_Store +0 -0
  23. data/app/overrides/add_favorite_products.rb +34 -0
  24. data/app/overrides/add_favorite_products_tab_ro_user_edit.rb +11 -0
  25. data/app/overrides/add_favorite_products_to_products_tab.rb +6 -0
  26. data/app/overrides/add_favorite_users_tab_in_product_edit.rb +10 -0
  27. data/app/overrides/add_link_to_mark_product_as_favorite.rb +13 -0
  28. data/app/overrides/add_link_to_users_favorite_products.rb +8 -0
  29. data/app/views/spree/.DS_Store +0 -0
  30. data/app/views/spree/admin/favorite_products/index.html.erb +45 -0
  31. data/app/views/spree/admin/favorite_products/users.html.erb +15 -0
  32. data/app/views/spree/admin/products/favorite_users.html.erb +24 -0
  33. data/app/views/spree/admin/shared/favorite_products/_users.html.erb +15 -0
  34. data/app/views/spree/admin/users/favorite_products.html.erb +38 -0
  35. data/app/views/spree/favorite_products/create.js.erb +4 -0
  36. data/app/views/spree/favorite_products/destroy.js.erb +14 -0
  37. data/app/views/spree/favorite_products/index.html.erb +30 -0
  38. data/config/locales/en.yml +25 -0
  39. data/config/routes.rb +18 -0
  40. data/db/migrate/20130705080641_create_table_favorites.rb +9 -0
  41. data/db/migrate/20130710105100_rename_favorites_to_spree_favorites.rb +7 -0
  42. data/db/migrate/20160304152800_add_favorites_user_counts_to_product.rb +6 -0
  43. data/lib/generators/solidus_favorite_products/install/install_generator.rb +31 -0
  44. data/lib/solidus_favorite_products.rb +2 -0
  45. data/lib/solidus_favorite_products/engine.rb +22 -0
  46. data/lib/solidus_favorite_products/factories.rb +6 -0
  47. data/script/rails +7 -0
  48. data/solidus_favorite_products.gemspec +26 -0
  49. data/spec/controllers/spree/admin/favorite_products_controller_spec.rb +111 -0
  50. data/spec/controllers/spree/admin/products_controller_decorator_spec.rb +28 -0
  51. data/spec/controllers/spree/admin/users_controller_decorator_spec.rb +28 -0
  52. data/spec/controllers/spree/favorite_products_controller_spec.rb +175 -0
  53. data/spec/models/spree/app_configuration_decorator_spec.rb +3 -0
  54. data/spec/models/spree/favorite_spec.rb +32 -0
  55. data/spec/models/spree/product_decorator_spec.rb +42 -0
  56. data/spec/models/spree/user_decorator_spec.rb +27 -0
  57. data/spec/spec_helper.rb +103 -0
  58. metadata +188 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1336257eb0d7516d9afde7364887ea22c1ef2df6
4
+ data.tar.gz: 0560780a0201e39767ba8150e4430cf9239c3dca
5
+ SHA512:
6
+ metadata.gz: 520aba523d389e9e38de267aef542c8632dfdca204bda8525516d05577a29016ba257d9d5cb246132fa2d573c34f6ddefb5c80482bfa696116dd1796b2c118da
7
+ data.tar.gz: f4f80f4ce7f5c594d715b47fe45b933a616303c33df7662a9952d324ba551b75b957096cd7eac1a0c9f0cb364fda9dc00943ba1640c4ebc2bc41db013d74a887
@@ -0,0 +1,6 @@
1
+ /spec/dummy
2
+ /coverage
3
+ .DS_STORE
4
+ .ruby-version
5
+ Gemfile.lock
6
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-2.1.6
4
+ script:
5
+ - bundle exec rake test_app
6
+ - bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Provides basic authentication functionality for testing parts of your engine
4
+ gem 'solidus', '>= 2.1.0'
5
+ gem 'solidus_auth_devise'
6
+
7
+ gem 'byebug', '~> 9.0.6'
8
+
9
+ group :test do
10
+ gem 'rspec-rails', '~> 3.5.2'
11
+ gem 'shoulda-matchers', '3.1.1'
12
+ gem 'simplecov', '~> 0.12.0', require: false
13
+ gem 'database_cleaner', '~> 1.5.3'
14
+ gem 'rspec-activemodel-mocks', '~> 1.0.3'
15
+ gem 'rails-controller-testing', '~> 1.0.1'
16
+ end
17
+
18
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2014 Vinsol
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,60 @@
1
+ Solidus Favorite Products
2
+ =======================
3
+ [![Code Climate](https://codeclimate.com/github/vinsol/solidus_favorite_products.svg)](https://codeclimate.com/github/vinsol/solidus_favorite_products)
4
+ * Solidus Favorite Products is an extension that allows the user to mark/unkmark a product as favorite from the product page.
5
+
6
+ * Admin can view which products have been marked as favorite, and by which users, on the Admin end.
7
+
8
+ * It gives user the ability to see all products marked as favorite by him/her.
9
+
10
+ Installation
11
+ ------------
12
+
13
+ Add solidus_favorite_products to your Gemfile:
14
+
15
+ ```ruby
16
+ gem 'solidus_favorite_products'
17
+ ```
18
+
19
+ Bundle your dependencies and run the installation generator:
20
+
21
+ ```shell
22
+ bundle
23
+ bundle exec rails g solidus_favorite_products:install
24
+ ```
25
+
26
+ Testing
27
+ -------
28
+
29
+ Be sure to bundle your dependencies and then create a dummy test app for the specs to run against.
30
+
31
+ ```shell
32
+ bundle
33
+ bundle exec rake test_app
34
+ bundle exec rspec spec
35
+ ```
36
+
37
+ When testing your applications integration with this extension you may use it's factories.
38
+ Simply add this require statement to your spec_helper:
39
+
40
+ ```ruby
41
+ require 'solidus_favorite_products/factories'
42
+ ```
43
+
44
+ Contributing
45
+ ------------
46
+
47
+ 1. Fork the repo.
48
+ 2. Clone your repo.
49
+ 3. Run `bundle install`.
50
+ 4. Run `bundle exec rake test_app` to create the test application in `spec/test_app`.
51
+ 5. Make your changes.
52
+ 6. Ensure specs pass by running `bundle exec rspec spec`.
53
+ 7. Submit your pull request.
54
+
55
+ Credits
56
+ -------
57
+
58
+ [![vinsol.com: Ruby on Rails, iOS and Android developers](http://vinsol.com/vin_logo.png "Ruby on Rails, iOS and Android developers")](http://vinsol.com)
59
+
60
+ Copyright (c) 2017 [vinsol.com](http://vinsol.com "Ruby on Rails, iOS and Android developers"), released under the New MIT 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'] = 'solidus_favorite_products'
14
+ Rake::Task['extension:test_app'].invoke
15
+ end
Binary file
@@ -0,0 +1,21 @@
1
+ module Spree
2
+ module Admin
3
+ class FavoriteProductsController < Spree::Admin::BaseController
4
+
5
+ def index
6
+ @search = Spree::Product.favorite.includes(master: :images).search(params[:q])
7
+ @favorite_products = @search.result.order_by_favorite_users_count(sort_in_ascending_users_count?).page(params[:page])
8
+ end
9
+
10
+ private
11
+ def sort_in_ascending_users_count?
12
+ params[:q] && params[:q][:s] == 'favorite_users_count asc'
13
+ end
14
+
15
+ def model_class
16
+ Spree::Product
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ Spree::Admin::ProductsController.class_eval do
2
+
3
+ def favorite_users
4
+ @users = @product.favorite_users.page(params[:page]).per(Spree::Config.favorite_users_per_page)
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ Spree::Admin::UsersController.class_eval do
2
+ def favorite_products
3
+ @favorite_products = @user.favorite_products.page(params[:page]).per(Spree::Config.favorite_products_per_page)
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ module Spree
2
+ class FavoriteProductsController < Spree::StoreController
3
+
4
+ prepend_before_action :store_favorite_product_preference, only: :create
5
+ before_action :authenticate_spree_user!
6
+ before_action :find_favorite_product, only: :destroy
7
+
8
+ def index
9
+ @favorite_products = spree_current_user.favorite_products.page(params[:page]).per(Spree::Config.favorite_products_per_page)
10
+ end
11
+
12
+ def create
13
+ favorite = spree_current_user.favorites.new(product_id: params[:id])
14
+ if @success = favorite.save
15
+ @message = Spree.t(:success, scope: [:favorite_products, :create])
16
+ else
17
+ @message = favorite.errors.full_messages.to_sentence
18
+ end
19
+ respond_to do |format|
20
+ format.js
21
+ end
22
+ end
23
+
24
+ def destroy
25
+ if @favorite
26
+ @success = @favorite.destroy
27
+ end
28
+ end
29
+
30
+ private
31
+ def find_favorite_product
32
+ @favorite = spree_current_user.favorites.with_product_id(params[:id]).first
33
+ end
34
+
35
+ def store_favorite_product_preference
36
+ unless spree_current_user
37
+ session[:spree_user_return_to] = product_path(id: params[:id], favorite_product_id: params[:id])
38
+ redirect_to login_path, notice: Spree.t(:login_to_add_favorite)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ Spree::AppConfiguration.class_eval do
2
+ preference :favorite_products_per_page, :integer, default: 20
3
+ preference :favorite_users_per_page, :integer, default: 20
4
+ end
@@ -0,0 +1,12 @@
1
+ module Spree
2
+ class Favorite < ActiveRecord::Base
3
+ with_options required: true do
4
+ belongs_to :product, counter_cache: :favorite_users_count
5
+ belongs_to :user
6
+ end
7
+
8
+ validates :product_id, uniqueness: { scope: :user_id, message: Spree.t(:duplicate_favorite), allow_blank: true }
9
+
10
+ scope :with_product_id, ->(id) { joins(:product).readonly(false).merge(Spree::Product.where(id: id)) }
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ Spree::Product.class_eval do
2
+ has_many :favorites, dependent: :destroy
3
+ has_many :favorite_users, through: :favorites, class_name: 'Spree::User', source: :user
4
+
5
+ scope :favorite, -> { joins(:favorites).distinct }
6
+
7
+ scope :order_by_favorite_users_count, ->(asc = false) { order(favorite_users_count: "#{asc ? 'asc' : 'desc'}") }
8
+ end
@@ -0,0 +1,8 @@
1
+ Spree::User.class_eval do
2
+ has_many :favorites, dependent: :destroy
3
+ has_many :favorite_products, through: :favorites, class_name: 'Spree::Product', source: :product
4
+
5
+ def has_favorite_product?(product_id)
6
+ favorites.exists? product_id: product_id
7
+ end
8
+ end
Binary file
@@ -0,0 +1,34 @@
1
+ Deface::Override.new(
2
+ virtual_path: 'spree/products/show',
3
+ name: 'add_favorite_products_after_login',
4
+ insert_after: '[data-hook="product_show"]',
5
+ text: %Q{
6
+ <%= javascript_tag do %>
7
+ var getQueryParams = function(qs) {
8
+ qs = qs.split('+').join(' ');
9
+
10
+ var params = {},
11
+ tokens,
12
+ re = /[?&]?([^=]+)=([^&]*)/g;
13
+
14
+ while (tokens = re.exec(qs)) {
15
+ params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
16
+ }
17
+
18
+ return params;
19
+ }
20
+
21
+ $(document).ready(
22
+ function(){
23
+ var params = getQueryParams(document.location.search);
24
+ if(params['favorite_product_id'] != null){
25
+ $('#mark-as-favorite').attr('data-remote', true);
26
+ $('#mark-as-favorite').trigger('click');
27
+ }
28
+ }
29
+ );
30
+ <% end %>
31
+ }
32
+ )
33
+
34
+
@@ -0,0 +1,11 @@
1
+ Deface::Override.new(
2
+ virtual_path: 'spree/admin/users/_tabs',
3
+ name: 'add_favorite_products_tab_user_edit',
4
+ insert_bottom: '[data-hook="admin_user_tab_options"]',
5
+ text: %Q{
6
+ <%= content_tag :li, class: ('active' if current == :favorite_products) do %>
7
+ <%= link_to Spree.t(:favorite_products,scope: [:admin, :tab]), spree.favorite_products_admin_user_path(@user) %>
8
+ <% end if can?(:admin, Spree::Product) %>
9
+ }
10
+ )
11
+
@@ -0,0 +1,6 @@
1
+ Deface::Override.new(
2
+ virtual_path: 'spree/admin/shared/_product_sub_menu',
3
+ name: 'select_products_tab',
4
+ insert_bottom: '[data-hook="admin_product_sub_tabs"]',
5
+ text: %Q{ <%= tab :favorite_products if can?(:admin, Spree::Product) %> }
6
+ )
@@ -0,0 +1,10 @@
1
+ Deface::Override.new(
2
+ virtual_path: 'spree/admin/shared/_product_tabs',
3
+ name: 'add_favorite_users_tab_in_product_edit',
4
+ insert_bottom: '[data-hook="admin_product_tabs"]',
5
+ text: %Q{
6
+ <%= content_tag :li, class: ('active' if current == 'Favorite Products') do %>
7
+ <%= link_to Spree.t(:favorites), spree.favorite_users_admin_product_path(@product) %>
8
+ <% end if can?(:admin, Spree::Product) %>
9
+ }
10
+ )
@@ -0,0 +1,13 @@
1
+ Deface::Override.new(
2
+ :virtual_path => 'spree/products/show',
3
+ :name => 'add_link_to_mark_product_as_favorite',
4
+ :surround_contents => "h1[itemprop='name']",
5
+ :text => %Q{
6
+ <%= render_original %>
7
+ <% if spree_user_signed_in? && spree_current_user.has_favorite_product?(@product.id) %>
8
+ <%= link_to Spree.t(:unmark_as_favorite), favorite_product_path(id: @product.id), method: :delete, remote: true, class: 'favorite_link button pull-right', id: 'unmark-as-favorite' %>
9
+ <% else %>
10
+ <%= link_to Spree.t(:mark_as_favorite), favorite_products_path(id: @product.id), method: :post, remote: spree_user_signed_in?, class: 'favorite_link button pull-right', id: 'mark-as-favorite' %>
11
+ <% end %>
12
+ }
13
+ )
@@ -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,45 @@
1
+ <% content_for :page_title do %>
2
+ <%= Spree.t(:listing_favorite_products) %>
3
+ <% end %>
4
+
5
+ <%= paginate @favorite_products %>
6
+
7
+ <% if @favorite_products.any? %>
8
+ <table class="table" id="listing_products">
9
+ <colgroup>
10
+ <col style="width: 15%;">
11
+ <col style="width: 10%;">
12
+ <col style="width: 38%;">
13
+ <col style="width: 10%;">
14
+ <col style="width: 17%;">
15
+ <col style="width: 10%;">
16
+ </colgroup>
17
+ <thead>
18
+ <tr data-hook="admin_products_index_headers">
19
+ <th><%= Spree.t(:sku) %></th>
20
+ <th colspan="2"><%= sort_link @search, :name, Spree.t(:name) %></th>
21
+ <th><%= sort_link @search, :master_default_price_amount, Spree.t(:master_price) %></th>
22
+ <th><%= sort_link @search, 'Users Count', Spree.t(:user_count) %></th>
23
+ </tr>
24
+ </thead>
25
+ <tbody>
26
+ <% @favorite_products.each do |product| %>
27
+ <tr <%= "style='color: red;'" if product.deleted? %> id="<%= spree_dom_id product %>" data-hook="admin_products_index_rows" class="<%= cycle('odd', 'even') %>">
28
+ <td class="align-center"><%= product.sku rescue '' %></td>
29
+ <td class="align-center">
30
+ <%= image_tag product.display_image.attachment(:mini) %>
31
+ </td>
32
+ <td><%= link_to product.try(:name), edit_admin_product_path(product) %></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 %>
@@ -0,0 +1,15 @@
1
+ <% content_for :page_title do %>
2
+ <%= Spree.t(:listing_favorite_products_users) %>
3
+ <% end %>
4
+
5
+ <%= paginate @users %>
6
+
7
+ <% if @users.any? %>
8
+ <%= render 'spree/admin/shared/favorite_products/users', users: @users %>
9
+ <% else %>
10
+ <div class="no-objects-found">
11
+ <%= Spree.t(:no_results) %>
12
+ </div>
13
+ <% end %>
14
+
15
+ <%= paginate @users %>
@@ -0,0 +1,24 @@
1
+ <% content_for :page_actions do %>
2
+ <% if can?(:admin, Spree::Product) %>
3
+ <li id="new_product_link">
4
+ <%= Spree.t(:listing_favorite_products_users) %>
5
+ </li>
6
+ <% end %>
7
+ <% end %>
8
+
9
+ <%= render :partial => 'spree/admin/shared/product_tabs', :locals => { :current => 'Favorite Products' } %>
10
+ <%= render :partial => 'spree/shared/error_messages', :locals => { :target => @product } %>
11
+
12
+ <% admin_breadcrumb(Spree.t(:favorite_products, scope: [:admin, :tab])) %>
13
+
14
+ <%= paginate @users if @user.present? %>
15
+
16
+ <% if @users.any? %>
17
+ <%= render 'spree/admin/shared/favorite_products/users', users: @users %>
18
+ <% else %>
19
+ <div class="no-objects-found">
20
+ <%= Spree.t(:no_results) %>
21
+ </div>
22
+ <% end %>
23
+
24
+ <%= paginate @users if @user.present? %>