add scope support
parent
dd1c8e2dd2
commit
cd51130143
101
README.md
101
README.md
|
@ -3,7 +3,7 @@
|
||||||
[![Build Status](https://travis-ci.org/wbotelhos/rating.svg)](https://travis-ci.org/wbotelhos/rating)
|
[![Build Status](https://travis-ci.org/wbotelhos/rating.svg)](https://travis-ci.org/wbotelhos/rating)
|
||||||
[![Gem Version](https://badge.fury.io/rb/rating.svg)](https://badge.fury.io/rb/rating)
|
[![Gem Version](https://badge.fury.io/rb/rating.svg)](https://badge.fury.io/rb/rating)
|
||||||
|
|
||||||
A true Bayesian rating system with cache enabled.
|
A true Bayesian rating system with scope and cache enabled.
|
||||||
|
|
||||||
## JS Rating?
|
## JS Rating?
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ class User < ApplicationRecord
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
Now this model can vote or be voted.
|
Now this model can vote or receive votes.
|
||||||
|
|
||||||
### rate
|
### rate
|
||||||
|
|
||||||
|
@ -175,6 +175,103 @@ Article.order_by_rating :average, :asc
|
||||||
|
|
||||||
It will return a collection of resource ordered by `Rating` table data.
|
It will return a collection of resource ordered by `Rating` table data.
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
|
||||||
|
All methods support scope query, since you may want to vote on items of a resource instead the resource itself.
|
||||||
|
Let's say an article belongs to one or more categories and you want to vote on some categories of this article.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
category_1 = Category.first
|
||||||
|
category_2 = Category.second
|
||||||
|
author = User.last
|
||||||
|
resource = Article.last
|
||||||
|
```
|
||||||
|
|
||||||
|
In this situation you should scope the vote of article with some category:
|
||||||
|
|
||||||
|
**rate**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
author.rate resource, 3, scopeable: category_1
|
||||||
|
author.rate resource, 5, scopeable: category_2
|
||||||
|
```
|
||||||
|
|
||||||
|
Now `article` has a rating for `category_1` and another one for `category_2`.
|
||||||
|
|
||||||
|
**rating**
|
||||||
|
|
||||||
|
Recovering the rating values for article, we have:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
author.rating
|
||||||
|
# nil
|
||||||
|
```
|
||||||
|
|
||||||
|
But using the scope to make the right query:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
author.rating scope: category_1
|
||||||
|
# { average: 3, estimate: 3, sum: 3, total: 1 }
|
||||||
|
|
||||||
|
author.rating scope: category_2
|
||||||
|
# { average: 5, estimate: 5, sum: 5, total: 1 }
|
||||||
|
```
|
||||||
|
|
||||||
|
**rated**
|
||||||
|
|
||||||
|
On the same way you can find your rates with a scoped query:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
user.rated scope: category_1
|
||||||
|
# { value: 3, scopeable: category_1 }
|
||||||
|
```
|
||||||
|
|
||||||
|
**rates**
|
||||||
|
|
||||||
|
The resource still have the power to consult its rates:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
article.rates scope: category_1
|
||||||
|
# { value: 3, scopeable: category_1 }
|
||||||
|
|
||||||
|
article.rates scope: category_2
|
||||||
|
# { value: 3, scopeable: category_2 }
|
||||||
|
```
|
||||||
|
|
||||||
|
**order_by_rating**
|
||||||
|
|
||||||
|
To order the rating you do the same thing:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Article.order_by_rating scope: category_1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Records
|
||||||
|
|
||||||
|
Maybe you want to recover all records with or without scope, so you can add the suffix `_records` on relations:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
category_1 = Category.first
|
||||||
|
category_2 = Category.second
|
||||||
|
author = User.last
|
||||||
|
resource = Article.last
|
||||||
|
|
||||||
|
author.rate resource, 1
|
||||||
|
author.rate resource, 3, scopeable: category_1
|
||||||
|
author.rate resource, 5, scopeable: category_2
|
||||||
|
|
||||||
|
author.rating_records
|
||||||
|
# { average: 1, estimate: 1, scopeable: nil , sum: 1, total: 1 },
|
||||||
|
# { average: 3, estimate: 3, scopeable: category_1, sum: 3, total: 1 },
|
||||||
|
# { average: 5, estimate: 5, scopeable: category_2, sum: 5, total: 1 }
|
||||||
|
|
||||||
|
user.rated_records
|
||||||
|
# { value: 1 }, { value: 3, scopeable: category_1 }, { value: 5, scopeable: category_2 }
|
||||||
|
|
||||||
|
article.rates_records
|
||||||
|
# { value: 1 }, { value: 3, scopeable: category_1 }, { value: 5, scopeable: category_2 }
|
||||||
|
```
|
||||||
|
|
||||||
## Love it!
|
## Love it!
|
||||||
|
|
||||||
Via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=X8HEP2878NDEG&item_name=rating) or [Gratipay](https://gratipay.com/rating). Thanks! (:
|
Via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=X8HEP2878NDEG&item_name=rating) or [Gratipay](https://gratipay.com/rating). Thanks! (:
|
||||||
|
|
|
@ -7,12 +7,13 @@ class CreateRatingTables < ActiveRecord::Migration[5.0]
|
||||||
|
|
||||||
t.references :author , index: true, null: false, polymorphic: true
|
t.references :author , index: true, null: false, polymorphic: true
|
||||||
t.references :resource , index: true, null: false, polymorphic: true
|
t.references :resource , index: true, null: false, polymorphic: true
|
||||||
|
t.references :scopeable, index: true, null: true , polymorphic: true
|
||||||
|
|
||||||
t.timestamps null: false
|
t.timestamps null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index :rating_rates, %i[author_id author_type resource_id resource_type],
|
add_index :rating_rates, %i[author_id author_type resource_id resource_type scopeable_id scopeable_type],
|
||||||
name: :index_rating_rates_on_author_and_resource,
|
name: :index_rating_rates_on_author_and_resource_and_scopeable,
|
||||||
unique: true
|
unique: true
|
||||||
|
|
||||||
create_table :rating_ratings do |t|
|
create_table :rating_ratings do |t|
|
||||||
|
@ -22,10 +23,13 @@ class CreateRatingTables < ActiveRecord::Migration[5.0]
|
||||||
t.integer :total , default: 0, mull: false
|
t.integer :total , default: 0, mull: false
|
||||||
|
|
||||||
t.references :resource , index: true, null: false, polymorphic: true
|
t.references :resource , index: true, null: false, polymorphic: true
|
||||||
|
t.references :scopeable, index: true, null: true , polymorphic: true
|
||||||
|
|
||||||
t.timestamps null: false
|
t.timestamps null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index :rating_ratings, %i[resource_id resource_type], unique: true
|
add_index :rating_ratings, %i[resource_id resource_type scopeable_id scopeable_type],
|
||||||
|
name: :index_rating_rating_on_resource_and_scopeable,
|
||||||
|
unique: true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,16 +5,28 @@ module Rating
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
def rate(resource, value, author: self)
|
def rate(resource, value, author: self, scope: nil)
|
||||||
Rate.create author: author, resource: resource, value: value
|
Rate.create author: author, resource: resource, scopeable: scope, value: value
|
||||||
end
|
end
|
||||||
|
|
||||||
def rate_for(resource)
|
def rate_for(resource, scope: nil)
|
||||||
Rate.rate_for author: self, resource: resource
|
Rate.rate_for author: self, resource: resource, scopeable: scope
|
||||||
end
|
end
|
||||||
|
|
||||||
def rated?(resource)
|
def rated?(resource, scope: nil)
|
||||||
!rate_for(resource).nil?
|
!rate_for(resource, scope: scope).nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def rates(scope: nil)
|
||||||
|
rates_records.where scopeable: scope
|
||||||
|
end
|
||||||
|
|
||||||
|
def rated(scope: nil)
|
||||||
|
rated_records.where scopeable: scope
|
||||||
|
end
|
||||||
|
|
||||||
|
def rating(scope: nil)
|
||||||
|
rating_records.find_by scopeable: scope
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -22,23 +34,30 @@ module Rating
|
||||||
def rating
|
def rating
|
||||||
after_create { Rating.find_or_create_by resource: self }
|
after_create { Rating.find_or_create_by resource: self }
|
||||||
|
|
||||||
has_one :rating,
|
has_many :rating_records,
|
||||||
as: :resource,
|
as: :resource,
|
||||||
class_name: '::Rating::Rating',
|
class_name: '::Rating::Rating',
|
||||||
dependent: :destroy
|
dependent: :destroy
|
||||||
|
|
||||||
has_many :rates,
|
has_many :rates_records,
|
||||||
as: :resource,
|
as: :resource,
|
||||||
class_name: '::Rating::Rate',
|
class_name: '::Rating::Rate',
|
||||||
dependent: :destroy
|
dependent: :destroy
|
||||||
|
|
||||||
has_many :rated,
|
has_many :rated_records,
|
||||||
as: :author,
|
as: :author,
|
||||||
class_name: '::Rating::Rate',
|
class_name: '::Rating::Rate',
|
||||||
dependent: :destroy
|
dependent: :destroy
|
||||||
|
|
||||||
scope :order_by_rating, ->(column = :estimate, direction = :desc) {
|
scope :order_by_rating, ->(column = :estimate, direction = :desc, scope: nil) {
|
||||||
includes(:rating).order("#{Rating.table_name}.#{column} #{direction}")
|
scope_values = {
|
||||||
|
scopeable_id: scope&.id,
|
||||||
|
scopeable_type: scope&.class&.base_class&.name
|
||||||
|
}
|
||||||
|
|
||||||
|
includes(:rating_records)
|
||||||
|
.where(Rating.table_name => scope_values)
|
||||||
|
.order("#{Rating.table_name}.#{column} #{direction}")
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,17 +8,18 @@ module Rating
|
||||||
|
|
||||||
belongs_to :author , polymorphic: true
|
belongs_to :author , polymorphic: true
|
||||||
belongs_to :resource , polymorphic: true
|
belongs_to :resource , polymorphic: true
|
||||||
|
belongs_to :scopeable, polymorphic: true
|
||||||
|
|
||||||
validates :author, :resource, :value, presence: true
|
validates :author, :resource, :value, presence: true
|
||||||
validates :value, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 100 }
|
validates :value, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 100 }
|
||||||
|
|
||||||
validates :author_id, uniqueness: {
|
validates :author_id, uniqueness: {
|
||||||
case_sensitive: false,
|
case_sensitive: false,
|
||||||
scope: %i[author_type resource_id resource_type]
|
scope: %i[author_type resource_id resource_type scopeable_id scopeable_type]
|
||||||
}
|
}
|
||||||
|
|
||||||
def self.create(author:, resource:, value:)
|
def self.create(author:, resource:, scopeable: nil, value:)
|
||||||
record = find_or_initialize_by(author: author, resource: resource)
|
record = find_or_initialize_by(author: author, resource: resource, scopeable: scopeable)
|
||||||
|
|
||||||
return record if record.persisted? && value == record.value
|
return record if record.persisted? && value == record.value
|
||||||
|
|
||||||
|
@ -28,14 +29,14 @@ module Rating
|
||||||
record
|
record
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.rate_for(author:, resource:)
|
def self.rate_for(author:, resource:, scopeable: nil)
|
||||||
find_by author: author, resource: resource
|
find_by author: author, resource: resource, scopeable: scopeable
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def update_rating
|
def update_rating
|
||||||
::Rating::Rating.update_rating resource
|
::Rating::Rating.update_rating resource, scopeable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,29 +5,33 @@ module Rating
|
||||||
self.table_name = 'rating_ratings'
|
self.table_name = 'rating_ratings'
|
||||||
|
|
||||||
belongs_to :resource , polymorphic: true
|
belongs_to :resource , polymorphic: true
|
||||||
|
belongs_to :scopeable, polymorphic: true
|
||||||
|
|
||||||
validates :average, :estimate, :resource, :sum, :total, presence: true
|
validates :average, :estimate, :resource, :sum, :total, presence: true
|
||||||
validates :average, :estimate, :sum, :total, numericality: true
|
validates :average, :estimate, :sum, :total, numericality: true
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def averager_data(resource)
|
def averager_data(resource, scopeable)
|
||||||
total_count = how_many_resource_received_votes_sql?
|
total_count = how_many_resource_received_votes_sql?(distinct: false, scopeable: scopeable)
|
||||||
distinct_count = how_many_resource_received_votes_sql?(distinct: true)
|
distinct_count = how_many_resource_received_votes_sql?(distinct: true , scopeable: scopeable)
|
||||||
|
values = { resource_type: resource.class.base_class.name }
|
||||||
|
|
||||||
|
values[:scopeable_type] = scopeable.class.base_class.name unless scopeable.nil?
|
||||||
|
|
||||||
sql = %(
|
sql = %(
|
||||||
SELECT
|
SELECT
|
||||||
(#{total_count} / CAST(#{distinct_count} AS float)) count_avg,
|
(#{total_count} / CAST(#{distinct_count} AS float)) count_avg,
|
||||||
COALESCE(AVG(value), 0) rating_avg
|
COALESCE(AVG(value), 0) rating_avg
|
||||||
FROM #{rate_table_name}
|
FROM #{rate_table_name}
|
||||||
WHERE resource_type = :resource_type
|
WHERE resource_type = :resource_type and #{scope_type_query(scopeable)}
|
||||||
).squish
|
).squish
|
||||||
|
|
||||||
execute_sql [sql, resource_type: resource.class.base_class.name]
|
execute_sql [sql, values]
|
||||||
end
|
end
|
||||||
|
|
||||||
def data(resource)
|
def data(resource, scopeable)
|
||||||
averager = averager_data(resource)
|
averager = averager_data(resource, scopeable)
|
||||||
values = values_data(resource)
|
values = values_data(resource, scopeable)
|
||||||
|
|
||||||
{
|
{
|
||||||
average: values.rating_avg,
|
average: values.rating_avg,
|
||||||
|
@ -37,22 +41,31 @@ module Rating
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def values_data(resource)
|
def values_data(resource, scopeable)
|
||||||
|
scope_query = if scopeable.nil?
|
||||||
|
'scopeable_type is NULL and scopeable_id is NULL'
|
||||||
|
else
|
||||||
|
'scopeable_type = ? and scopeable_id = ?'
|
||||||
|
end
|
||||||
|
|
||||||
sql = %(
|
sql = %(
|
||||||
SELECT
|
SELECT
|
||||||
COALESCE(AVG(value), 0) rating_avg,
|
COALESCE(AVG(value), 0) rating_avg,
|
||||||
COALESCE(SUM(value), 0) rating_sum,
|
COALESCE(SUM(value), 0) rating_sum,
|
||||||
COUNT(1) rating_count
|
COUNT(1) rating_count
|
||||||
FROM #{rate_table_name}
|
FROM #{rate_table_name}
|
||||||
WHERE resource_type = ? and resource_id = ?
|
WHERE resource_type = ? and resource_id = ? and #{scope_query}
|
||||||
).squish
|
).squish
|
||||||
|
|
||||||
execute_sql [sql, resource.class.base_class.name, resource.id]
|
values = [sql, resource.class.base_class.name, resource.id]
|
||||||
|
values += [scopeable.class.base_class.name, scopeable.id] unless scopeable.nil?
|
||||||
|
|
||||||
|
execute_sql values
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_rating(resource)
|
def update_rating(resource, scopeable)
|
||||||
record = find_or_initialize_by(resource: resource)
|
record = find_or_initialize_by(resource: resource, scopeable: scopeable)
|
||||||
result = data(resource)
|
result = data(resource, scopeable)
|
||||||
|
|
||||||
record.average = result[:average]
|
record.average = result[:average]
|
||||||
record.sum = result[:sum]
|
record.sum = result[:sum]
|
||||||
|
@ -78,19 +91,23 @@ module Rating
|
||||||
Rate.find_by_sql(sql).first
|
Rate.find_by_sql(sql).first
|
||||||
end
|
end
|
||||||
|
|
||||||
def how_many_resource_received_votes_sql?(distinct: false)
|
def how_many_resource_received_votes_sql?(distinct:, scopeable:)
|
||||||
count = distinct ? 'COUNT(DISTINCT resource_id)' : 'COUNT(1)'
|
count = distinct ? 'COUNT(DISTINCT resource_id)' : 'COUNT(1)'
|
||||||
|
|
||||||
%((
|
%((
|
||||||
SELECT #{count}
|
SELECT #{count}
|
||||||
FROM #{rate_table_name}
|
FROM #{rate_table_name}
|
||||||
WHERE resource_type = :resource_type
|
WHERE resource_type = :resource_type and #{scope_type_query(scopeable)}
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
|
|
||||||
def rate_table_name
|
def rate_table_name
|
||||||
@rate_table_name ||= Rate.table_name
|
@rate_table_name ||= Rate.table_name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def scope_type_query(scopeable)
|
||||||
|
scopeable.nil? ? 'scopeable_type is NULL' : 'scopeable_type = :scopeable_type'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,14 +8,14 @@ require 'rating/version'
|
||||||
|
|
||||||
Gem::Specification.new do |spec|
|
Gem::Specification.new do |spec|
|
||||||
spec.author = 'Washington Botelho'
|
spec.author = 'Washington Botelho'
|
||||||
spec.description = 'A true Bayesian rating system with cache enabled.'
|
spec.description = 'A true Bayesian rating system with scope and cache enabled.'
|
||||||
spec.email = 'wbotelhos@gmail.com'
|
spec.email = 'wbotelhos@gmail.com'
|
||||||
spec.files = Dir['lib/**/*'] + %w[CHANGELOG.md LICENSE README.md]
|
spec.files = Dir['lib/**/*'] + %w[CHANGELOG.md LICENSE README.md]
|
||||||
spec.homepage = 'https://github.com/wbotelhos/rating'
|
spec.homepage = 'https://github.com/wbotelhos/rating'
|
||||||
spec.license = 'MIT'
|
spec.license = 'MIT'
|
||||||
spec.name = 'rating'
|
spec.name = 'rating'
|
||||||
spec.platform = Gem::Platform::RUBY
|
spec.platform = Gem::Platform::RUBY
|
||||||
spec.summary = 'A true Bayesian rating system with cache enabled.'
|
spec.summary = 'A true Bayesian rating system with scope and cache enabled.'
|
||||||
spec.test_files = Dir['spec/**/*']
|
spec.test_files = Dir['spec/**/*']
|
||||||
spec.version = Rating::VERSION
|
spec.version = Rating::VERSION
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :article do
|
factory :article do
|
||||||
sequence(:name) { |i| "Name #{i}" }
|
sequence(:name) { |i| "Article #{i}" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :category do
|
||||||
|
sequence(:name) { |i| "Category #{i}" }
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :user do
|
factory :user do
|
||||||
sequence(:name) { |i| "Name #{i}" }
|
sequence(:name) { |i| "User #{i}" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,7 @@ RSpec.describe Rating::Extension, ':after_create' do
|
||||||
expect(rating.average).to eq 0
|
expect(rating.average).to eq 0
|
||||||
expect(rating.estimate).to eq 0
|
expect(rating.estimate).to eq 0
|
||||||
expect(rating.resource).to eq user
|
expect(rating.resource).to eq user
|
||||||
|
expect(rating.scopeable).to eq nil
|
||||||
expect(rating.sum).to eq 0
|
expect(rating.sum).to eq 0
|
||||||
expect(rating.total).to eq 0
|
expect(rating.total).to eq 0
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Rating::Extension, ':order_by_rating' do
|
RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
|
let!(:category) { create :category }
|
||||||
|
|
||||||
let!(:user_1) { create :user }
|
let!(:user_1) { create :user }
|
||||||
let!(:user_2) { create :user }
|
let!(:user_2) { create :user }
|
||||||
|
|
||||||
|
@ -15,6 +17,9 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
create :rating_rate, author: user_1, resource: article_2, value: 11
|
create :rating_rate, author: user_1, resource: article_2, value: 11
|
||||||
create :rating_rate, author: user_1, resource: article_3, value: 10
|
create :rating_rate, author: user_1, resource: article_3, value: 10
|
||||||
create :rating_rate, author: user_2, resource: article_1, value: 1
|
create :rating_rate, author: user_2, resource: article_1, value: 1
|
||||||
|
|
||||||
|
create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1
|
||||||
|
create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with default filters' do
|
context 'with default filters' do
|
||||||
|
@ -36,6 +41,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
article_1
|
article_1
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'works' do
|
||||||
|
expect(Article.order_by_rating(:average, :asc, scope: category)).to eq [
|
||||||
|
article_1
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'as desc' do
|
context 'as desc' do
|
||||||
|
@ -46,6 +59,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
article_3
|
article_3
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'works' do
|
||||||
|
expect(Article.order_by_rating(:average, :desc, scope: category)).to eq [
|
||||||
|
article_1
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -58,6 +79,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
article_1
|
article_1
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'works' do
|
||||||
|
expect(Article.order_by_rating(:estimate, :asc, scope: category)).to eq [
|
||||||
|
article_1
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'as desc' do
|
context 'as desc' do
|
||||||
|
@ -68,6 +97,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
article_3
|
article_3
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'works' do
|
||||||
|
expect(Article.order_by_rating(:estimate, :desc, scope: category)).to eq [
|
||||||
|
article_1
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -80,6 +117,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
article_1
|
article_1
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'works' do
|
||||||
|
expect(Article.order_by_rating(:sum, :asc, scope: category)).to eq [
|
||||||
|
article_1
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'as desc' do
|
context 'as desc' do
|
||||||
|
@ -90,6 +135,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
article_3
|
article_3
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'works' do
|
||||||
|
expect(Article.order_by_rating(:sum, :desc, scope: category)).to eq [
|
||||||
|
article_1
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'filtering by :total' do
|
context 'filtering by :total' do
|
||||||
|
@ -100,6 +153,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
expect(result[0..1]).to match_array [article_2, article_3]
|
expect(result[0..1]).to match_array [article_2, article_3]
|
||||||
expect(result.last).to eq article_1
|
expect(result.last).to eq article_1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'works' do
|
||||||
|
expect(Article.order_by_rating(:total, :asc, scope: category)).to eq [
|
||||||
|
article_1
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'as desc' do
|
context 'as desc' do
|
||||||
|
@ -109,6 +170,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
expect(result.first).to eq article_1
|
expect(result.first).to eq article_1
|
||||||
expect(result[1..2]).to match_array [article_2, article_3]
|
expect(result[1..2]).to match_array [article_2, article_3]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'works' do
|
||||||
|
expect(Article.order_by_rating(:total, :desc, scope: category)).to eq [
|
||||||
|
article_1
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -117,5 +186,11 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||||
it 'works' do
|
it 'works' do
|
||||||
expect(User.order_by_rating(:total, :desc)).to match_array [user_1, user_2]
|
expect(User.order_by_rating(:total, :desc)).to match_array [user_1, user_2]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'returns empty since creation has no scope' do
|
||||||
|
expect(User.order_by_rating(:total, :desc, scope: category)).to eq []
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,9 +6,21 @@ RSpec.describe Rating::Extension, ':rate_for' do
|
||||||
let!(:user) { create :user }
|
let!(:user) { create :user }
|
||||||
let!(:article) { create :article }
|
let!(:article) { create :article }
|
||||||
|
|
||||||
|
context 'with no scopeable' do
|
||||||
it 'delegates to rate object' do
|
it 'delegates to rate object' do
|
||||||
expect(Rating::Rate).to receive(:rate_for).with author: user, resource: article
|
expect(Rating::Rate).to receive(:rate_for).with author: user, resource: article, scopeable: nil
|
||||||
|
|
||||||
user.rate_for article
|
user.rate_for article
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scopeable' do
|
||||||
|
let!(:category) { build :category }
|
||||||
|
|
||||||
|
it 'delegates to rate object' do
|
||||||
|
expect(Rating::Rate).to receive(:rate_for).with author: user, resource: article, scopeable: category
|
||||||
|
|
||||||
|
user.rate_for article, scope: category
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -6,9 +6,21 @@ RSpec.describe Rating::Extension, ':rate' do
|
||||||
let!(:user) { create :user }
|
let!(:user) { create :user }
|
||||||
let!(:article) { create :article }
|
let!(:article) { create :article }
|
||||||
|
|
||||||
|
context 'with no scopeable' do
|
||||||
it 'delegates to rate object' do
|
it 'delegates to rate object' do
|
||||||
expect(Rating::Rate).to receive(:create).with author: user, resource: article, value: 3
|
expect(Rating::Rate).to receive(:create).with author: user, resource: article, scopeable: nil, value: 3
|
||||||
|
|
||||||
user.rate article, 3
|
user.rate article, 3
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scopeable' do
|
||||||
|
let!(:category) { build :category }
|
||||||
|
|
||||||
|
it 'delegates to rate object' do
|
||||||
|
expect(Rating::Rate).to receive(:create).with author: user, resource: article, scopeable: category, value: 3
|
||||||
|
|
||||||
|
user.rate article, 3, scope: category
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -6,15 +6,33 @@ RSpec.describe Rating::Extension, ':rated?' do
|
||||||
let!(:user) { create :user }
|
let!(:user) { create :user }
|
||||||
let!(:article) { create :article }
|
let!(:article) { create :article }
|
||||||
|
|
||||||
|
context 'with no scopeable' do
|
||||||
context 'when has no rate for the given resource' do
|
context 'when has no rate for the given resource' do
|
||||||
before { allow(user).to receive(:rate_for).with(article) { nil } }
|
before { allow(user).to receive(:rate_for).with(article, scope: nil) { nil } }
|
||||||
|
|
||||||
specify { expect(user.rated?(article)).to eq false }
|
specify { expect(user.rated?(article)).to eq false }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when has rate for the given resource' do
|
context 'when has rate for the given resource' do
|
||||||
before { allow(user).to receive(:rate_for).with(article) { double } }
|
before { allow(user).to receive(:rate_for).with(article, scope: nil) { double } }
|
||||||
|
|
||||||
specify { expect(user.rated?(article)).to eq true }
|
specify { expect(user.rated?(article)).to eq true }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scopeable' do
|
||||||
|
let!(:category) { build :category }
|
||||||
|
|
||||||
|
context 'when has no rate for the given resource' do
|
||||||
|
before { allow(user).to receive(:rate_for).with(article, scope: category) { nil } }
|
||||||
|
|
||||||
|
specify { expect(user.rated?(article, scope: category)).to eq false }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when has rate for the given resource' do
|
||||||
|
before { allow(user).to receive(:rate_for).with(article, scope: category) { double } }
|
||||||
|
|
||||||
|
specify { expect(user.rated?(article, scope: category)).to eq true }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Rating::Extension, '.rated_records' do
|
||||||
|
let!(:category) { create :category }
|
||||||
|
|
||||||
|
let!(:user_1) { create :user }
|
||||||
|
let!(:user_2) { create :user }
|
||||||
|
|
||||||
|
let!(:article_1) { create :article }
|
||||||
|
let!(:article_2) { create :article }
|
||||||
|
let!(:article_3) { create :article }
|
||||||
|
|
||||||
|
let!(:rate_1) { create :rating_rate, author: user_1, resource: article_1, value: 100 }
|
||||||
|
let!(:rate_2) { create :rating_rate, author: user_1, resource: article_2, value: 11 }
|
||||||
|
let!(:rate_3) { create :rating_rate, author: user_1, resource: article_3, value: 10 }
|
||||||
|
let!(:rate_4) { create :rating_rate, author: user_2, resource: article_1, value: 1 }
|
||||||
|
|
||||||
|
let!(:rate_5) { create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1 }
|
||||||
|
let!(:rate_6) { create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2 }
|
||||||
|
|
||||||
|
it 'returns all rates that this author gave' do
|
||||||
|
expect(user_1.rated_records).to match_array [rate_1, rate_2, rate_3, rate_5]
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,36 +3,56 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Rating::Extension, ':rated' do
|
RSpec.describe Rating::Extension, ':rated' do
|
||||||
let!(:user) { create :user }
|
let!(:category) { create :category }
|
||||||
let!(:article) { create :article }
|
|
||||||
|
|
||||||
before { user.rate article, 3 }
|
let!(:user_1) { create :user }
|
||||||
|
let!(:user_2) { create :user }
|
||||||
|
|
||||||
it 'returns rates made by the caller' do
|
let!(:article_1) { create :article }
|
||||||
expect(user.rated).to eq [Rating::Rate.find_by(resource: article)]
|
let!(:article_2) { create :article }
|
||||||
|
let!(:article_3) { create :article }
|
||||||
|
|
||||||
|
let!(:rate_1) { create :rating_rate, author: user_1, resource: article_1, value: 100 }
|
||||||
|
let!(:rate_2) { create :rating_rate, author: user_1, resource: article_2, value: 11 }
|
||||||
|
let!(:rate_3) { create :rating_rate, author: user_1, resource: article_3, value: 10 }
|
||||||
|
let!(:rate_4) { create :rating_rate, author: user_2, resource: article_1, value: 1 }
|
||||||
|
|
||||||
|
let!(:rate_5) { create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1 }
|
||||||
|
let!(:rate_6) { create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2 }
|
||||||
|
|
||||||
|
context 'with no scope' do
|
||||||
|
it 'returns rates made by this author' do
|
||||||
|
expect(user_1.rated).to match_array [rate_1, rate_2, rate_3]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with no scope' do
|
||||||
|
it 'returns scoped rates made by this author' do
|
||||||
|
expect(user_1.rated(scope: category)).to eq [rate_5]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when destroy author' do
|
context 'when destroy author' do
|
||||||
before do
|
before do
|
||||||
expect(Rating::Rate.where(author: user).count).to eq 1
|
expect(Rating::Rate.where(author: user_1).count).to eq 4
|
||||||
|
|
||||||
user.destroy!
|
user_1.destroy!
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'destroys rates of this author' do
|
it 'destroys rates of that author' do
|
||||||
expect(Rating::Rate.where(author: user).count).to eq 0
|
expect(Rating::Rate.where(author: user_1).count).to eq 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when destroy resource rated by author' do
|
context 'when destroy resource rated by author' do
|
||||||
before do
|
before do
|
||||||
expect(Rating::Rate.where(resource: article).count).to eq 1
|
expect(Rating::Rate.where(resource: article_1).count).to eq 4
|
||||||
|
|
||||||
article.destroy!
|
article_1.destroy!
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'destroys rates for that resource' do
|
it 'destroys rates of that resource' do
|
||||||
expect(Rating::Rate.where(resource: article).count).to eq 0
|
expect(Rating::Rate.where(resource: article_1).count).to eq 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Rating::Extension, '.rates_records' do
|
||||||
|
let!(:category) { create :category }
|
||||||
|
|
||||||
|
let!(:user_1) { create :user }
|
||||||
|
let!(:user_2) { create :user }
|
||||||
|
|
||||||
|
let!(:article_1) { create :article }
|
||||||
|
let!(:article_2) { create :article }
|
||||||
|
let!(:article_3) { create :article }
|
||||||
|
|
||||||
|
let!(:rate_1) { create :rating_rate, author: user_1, resource: article_1, value: 100 }
|
||||||
|
let!(:rate_2) { create :rating_rate, author: user_1, resource: article_2, value: 11 }
|
||||||
|
let!(:rate_3) { create :rating_rate, author: user_1, resource: article_3, value: 10 }
|
||||||
|
let!(:rate_4) { create :rating_rate, author: user_2, resource: article_1, value: 1 }
|
||||||
|
|
||||||
|
let!(:rate_5) { create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1 }
|
||||||
|
let!(:rate_6) { create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2 }
|
||||||
|
|
||||||
|
it 'returns all rates that this resource received' do
|
||||||
|
expect(article_1.rates_records).to match_array [rate_1, rate_4, rate_5, rate_6]
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,36 +3,52 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Rating::Extension, ':rates' do
|
RSpec.describe Rating::Extension, ':rates' do
|
||||||
let!(:user) { create :user }
|
let!(:category) { create :category }
|
||||||
let!(:article) { create :article }
|
|
||||||
|
|
||||||
before { user.rate article, 3 }
|
let!(:user_1) { create :user }
|
||||||
|
let!(:user_2) { create :user }
|
||||||
|
|
||||||
it 'returns rates record' do
|
let!(:article_1) { create :article }
|
||||||
expect(article.rates).to eq [Rating::Rate.last]
|
let!(:article_2) { create :article }
|
||||||
|
let!(:article_3) { create :article }
|
||||||
|
|
||||||
|
let!(:rate_1) { create :rating_rate, author: user_1, resource: article_1, value: 100 }
|
||||||
|
let!(:rate_2) { create :rating_rate, author: user_1, resource: article_2, value: 11 }
|
||||||
|
let!(:rate_3) { create :rating_rate, author: user_1, resource: article_3, value: 10 }
|
||||||
|
let!(:rate_4) { create :rating_rate, author: user_2, resource: article_1, value: 1 }
|
||||||
|
|
||||||
|
let!(:rate_5) { create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1 }
|
||||||
|
let!(:rate_6) { create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2 }
|
||||||
|
|
||||||
|
context 'with no scope' do
|
||||||
|
it 'returns rates that this resource received' do
|
||||||
|
expect(article_1.rates).to match_array [rate_1, rate_4]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'returns scoped rates that this resource received' do
|
||||||
|
expect(article_1.rates(scope: category)).to match_array [rate_5, rate_6]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when destroy author' do
|
context 'when destroy author' do
|
||||||
before do
|
it 'destroys rates of that author' do
|
||||||
expect(Rating::Rate.where(resource: article).count).to eq 1
|
expect(Rating::Rate.where(author: user_1).count).to eq 4
|
||||||
|
|
||||||
user.destroy!
|
user_1.destroy!
|
||||||
end
|
|
||||||
|
|
||||||
it 'destroys rates of that resource' do
|
expect(Rating::Rate.where(author: user_1).count).to eq 0
|
||||||
expect(Rating::Rate.where(resource: article).count).to eq 0
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when destroy resource' do
|
context 'when destroy resource' do
|
||||||
before do
|
|
||||||
expect(Rating::Rate.where(resource: article).count).to eq 1
|
|
||||||
|
|
||||||
article.destroy!
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'destroys rates of that resource' do
|
it 'destroys rates of that resource' do
|
||||||
expect(Rating::Rate.where(resource: article).count).to eq 0
|
expect(Rating::Rate.where(resource: article_1).count).to eq 4
|
||||||
|
|
||||||
|
article_1.destroy!
|
||||||
|
|
||||||
|
expect(Rating::Rate.where(resource: article_1).count).to eq 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Rating::Extension, '.rating' do
|
||||||
|
let!(:category) { create :category }
|
||||||
|
|
||||||
|
let!(:user_1) { create :user }
|
||||||
|
let!(:user_2) { create :user }
|
||||||
|
|
||||||
|
let!(:article_1) { create :article }
|
||||||
|
let!(:article_2) { create :article }
|
||||||
|
let!(:article_3) { create :article }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create :rating_rate, author: user_1, resource: article_1, value: 100
|
||||||
|
create :rating_rate, author: user_1, resource: article_2, value: 11
|
||||||
|
create :rating_rate, author: user_1, resource: article_3, value: 10
|
||||||
|
create :rating_rate, author: user_2, resource: article_1, value: 1
|
||||||
|
|
||||||
|
create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1
|
||||||
|
create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns all rating of this resource' do
|
||||||
|
expect(article_1.rating_records).to match_array Rating::Rating.where(resource: article_1)
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,37 +2,53 @@
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Rating::Extension, ':rating' do
|
RSpec.describe Rating::Extension, '.rating' do
|
||||||
|
let!(:category) { create :category }
|
||||||
|
|
||||||
let!(:user_1) { create :user }
|
let!(:user_1) { create :user }
|
||||||
let!(:article) { create :article }
|
let!(:user_2) { create :user }
|
||||||
|
|
||||||
before { user_1.rate article, 1 }
|
let!(:article_1) { create :article }
|
||||||
|
let!(:article_2) { create :article }
|
||||||
|
let!(:article_3) { create :article }
|
||||||
|
|
||||||
|
let!(:rate_1) { create :rating_rate, author: user_1, resource: article_1, value: 100 }
|
||||||
|
let!(:rate_2) { create :rating_rate, author: user_1, resource: article_2, value: 11 }
|
||||||
|
let!(:rate_3) { create :rating_rate, author: user_1, resource: article_3, value: 10 }
|
||||||
|
let!(:rate_4) { create :rating_rate, author: user_2, resource: article_1, value: 1 }
|
||||||
|
|
||||||
|
let!(:rate_5) { create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1 }
|
||||||
|
let!(:rate_6) { create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2 }
|
||||||
|
|
||||||
|
context 'with no scope' do
|
||||||
it 'returns rating record' do
|
it 'returns rating record' do
|
||||||
expect(article.rating).to eq Rating::Rating.last
|
expect(article_1.rating).to eq Rating::Rating.find_by(resource: article_1, scopeable: nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with scope' do
|
||||||
|
it 'returns scoped rating record' do
|
||||||
|
expect(article_1.rating(scope: category)).to eq Rating::Rating.find_by(resource: article_1, scopeable: category)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when destroy author' do
|
context 'when destroy author' do
|
||||||
let!(:user_2) { create :user }
|
|
||||||
|
|
||||||
before { user_2.rate article, 2 }
|
|
||||||
|
|
||||||
it 'does not destroy resource rating' do
|
it 'does not destroy resource rating' do
|
||||||
expect(Rating::Rating.where(resource: article).count).to eq 1
|
expect(Rating::Rating.where(resource: article_1).count).to eq 2
|
||||||
|
|
||||||
user_1.destroy!
|
user_1.destroy!
|
||||||
|
|
||||||
expect(Rating::Rating.where(resource: article).count).to eq 1
|
expect(Rating::Rating.where(resource: article_1).count).to eq 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when destroy resource' do
|
context 'when destroy resource' do
|
||||||
it 'destroys resource rating too' do
|
it 'destroys resource rating too' do
|
||||||
expect(Rating::Rating.where(resource: article).count).to eq 1
|
expect(Rating::Rating.where(resource: article_1).count).to eq 2
|
||||||
|
|
||||||
article.destroy!
|
article_1.destroy!
|
||||||
|
|
||||||
expect(Rating::Rating.where(resource: article).count).to eq 0
|
expect(Rating::Rating.where(resource: article_1).count).to eq 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ RSpec.describe Rating::Rate, ':create' do
|
||||||
let!(:user) { create :user }
|
let!(:user) { create :user }
|
||||||
let!(:article) { create :article }
|
let!(:article) { create :article }
|
||||||
|
|
||||||
|
context 'with no scopeable' do
|
||||||
before { create :rating_rate, author: user, resource: article, value: 3 }
|
before { create :rating_rate, author: user, resource: article, value: 3 }
|
||||||
|
|
||||||
context 'when rate does not exist yet' do
|
context 'when rate does not exist yet' do
|
||||||
|
@ -62,3 +63,69 @@ RSpec.describe Rating::Rate, ':create' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scopeable' do
|
||||||
|
let!(:category) { create :category }
|
||||||
|
|
||||||
|
before { create :rating_rate, author: user, resource: article, scopeable: category, value: 3 }
|
||||||
|
|
||||||
|
context 'when rate does not exist yet' do
|
||||||
|
it 'creates a rate entry' do
|
||||||
|
rate = described_class.last
|
||||||
|
|
||||||
|
expect(rate.author).to eq user
|
||||||
|
expect(rate.resource).to eq article
|
||||||
|
expect(rate.scopeable).to eq category
|
||||||
|
expect(rate.value).to eq 3
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a rating entry' do
|
||||||
|
rating = Rating::Rating.last
|
||||||
|
|
||||||
|
expect(rating.average).to eq 3
|
||||||
|
expect(rating.estimate).to eq 3
|
||||||
|
expect(rating.resource).to eq article
|
||||||
|
expect(rating.scopeable).to eq category
|
||||||
|
expect(rating.sum).to eq 3
|
||||||
|
expect(rating.total).to eq 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when rate already exists' do
|
||||||
|
let!(:user_2) { create :user }
|
||||||
|
|
||||||
|
before { create :rating_rate, author: user_2, resource: article, scopeable: category, value: 4 }
|
||||||
|
|
||||||
|
it 'creates one more rate entry' do
|
||||||
|
rates = described_class.where(author: [user, user_2]).order('created_at asc')
|
||||||
|
|
||||||
|
expect(rates.size).to eq 2
|
||||||
|
|
||||||
|
rate = rates[0]
|
||||||
|
|
||||||
|
expect(rate.author).to eq user
|
||||||
|
expect(rate.resource).to eq article
|
||||||
|
expect(rate.scopeable).to eq category
|
||||||
|
expect(rate.value).to eq 3
|
||||||
|
|
||||||
|
rate = rates[1]
|
||||||
|
|
||||||
|
expect(rate.author).to eq user_2
|
||||||
|
expect(rate.resource).to eq article
|
||||||
|
expect(rate.scopeable).to eq category
|
||||||
|
expect(rate.value).to eq 4
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the unique rating entry' do
|
||||||
|
rating = Rating::Rating.find_by(resource: article, scopeable: category)
|
||||||
|
|
||||||
|
expect(rating.average).to eq 3.5
|
||||||
|
expect(rating.estimate).to eq 3.5
|
||||||
|
expect(rating.resource).to eq article
|
||||||
|
expect(rating.scopeable).to eq category
|
||||||
|
expect(rating.sum).to eq 7
|
||||||
|
expect(rating.total).to eq 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ RSpec.describe Rating::Rate, ':rate_for' do
|
||||||
let!(:user) { create :user }
|
let!(:user) { create :user }
|
||||||
let!(:article) { create :article }
|
let!(:article) { create :article }
|
||||||
|
|
||||||
|
context 'with no scopeable' do
|
||||||
context 'when rate does not exist' do
|
context 'when rate does not exist' do
|
||||||
specify { expect(described_class.rate_for(author: user, resource: article)).to eq nil }
|
specify { expect(described_class.rate_for(author: user, resource: article)).to eq nil }
|
||||||
end
|
end
|
||||||
|
@ -18,3 +19,22 @@ RSpec.describe Rating::Rate, ':rate_for' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scopeable' do
|
||||||
|
let!(:category) { create :category }
|
||||||
|
|
||||||
|
context 'when rate does not exist' do
|
||||||
|
specify { expect(described_class.rate_for(author: user, resource: article, scopeable: category)).to eq nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when rate does not exist' do
|
||||||
|
before { described_class.create author: user, resource: article, scopeable: category, value: 3 }
|
||||||
|
|
||||||
|
it 'returns the record' do
|
||||||
|
query = described_class.rate_for(author: user, resource: article, scopeable: category)
|
||||||
|
|
||||||
|
expect(query).to eq described_class.last
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ RSpec.describe Rating::Rate do
|
||||||
|
|
||||||
it { is_expected.to belong_to :author }
|
it { is_expected.to belong_to :author }
|
||||||
it { is_expected.to belong_to :resource }
|
it { is_expected.to belong_to :resource }
|
||||||
|
it { is_expected.to belong_to :scopeable }
|
||||||
|
|
||||||
it { is_expected.to validate_presence_of :author }
|
it { is_expected.to validate_presence_of :author }
|
||||||
it { is_expected.to validate_presence_of :resource }
|
it { is_expected.to validate_presence_of :resource }
|
||||||
|
@ -20,7 +21,7 @@ RSpec.describe Rating::Rate do
|
||||||
|
|
||||||
specify do
|
specify do
|
||||||
expect(object).to validate_uniqueness_of(:author_id)
|
expect(object).to validate_uniqueness_of(:author_id)
|
||||||
.scoped_to(%i[author_type resource_id resource_type])
|
.scoped_to(%i[author_type resource_id resource_type scopeable_id scopeable_type])
|
||||||
.case_insensitive
|
.case_insensitive
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Rating::Rating, ':averager_data' do
|
RSpec.describe Rating::Rating, ':averager_data' do
|
||||||
subject { described_class.averager_data article_1 }
|
let!(:category) { create :category }
|
||||||
|
|
||||||
let!(:user_1) { create :user }
|
let!(:user_1) { create :user }
|
||||||
let!(:user_2) { create :user }
|
let!(:user_2) { create :user }
|
||||||
|
@ -17,8 +17,14 @@ RSpec.describe Rating::Rating, ':averager_data' do
|
||||||
create :rating_rate, author: user_1, resource: article_2, value: 11
|
create :rating_rate, author: user_1, resource: article_2, value: 11
|
||||||
create :rating_rate, author: user_1, resource: article_3, value: 10
|
create :rating_rate, author: user_1, resource: article_3, value: 10
|
||||||
create :rating_rate, author: user_2, resource: article_1, value: 1
|
create :rating_rate, author: user_2, resource: article_1, value: 1
|
||||||
|
|
||||||
|
create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1
|
||||||
|
create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with no scopeable' do
|
||||||
|
subject { described_class.averager_data article_1, nil }
|
||||||
|
|
||||||
it 'returns the values average of given resource type' do
|
it 'returns the values average of given resource type' do
|
||||||
expect(subject.as_json['rating_avg']).to eq 30.5
|
expect(subject.as_json['rating_avg']).to eq 30.5
|
||||||
end
|
end
|
||||||
|
@ -27,3 +33,16 @@ RSpec.describe Rating::Rating, ':averager_data' do
|
||||||
expect(subject.as_json['count_avg']).to eq 1.3333333333333333
|
expect(subject.as_json['count_avg']).to eq 1.3333333333333333
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scopeable' do
|
||||||
|
subject { described_class.averager_data article_1, category }
|
||||||
|
|
||||||
|
it 'returns the values average of given resource type' do
|
||||||
|
expect(subject.as_json['rating_avg']).to eq 1.5
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the average of number of records for the given resource type' do
|
||||||
|
expect(subject.as_json['count_avg']).to eq 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Rating::Rating, ':data' do
|
RSpec.describe Rating::Rating, ':data' do
|
||||||
subject { described_class.data article_1 }
|
let!(:category) { create :category }
|
||||||
|
|
||||||
let!(:user_1) { create :user }
|
let!(:user_1) { create :user }
|
||||||
let!(:user_2) { create :user }
|
let!(:user_2) { create :user }
|
||||||
|
@ -17,8 +17,14 @@ RSpec.describe Rating::Rating, ':data' do
|
||||||
create :rating_rate, author: user_1, resource: article_2, value: 11
|
create :rating_rate, author: user_1, resource: article_2, value: 11
|
||||||
create :rating_rate, author: user_1, resource: article_3, value: 10
|
create :rating_rate, author: user_1, resource: article_3, value: 10
|
||||||
create :rating_rate, author: user_2, resource: article_1, value: 1
|
create :rating_rate, author: user_2, resource: article_1, value: 1
|
||||||
|
|
||||||
|
create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1
|
||||||
|
create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with no scopeable' do
|
||||||
|
subject { described_class.data article_1, nil }
|
||||||
|
|
||||||
it 'returns the average of value for a resource' do
|
it 'returns the average of value for a resource' do
|
||||||
expect(subject[:average]).to eq 50.5
|
expect(subject[:average]).to eq 50.5
|
||||||
end
|
end
|
||||||
|
@ -35,3 +41,24 @@ RSpec.describe Rating::Rating, ':data' do
|
||||||
expect(subject[:estimate]).to eq 42.50000000000001
|
expect(subject[:estimate]).to eq 42.50000000000001
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scopeable' do
|
||||||
|
subject { described_class.data article_1, category }
|
||||||
|
|
||||||
|
it 'returns the average of value for a resource' do
|
||||||
|
expect(subject[:average]).to eq 1.5
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the sum of values for a resource' do
|
||||||
|
expect(subject[:sum]).to eq 3
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the count of votes for a resource' do
|
||||||
|
expect(subject[:total]).to eq 2
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the estimate for a resource' do
|
||||||
|
expect(subject[:estimate]).to eq 1.5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Rating::Rating, ':update_rating' do
|
RSpec.describe Rating::Rating, ':update_rating' do
|
||||||
|
let!(:category) { create :category }
|
||||||
|
|
||||||
let!(:user_1) { create :user }
|
let!(:user_1) { create :user }
|
||||||
let!(:user_2) { create :user }
|
let!(:user_2) { create :user }
|
||||||
|
|
||||||
|
@ -15,8 +17,12 @@ RSpec.describe Rating::Rating, ':update_rating' do
|
||||||
create :rating_rate, author: user_1, resource: article_2, value: 11
|
create :rating_rate, author: user_1, resource: article_2, value: 11
|
||||||
create :rating_rate, author: user_1, resource: article_3, value: 10
|
create :rating_rate, author: user_1, resource: article_3, value: 10
|
||||||
create :rating_rate, author: user_2, resource: article_1, value: 1
|
create :rating_rate, author: user_2, resource: article_1, value: 1
|
||||||
|
|
||||||
|
create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1
|
||||||
|
create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with no scopeable' do
|
||||||
it 'updates the rating data of the given resource' do
|
it 'updates the rating data of the given resource' do
|
||||||
record = described_class.find_by(resource: article_1)
|
record = described_class.find_by(resource: article_1)
|
||||||
|
|
||||||
|
@ -26,3 +32,15 @@ RSpec.describe Rating::Rating, ':update_rating' do
|
||||||
expect(record.total).to eq 2
|
expect(record.total).to eq 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scopeable' do
|
||||||
|
it 'updates the rating data of the given resource respecting the scope' do
|
||||||
|
record = described_class.find_by(resource: article_1, scopeable: category)
|
||||||
|
|
||||||
|
expect(record.average).to eq 1.5
|
||||||
|
expect(record.estimate).to eq 1.5
|
||||||
|
expect(record.sum).to eq 3
|
||||||
|
expect(record.total).to eq 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Rating::Rating, ':values_data' do
|
RSpec.describe Rating::Rating, ':values_data' do
|
||||||
subject { described_class.values_data article_1 }
|
let!(:category) { create :category }
|
||||||
|
|
||||||
let!(:user_1) { create :user }
|
let!(:user_1) { create :user }
|
||||||
let!(:user_2) { create :user }
|
let!(:user_2) { create :user }
|
||||||
|
@ -17,8 +17,14 @@ RSpec.describe Rating::Rating, ':values_data' do
|
||||||
create :rating_rate, author: user_1, resource: article_2, value: 11
|
create :rating_rate, author: user_1, resource: article_2, value: 11
|
||||||
create :rating_rate, author: user_1, resource: article_3, value: 10
|
create :rating_rate, author: user_1, resource: article_3, value: 10
|
||||||
create :rating_rate, author: user_2, resource: article_1, value: 4
|
create :rating_rate, author: user_2, resource: article_1, value: 4
|
||||||
|
|
||||||
|
create :rating_rate, author: user_1, resource: article_1, scopeable: category, value: 1
|
||||||
|
create :rating_rate, author: user_2, resource: article_1, scopeable: category, value: 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with no scopeable' do
|
||||||
|
subject { described_class.values_data article_1, nil }
|
||||||
|
|
||||||
it 'returns the average of value for a resource' do
|
it 'returns the average of value for a resource' do
|
||||||
expect(subject.as_json['rating_avg']).to eq 52.0
|
expect(subject.as_json['rating_avg']).to eq 52.0
|
||||||
end
|
end
|
||||||
|
@ -31,3 +37,20 @@ RSpec.describe Rating::Rating, ':values_data' do
|
||||||
expect(subject.as_json['rating_count']).to eq 2
|
expect(subject.as_json['rating_count']).to eq 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with scopeable' do
|
||||||
|
subject { described_class.values_data article_1, category }
|
||||||
|
|
||||||
|
it 'returns the average of value for a resource' do
|
||||||
|
expect(subject.as_json['rating_avg']).to eq 1.5
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the sum of values for a resource' do
|
||||||
|
expect(subject.as_json['rating_sum']).to eq 3
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the count of votes for a resource' do
|
||||||
|
expect(subject.as_json['rating_count']).to eq 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@ RSpec.describe Rating::Rating do
|
||||||
it { expect(object).to be_valid }
|
it { expect(object).to be_valid }
|
||||||
|
|
||||||
it { is_expected.to belong_to :resource }
|
it { is_expected.to belong_to :resource }
|
||||||
|
it { is_expected.to belong_to :scopeable }
|
||||||
|
|
||||||
it { is_expected.to validate_presence_of :average }
|
it { is_expected.to validate_presence_of :average }
|
||||||
it { is_expected.to validate_presence_of :estimate }
|
it { is_expected.to validate_presence_of :estimate }
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CreateCategoriesTable < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
create_table :categories do |t|
|
||||||
|
t.string :name, null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
require File.expand_path('../../lib/generators/rating/templates/db/migrate/create_rating_tables.rb', __dir__)
|
require File.expand_path('../../lib/generators/rating/templates/db/migrate/create_rating_tables.rb', __dir__)
|
||||||
|
|
||||||
|
CreateArticlesTable.new.change
|
||||||
|
CreateCategoriesTable.new.change
|
||||||
CreateRatingTables.new.change
|
CreateRatingTables.new.change
|
||||||
CreateUsersTable.new.change
|
CreateUsersTable.new.change
|
||||||
CreateArticlesTable.new.change
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Category < ::ActiveRecord::Base
|
||||||
|
end
|
Loading…
Reference in New Issue