diff --git a/.travis.yml b/.travis.yml index 5c2f8313..a4497470 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ env: gemfile: - gemfiles/spree_3_1.gemfile - gemfiles/spree_3_2.gemfile + - gemfiles/spree_4_0.gemfile - gemfiles/spree_master.gemfile script: @@ -19,8 +20,10 @@ script: rvm: - 2.3.1 - 2.2.7 + - 2.3.8 addons: + postgresql: 9.4 apt: packages: - mysql-server-5.6 diff --git a/Appraisals b/Appraisals index cfe64a61..cdba851e 100644 --- a/Appraisals +++ b/Appraisals @@ -15,6 +15,12 @@ appraise 'spree-3-3' do gem 'rails-controller-testing' end +appraise 'spree-4-0' do + gem 'spree', '~> 4.0.0' + gem 'spree_auth_devise', '~> 4.0' + gem 'rails-controller-testing' +end + appraise 'spree-master' do gem 'spree', github: 'spree/spree', branch: 'master' gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: 'master' diff --git a/README.md b/README.md index 9ab34bf4..cfe4c544 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Straightforward review/rating functionality. ## Installation -#### Spree >= 3.1 +#### Spree >= 4.0 ```ruby gem 'spree_reviews', github: 'spree-contrib/spree_reviews' diff --git a/app/assets/javascripts/spree/frontend/spree_reviews.js b/app/assets/javascripts/spree/frontend/spree_reviews.js index f70c6184..9346fd21 100644 --- a/app/assets/javascripts/spree/frontend/spree_reviews.js +++ b/app/assets/javascripts/spree/frontend/spree_reviews.js @@ -1,6 +1,5 @@ //= require jquery.rating //= require spree/frontend -//= require spree/frontend/spree_auth // Navigating to a page with ratings via TurboLinks shows the radio buttons $(document).on('page:load', function () { diff --git a/app/controllers/spree/api/v2/storefront/account/user_reviews_controller.rb b/app/controllers/spree/api/v2/storefront/account/user_reviews_controller.rb new file mode 100644 index 00000000..53d3b73d --- /dev/null +++ b/app/controllers/spree/api/v2/storefront/account/user_reviews_controller.rb @@ -0,0 +1,40 @@ +module Spree + module Api + module V2 + module Storefront + module Account + class UserReviewsController < ::Spree::Api::V2::BaseController + before_action :require_spree_current_user + + # GET /api/v2/storefront/account/reviews + def index + render_serialized_payload { serialize_collection(resource) } + end + + private + + def resource + resource_finder.user_reviews(spree_current_user.id).most_recent_first + end + + def collection_serializer + Spree::V2::Storefront::ReviewSerializer + end + + def serialize_collection(collection) + collection_serializer.new( + collection, + include: resource_includes, + fields: sparse_fields + ).serializable_hash + end + + def resource_finder + Spree::Review + end + end + end + end + end + end +end diff --git a/app/controllers/spree/api/v2/storefront/reviews_controller.rb b/app/controllers/spree/api/v2/storefront/reviews_controller.rb new file mode 100644 index 00000000..3720a923 --- /dev/null +++ b/app/controllers/spree/api/v2/storefront/reviews_controller.rb @@ -0,0 +1,97 @@ +module Spree + module Api + module V2 + module Storefront + class ReviewsController < ::Spree::Api::V2::BaseController + include Spree::Api::V2::CollectionOptionsHelpers + + before_action :load_product, only: [:index, :create] + + def index + render_serialized_payload {serialize_collection(paginated_collection)} + end + + def show + render_serialized_payload {serialize_resource(resource)} + end + + def create + # TODO move to service + # result = create_service.call(user: spree_current_user, review_params: review_params, product: @product, ip_address: request.remote_ip) + + params[:review][:rating].sub!(/\s*[^0-9]*\z/, '') unless params[:review][:rating].blank? + + @review = Spree::Review.new(review_params) + @review.product = @product + @review.user = spree_current_user if spree_user_signed_in? + @review.ip_address = request.remote_ip + @review.locale = I18n.locale.to_s if Spree::Reviews::Config[:track_locale] + + # TODO: @prakash fix permission + # authorize! :create, @review + + render_result(@review) + end + + private + + def create_service + Spree::Api::Dependencies.storefront_account_create_address_service.constantize + end + + def scope + Spree::Review + end + + def resource + scope.find(params[:id]) + end + + def collection_serializer + Spree::V2::Storefront::ReviewSerializer + end + + def resource_serializer + Spree::V2::Storefront::ReviewSerializer + end + + def paginated_collection + collection_paginator.new(collection, params).call + end + + def collection + Spree::Review.approved.where(product: @product) + end + + def load_product + @product = Spree::Product.friendly.find(params[:product_id]) + end + + def permitted_review_attributes + [:rating, :title, :review, :name, :show_identifier] + end + + def review_params + params.require(:review).permit(permitted_review_attributes) + end + + def render_result(review) + if review.save + render_serialized_payload {serialize_resource(review)} + else + # TODO handle error from service + render_error_payload(review.errors) + end + # if result.success? + # render_serialized_payload { serialize_resource(result.value) } + # else + # render_error_payload(result.error) + # end + end + + + end + end + end + end +end diff --git a/app/controllers/spree/products_controller_decorator.rb b/app/controllers/spree/products_controller_decorator.rb index a231a1ce..bf206e37 100644 --- a/app/controllers/spree/products_controller_decorator.rb +++ b/app/controllers/spree/products_controller_decorator.rb @@ -1,5 +1,7 @@ -Spree::ProductsController.class_eval do - helper Spree::ReviewsHelper +module Spree::ProductsControllerDecorator + def self.prepended(base) + base.helper Spree::ReviewsHelper + end reviews_fields = [:avg_rating, :reviews_count] reviews_fields.each { |attrib| Spree::PermittedAttributes.product_attributes << attrib } @@ -8,3 +10,5 @@ reviews_fields.each { |attrib| class_variable_set(:@@product_attributes, class_variable_get(:@@product_attributes).push(attrib)) } end end + +::Spree::ProductsController.prepend(Spree::ProductsControllerDecorator) diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index c7a7083f..86ecb786 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -1,18 +1,25 @@ # Add access to reviews/ratings to the product model -Spree::Product.class_eval do - has_many :reviews +module Spree + module ProductDecorator - def stars - avg_rating.try(:round) || 0 - end + def self.prepended(base) + base.has_many :reviews + end - def recalculate_rating - self[:reviews_count] = reviews.reload.approved.count - if reviews_count > 0 - self[:avg_rating] = reviews.approved.sum(:rating).to_f / reviews_count - else - self[:avg_rating] = 0 + def stars + avg_rating.try(:round) || 0 + end + + def recalculate_rating + self[:reviews_count] = reviews.reload.approved.count + if reviews_count > 0 + self[:avg_rating] = reviews.approved.sum(:rating).to_f / reviews_count + else + self[:avg_rating] = 0 + end + save end - save end end + +Spree::Product.prepend Spree::ProductDecorator diff --git a/app/models/spree/review.rb b/app/models/spree/review.rb index a8724821..c5108378 100644 --- a/app/models/spree/review.rb +++ b/app/models/spree/review.rb @@ -1,6 +1,6 @@ class Spree::Review < ActiveRecord::Base belongs_to :product, touch: true - belongs_to :user, class_name: Spree.user_class.to_s + belongs_to :user, class_name: Spree.user_class.to_s, optional: true has_many :feedback_reviews after_save :recalculate_product_rating, if: :approved? @@ -23,6 +23,7 @@ class Spree::Review < ActiveRecord::Base scope :approved, -> { where(approved: true) } scope :not_approved, -> { where(approved: false) } scope :default_approval_filter, -> { Spree::Reviews::Config[:include_unapproved_reviews] ? all : approved } + scope :user_reviews, ->(user_id) { where('spree_reviews.user_id = ?', user_id) } def feedback_stars return 0 if feedback_reviews.size <= 0 diff --git a/app/serializers/spree/v2/storefront/review_serializer.rb b/app/serializers/spree/v2/storefront/review_serializer.rb new file mode 100644 index 00000000..49f0f332 --- /dev/null +++ b/app/serializers/spree/v2/storefront/review_serializer.rb @@ -0,0 +1,14 @@ +module Spree + module V2 + module Storefront + class ReviewSerializer < BaseSerializer + set_type :review + + attributes :title, :review, :rating + + has_one :user + has_one :product + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index ce787ddc..d5c63276 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,4 +14,18 @@ end end post '/reviews/:review_id/feedback(.:format)' => 'feedback_reviews#create', as: :feedback_reviews + + namespace :api, defaults: { format: 'json' } do + namespace :v2 do + namespace :storefront do + resources :products, only: [] do + resources :reviews, only: [:index, :create] + end + + namespace :account do + resources :reviews, controller: :user_reviews, only: %i[index] + end + end + end + end end diff --git a/gemfiles/spree_4_0.gemfile b/gemfiles/spree_4_0.gemfile new file mode 100644 index 00000000..82154e3a --- /dev/null +++ b/gemfiles/spree_4_0.gemfile @@ -0,0 +1,9 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "spree", "~> 4.0.0" +gem "spree_auth_devise", "~> 4.0" +gem "rails-controller-testing" + +gemspec path: "../" diff --git a/spec/models/review_spec.rb b/spec/models/review_spec.rb index 14d216d7..4595a4f5 100644 --- a/spec/models/review_spec.rb +++ b/spec/models/review_spec.rb @@ -141,6 +141,22 @@ expect(Spree::Review.default_approval_filter.to_a).to match_array expected end end + + context 'user_reviews' do + let!(:user_1) { create(:user, email: 'a@b.com') } + let!(:user_2) { create(:user, email: 'b@c.com') } + + let!(:review_1) { create(:review, created_at: 10.days.ago, user: user_1) } + let!(:review_2) { create(:review, created_at: 2.days.ago, user: user_2) } + + it 'properly runs user_reviews queries' do + expect(described_class.user_reviews(user_1).to_a).to eq([review_1]) + end + + it 'does not show other users reviews' do + expect(described_class.user_reviews(user_2).to_a).to eq([review_2]) + end + end end context '.recalculate_product_rating' do diff --git a/spree_reviews.gemspec b/spree_reviews.gemspec index 9dd61085..19d36756 100644 --- a/spree_reviews.gemspec +++ b/spree_reviews.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.requirements << 'none' - spree_version = '>= 3.1.0', '< 4.0' + spree_version = '>= 3.1.0', '< 5.0' s.add_runtime_dependency 'spree_core', spree_version s.add_runtime_dependency 'spree_auth_devise', spree_version s.add_runtime_dependency 'spree_extension'