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)
|
||||
[![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?
|
||||
|
||||
|
@ -71,7 +71,7 @@ class User < ApplicationRecord
|
|||
end
|
||||
```
|
||||
|
||||
Now this model can vote or be voted.
|
||||
Now this model can vote or receive votes.
|
||||
|
||||
### rate
|
||||
|
||||
|
@ -175,6 +175,103 @@ Article.order_by_rating :average, :asc
|
|||
|
||||
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!
|
||||
|
||||
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 :resource , index: true, null: false, polymorphic: true
|
||||
t.references :scopeable, index: true, null: true , polymorphic: true
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
add_index :rating_rates, %i[author_id author_type resource_id resource_type],
|
||||
name: :index_rating_rates_on_author_and_resource,
|
||||
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_and_scopeable,
|
||||
unique: true
|
||||
|
||||
create_table :rating_ratings do |t|
|
||||
|
@ -22,10 +23,13 @@ class CreateRatingTables < ActiveRecord::Migration[5.0]
|
|||
t.integer :total , default: 0, mull: false
|
||||
|
||||
t.references :resource , index: true, null: false, polymorphic: true
|
||||
t.references :scopeable, index: true, null: true , polymorphic: true
|
||||
|
||||
t.timestamps null: false
|
||||
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
|
||||
|
|
|
@ -5,16 +5,28 @@ module Rating
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
def rate(resource, value, author: self)
|
||||
Rate.create author: author, resource: resource, value: value
|
||||
def rate(resource, value, author: self, scope: nil)
|
||||
Rate.create author: author, resource: resource, scopeable: scope, value: value
|
||||
end
|
||||
|
||||
def rate_for(resource)
|
||||
Rate.rate_for author: self, resource: resource
|
||||
def rate_for(resource, scope: nil)
|
||||
Rate.rate_for author: self, resource: resource, scopeable: scope
|
||||
end
|
||||
|
||||
def rated?(resource)
|
||||
!rate_for(resource).nil?
|
||||
def rated?(resource, scope: 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
|
||||
|
||||
|
@ -22,23 +34,30 @@ module Rating
|
|||
def rating
|
||||
after_create { Rating.find_or_create_by resource: self }
|
||||
|
||||
has_one :rating,
|
||||
has_many :rating_records,
|
||||
as: :resource,
|
||||
class_name: '::Rating::Rating',
|
||||
dependent: :destroy
|
||||
|
||||
has_many :rates,
|
||||
has_many :rates_records,
|
||||
as: :resource,
|
||||
class_name: '::Rating::Rate',
|
||||
dependent: :destroy
|
||||
|
||||
has_many :rated,
|
||||
has_many :rated_records,
|
||||
as: :author,
|
||||
class_name: '::Rating::Rate',
|
||||
dependent: :destroy
|
||||
|
||||
scope :order_by_rating, ->(column = :estimate, direction = :desc) {
|
||||
includes(:rating).order("#{Rating.table_name}.#{column} #{direction}")
|
||||
scope :order_by_rating, ->(column = :estimate, direction = :desc, scope: nil) {
|
||||
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
|
||||
|
|
|
@ -8,17 +8,18 @@ module Rating
|
|||
|
||||
belongs_to :author , polymorphic: true
|
||||
belongs_to :resource , polymorphic: true
|
||||
belongs_to :scopeable, polymorphic: true
|
||||
|
||||
validates :author, :resource, :value, presence: true
|
||||
validates :value, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 100 }
|
||||
|
||||
validates :author_id, uniqueness: {
|
||||
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:)
|
||||
record = find_or_initialize_by(author: author, resource: resource)
|
||||
def self.create(author:, resource:, scopeable: nil, value:)
|
||||
record = find_or_initialize_by(author: author, resource: resource, scopeable: scopeable)
|
||||
|
||||
return record if record.persisted? && value == record.value
|
||||
|
||||
|
@ -28,14 +29,14 @@ module Rating
|
|||
record
|
||||
end
|
||||
|
||||
def self.rate_for(author:, resource:)
|
||||
find_by author: author, resource: resource
|
||||
def self.rate_for(author:, resource:, scopeable: nil)
|
||||
find_by author: author, resource: resource, scopeable: scopeable
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_rating
|
||||
::Rating::Rating.update_rating resource
|
||||
::Rating::Rating.update_rating resource, scopeable
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,29 +5,33 @@ module Rating
|
|||
self.table_name = 'rating_ratings'
|
||||
|
||||
belongs_to :resource , polymorphic: true
|
||||
belongs_to :scopeable, polymorphic: true
|
||||
|
||||
validates :average, :estimate, :resource, :sum, :total, presence: true
|
||||
validates :average, :estimate, :sum, :total, numericality: true
|
||||
|
||||
class << self
|
||||
def averager_data(resource)
|
||||
total_count = how_many_resource_received_votes_sql?
|
||||
distinct_count = how_many_resource_received_votes_sql?(distinct: true)
|
||||
def averager_data(resource, scopeable)
|
||||
total_count = how_many_resource_received_votes_sql?(distinct: false, scopeable: scopeable)
|
||||
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 = %(
|
||||
SELECT
|
||||
(#{total_count} / CAST(#{distinct_count} AS float)) count_avg,
|
||||
COALESCE(AVG(value), 0) rating_avg
|
||||
FROM #{rate_table_name}
|
||||
WHERE resource_type = :resource_type
|
||||
WHERE resource_type = :resource_type and #{scope_type_query(scopeable)}
|
||||
).squish
|
||||
|
||||
execute_sql [sql, resource_type: resource.class.base_class.name]
|
||||
execute_sql [sql, values]
|
||||
end
|
||||
|
||||
def data(resource)
|
||||
averager = averager_data(resource)
|
||||
values = values_data(resource)
|
||||
def data(resource, scopeable)
|
||||
averager = averager_data(resource, scopeable)
|
||||
values = values_data(resource, scopeable)
|
||||
|
||||
{
|
||||
average: values.rating_avg,
|
||||
|
@ -37,22 +41,31 @@ module Rating
|
|||
}
|
||||
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 = %(
|
||||
SELECT
|
||||
COALESCE(AVG(value), 0) rating_avg,
|
||||
COALESCE(SUM(value), 0) rating_sum,
|
||||
COUNT(1) rating_count
|
||||
FROM #{rate_table_name}
|
||||
WHERE resource_type = ? and resource_id = ?
|
||||
WHERE resource_type = ? and resource_id = ? and #{scope_query}
|
||||
).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
|
||||
|
||||
def update_rating(resource)
|
||||
record = find_or_initialize_by(resource: resource)
|
||||
result = data(resource)
|
||||
def update_rating(resource, scopeable)
|
||||
record = find_or_initialize_by(resource: resource, scopeable: scopeable)
|
||||
result = data(resource, scopeable)
|
||||
|
||||
record.average = result[:average]
|
||||
record.sum = result[:sum]
|
||||
|
@ -78,19 +91,23 @@ module Rating
|
|||
Rate.find_by_sql(sql).first
|
||||
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)'
|
||||
|
||||
%((
|
||||
SELECT #{count}
|
||||
FROM #{rate_table_name}
|
||||
WHERE resource_type = :resource_type
|
||||
WHERE resource_type = :resource_type and #{scope_type_query(scopeable)}
|
||||
))
|
||||
end
|
||||
|
||||
def rate_table_name
|
||||
@rate_table_name ||= Rate.table_name
|
||||
end
|
||||
|
||||
def scope_type_query(scopeable)
|
||||
scopeable.nil? ? 'scopeable_type is NULL' : 'scopeable_type = :scopeable_type'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,14 +8,14 @@ require 'rating/version'
|
|||
|
||||
Gem::Specification.new do |spec|
|
||||
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.files = Dir['lib/**/*'] + %w[CHANGELOG.md LICENSE README.md]
|
||||
spec.homepage = 'https://github.com/wbotelhos/rating'
|
||||
spec.license = 'MIT'
|
||||
spec.name = 'rating'
|
||||
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.version = Rating::VERSION
|
||||
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
FactoryBot.define do
|
||||
factory :article do
|
||||
sequence(:name) { |i| "Name #{i}" }
|
||||
sequence(:name) { |i| "Article #{i}" }
|
||||
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
|
||||
factory :user do
|
||||
sequence(:name) { |i| "Name #{i}" }
|
||||
sequence(:name) { |i| "User #{i}" }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,7 @@ RSpec.describe Rating::Extension, ':after_create' do
|
|||
expect(rating.average).to eq 0
|
||||
expect(rating.estimate).to eq 0
|
||||
expect(rating.resource).to eq user
|
||||
expect(rating.scopeable).to eq nil
|
||||
expect(rating.sum).to eq 0
|
||||
expect(rating.total).to eq 0
|
||||
end
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Rating::Extension, ':order_by_rating' do
|
||||
let!(:category) { create :category }
|
||||
|
||||
let!(:user_1) { 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_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
|
||||
|
||||
context 'with default filters' do
|
||||
|
@ -36,6 +41,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
|||
article_1
|
||||
]
|
||||
end
|
||||
|
||||
context 'with scope' do
|
||||
it 'works' do
|
||||
expect(Article.order_by_rating(:average, :asc, scope: category)).to eq [
|
||||
article_1
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'as desc' do
|
||||
|
@ -46,6 +59,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
|||
article_3
|
||||
]
|
||||
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
|
||||
|
||||
|
@ -58,6 +79,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
|||
article_1
|
||||
]
|
||||
end
|
||||
|
||||
context 'with scope' do
|
||||
it 'works' do
|
||||
expect(Article.order_by_rating(:estimate, :asc, scope: category)).to eq [
|
||||
article_1
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'as desc' do
|
||||
|
@ -68,6 +97,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
|||
article_3
|
||||
]
|
||||
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
|
||||
|
||||
|
@ -80,6 +117,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
|||
article_1
|
||||
]
|
||||
end
|
||||
|
||||
context 'with scope' do
|
||||
it 'works' do
|
||||
expect(Article.order_by_rating(:sum, :asc, scope: category)).to eq [
|
||||
article_1
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'as desc' do
|
||||
|
@ -90,6 +135,14 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
|||
article_3
|
||||
]
|
||||
end
|
||||
|
||||
context 'with scope' do
|
||||
it 'works' do
|
||||
expect(Article.order_by_rating(:sum, :desc, scope: category)).to eq [
|
||||
article_1
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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.last).to eq article_1
|
||||
end
|
||||
|
||||
context 'with scope' do
|
||||
it 'works' do
|
||||
expect(Article.order_by_rating(:total, :asc, scope: category)).to eq [
|
||||
article_1
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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[1..2]).to match_array [article_2, article_3]
|
||||
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
|
||||
|
@ -117,5 +186,11 @@ RSpec.describe Rating::Extension, ':order_by_rating' do
|
|||
it 'works' do
|
||||
expect(User.order_by_rating(:total, :desc)).to match_array [user_1, user_2]
|
||||
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
|
||||
|
|
|
@ -6,9 +6,21 @@ RSpec.describe Rating::Extension, ':rate_for' do
|
|||
let!(:user) { create :user }
|
||||
let!(:article) { create :article }
|
||||
|
||||
context 'with no scopeable' 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
|
||||
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!(:article) { create :article }
|
||||
|
||||
context 'with no scopeable' 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
|
||||
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!(:article) { create :article }
|
||||
|
||||
context 'with no scopeable' 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 }
|
||||
end
|
||||
|
||||
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 }
|
||||
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'
|
||||
|
||||
RSpec.describe Rating::Extension, ':rated' do
|
||||
let!(:user) { create :user }
|
||||
let!(:article) { create :article }
|
||||
let!(:category) { create :category }
|
||||
|
||||
before { user.rate article, 3 }
|
||||
let!(:user_1) { create :user }
|
||||
let!(:user_2) { create :user }
|
||||
|
||||
it 'returns rates made by the caller' do
|
||||
expect(user.rated).to eq [Rating::Rate.find_by(resource: article)]
|
||||
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 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
|
||||
|
||||
context 'when destroy author' 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
|
||||
|
||||
it 'destroys rates of this author' do
|
||||
expect(Rating::Rate.where(author: user).count).to eq 0
|
||||
it 'destroys rates of that author' do
|
||||
expect(Rating::Rate.where(author: user_1).count).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context 'when destroy resource rated by author' 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
|
||||
|
||||
it 'destroys rates for that resource' do
|
||||
expect(Rating::Rate.where(resource: article).count).to eq 0
|
||||
it 'destroys rates of that resource' do
|
||||
expect(Rating::Rate.where(resource: article_1).count).to eq 0
|
||||
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'
|
||||
|
||||
RSpec.describe Rating::Extension, ':rates' do
|
||||
let!(:user) { create :user }
|
||||
let!(:article) { create :article }
|
||||
let!(:category) { create :category }
|
||||
|
||||
before { user.rate article, 3 }
|
||||
let!(:user_1) { create :user }
|
||||
let!(:user_2) { create :user }
|
||||
|
||||
it 'returns rates record' do
|
||||
expect(article.rates).to eq [Rating::Rate.last]
|
||||
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 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
|
||||
|
||||
context 'when destroy author' do
|
||||
before do
|
||||
expect(Rating::Rate.where(resource: article).count).to eq 1
|
||||
it 'destroys rates of that author' do
|
||||
expect(Rating::Rate.where(author: user_1).count).to eq 4
|
||||
|
||||
user.destroy!
|
||||
end
|
||||
user_1.destroy!
|
||||
|
||||
it 'destroys rates of that resource' do
|
||||
expect(Rating::Rate.where(resource: article).count).to eq 0
|
||||
expect(Rating::Rate.where(author: user_1).count).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
|
|
|
@ -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'
|
||||
|
||||
RSpec.describe Rating::Extension, ':rating' do
|
||||
RSpec.describe Rating::Extension, '.rating' do
|
||||
let!(:category) { create :category }
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
context 'when destroy author' do
|
||||
let!(:user_2) { create :user }
|
||||
|
||||
before { user_2.rate article, 2 }
|
||||
|
||||
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!
|
||||
|
||||
expect(Rating::Rating.where(resource: article).count).to eq 1
|
||||
expect(Rating::Rating.where(resource: article_1).count).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'when destroy resource' 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
|
||||
|
|
|
@ -6,6 +6,7 @@ RSpec.describe Rating::Rate, ':create' do
|
|||
let!(:user) { create :user }
|
||||
let!(:article) { create :article }
|
||||
|
||||
context 'with no scopeable' do
|
||||
before { create :rating_rate, author: user, resource: article, value: 3 }
|
||||
|
||||
context 'when rate does not exist yet' do
|
||||
|
@ -62,3 +63,69 @@ RSpec.describe Rating::Rate, ':create' do
|
|||
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!(:article) { create :article }
|
||||
|
||||
context 'with no scopeable' do
|
||||
context 'when rate does not exist' do
|
||||
specify { expect(described_class.rate_for(author: user, resource: article)).to eq nil }
|
||||
end
|
||||
|
@ -18,3 +19,22 @@ RSpec.describe Rating::Rate, ':rate_for' do
|
|||
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 :resource }
|
||||
it { is_expected.to belong_to :scopeable }
|
||||
|
||||
it { is_expected.to validate_presence_of :author }
|
||||
it { is_expected.to validate_presence_of :resource }
|
||||
|
@ -20,7 +21,7 @@ RSpec.describe Rating::Rate do
|
|||
|
||||
specify do
|
||||
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
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require 'rails_helper'
|
||||
|
||||
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_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_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
|
||||
|
||||
context 'with no scopeable' do
|
||||
subject { described_class.averager_data article_1, nil }
|
||||
|
||||
it 'returns the values average of given resource type' do
|
||||
expect(subject.as_json['rating_avg']).to eq 30.5
|
||||
end
|
||||
|
@ -27,3 +33,16 @@ RSpec.describe Rating::Rating, ':averager_data' do
|
|||
expect(subject.as_json['count_avg']).to eq 1.3333333333333333
|
||||
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'
|
||||
|
||||
RSpec.describe Rating::Rating, ':data' do
|
||||
subject { described_class.data article_1 }
|
||||
let!(:category) { create :category }
|
||||
|
||||
let!(:user_1) { 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_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
|
||||
|
||||
context 'with no scopeable' do
|
||||
subject { described_class.data article_1, nil }
|
||||
|
||||
it 'returns the average of value for a resource' do
|
||||
expect(subject[:average]).to eq 50.5
|
||||
end
|
||||
|
@ -35,3 +41,24 @@ RSpec.describe Rating::Rating, ':data' do
|
|||
expect(subject[:estimate]).to eq 42.50000000000001
|
||||
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'
|
||||
|
||||
RSpec.describe Rating::Rating, ':update_rating' do
|
||||
let!(:category) { create :category }
|
||||
|
||||
let!(:user_1) { 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_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
|
||||
|
||||
context 'with no scopeable' do
|
||||
it 'updates the rating data of the given resource' do
|
||||
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
|
||||
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'
|
||||
|
||||
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_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_3, value: 10
|
||||
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
|
||||
|
||||
context 'with no scopeable' do
|
||||
subject { described_class.values_data article_1, nil }
|
||||
|
||||
it 'returns the average of value for a resource' do
|
||||
expect(subject.as_json['rating_avg']).to eq 52.0
|
||||
end
|
||||
|
@ -31,3 +37,20 @@ RSpec.describe Rating::Rating, ':values_data' do
|
|||
expect(subject.as_json['rating_count']).to eq 2
|
||||
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 { 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 :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__)
|
||||
|
||||
CreateArticlesTable.new.change
|
||||
CreateCategoriesTable.new.change
|
||||
CreateRatingTables.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