feat: scoping support
parent
296669ea6f
commit
4c7d957c22
|
@ -28,11 +28,23 @@ module Rating
|
|||
def rating(scope: nil)
|
||||
rating_records.find_by scopeable: scope
|
||||
end
|
||||
|
||||
def rating_warm_up(scoping: nil)
|
||||
return Rating.find_or_create_by(resource: self) if scoping.blank?
|
||||
|
||||
[scoping].flatten.compact.map do |attribute|
|
||||
next unless respond_to?(attribute)
|
||||
|
||||
[public_send(attribute)].flatten.compact.map do |object|
|
||||
Rating.find_or_create_by! resource: self, scopeable: object
|
||||
end
|
||||
end.flatten.compact
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def rating(as: nil)
|
||||
after_create -> { Rating.find_or_create_by resource: self }, unless: -> { as == :author }
|
||||
def rating(as: nil, scoping: nil)
|
||||
after_save -> { rating_warm_up scoping: scoping }, unless: -> { as == :author }
|
||||
|
||||
has_many :rating_records,
|
||||
as: :resource,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :comment
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Rating::Extension, ':after_create' do
|
||||
context 'when :as is nil' do
|
||||
let!(:article) { create :article }
|
||||
|
||||
it 'creates a rating record with zero values just to be easy to make the count' do
|
||||
rating = Rating::Rating.find_by(resource: article)
|
||||
|
||||
expect(rating.average).to eq 0
|
||||
expect(rating.estimate).to eq 0
|
||||
expect(rating.resource).to eq article
|
||||
expect(rating.scopeable).to eq nil
|
||||
expect(rating.sum).to eq 0
|
||||
expect(rating.total).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context 'when :as is :author' do
|
||||
let!(:author) { create :author }
|
||||
|
||||
it 'does not creates a rating record' do
|
||||
expect(Rating::Rating.exists?(resource: author)).to eq false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Rating::Extension, 'after_save' do
|
||||
context 'when record is author' do
|
||||
let!(:record) { build :author }
|
||||
|
||||
it 'does warm up the cache' do
|
||||
expect(record).not_to receive(:rating_warm_up)
|
||||
|
||||
record.save
|
||||
end
|
||||
end
|
||||
|
||||
context 'when record is not author' do
|
||||
context 'when record has scoping' do
|
||||
let!(:record) { build :article }
|
||||
|
||||
it 'warms up the cache' do
|
||||
expect(record).to receive(:rating_warm_up).with(scoping: :categories)
|
||||
|
||||
record.save
|
||||
end
|
||||
end
|
||||
|
||||
context 'when record has no scoping' do
|
||||
let!(:record) { build :comment }
|
||||
|
||||
it 'warms up the cache' do
|
||||
expect(record).to receive(:rating_warm_up).with(scoping: nil)
|
||||
|
||||
record.save
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,115 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Rating::Extension, '.rating_warm_up' do
|
||||
context 'when scoping is nil' do
|
||||
context 'when update is made' do
|
||||
let!(:record) { create :comment }
|
||||
let!(:rating) { ::Rating::Rating.find_by resource: record }
|
||||
|
||||
it 'creates the cache' do
|
||||
record.rating_warm_up scoping: nil
|
||||
|
||||
expect(::Rating::Rating.all).to eq [rating]
|
||||
end
|
||||
|
||||
it 'returns the cached object' do
|
||||
expect(record.rating_warm_up).to eq rating
|
||||
end
|
||||
end
|
||||
|
||||
context 'when record does not exist' do
|
||||
let!(:record) { create :comment }
|
||||
|
||||
before { ::Rating::Rating.destroy_all }
|
||||
|
||||
it 'creates the cache' do
|
||||
record.rating_warm_up scoping: nil
|
||||
|
||||
expect(::Rating::Rating.all.map(&:resource)).to eq [record]
|
||||
end
|
||||
|
||||
it 'returns the cached object' do
|
||||
expect(record.rating_warm_up).to eq ::Rating::Rating.last
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when scoping is not nil' do
|
||||
context 'when update is made' do
|
||||
let!(:category_1) { create :category }
|
||||
let!(:category_2) { create :category }
|
||||
let!(:record) { create :article, categories: [category_1, category_2] }
|
||||
|
||||
it 'creates the cache' do
|
||||
record.rating_warm_up scoping: :categories
|
||||
|
||||
ratings = ::Rating::Rating.all
|
||||
|
||||
expect(ratings.map(&:scopeable)).to match_array [category_1, category_2]
|
||||
expect(ratings.map(&:resource)).to match_array [record, record]
|
||||
end
|
||||
|
||||
it 'returns the cached objects' do
|
||||
expect(record.rating_warm_up(scoping: :categories)).to eq ::Rating::Rating.all
|
||||
end
|
||||
end
|
||||
|
||||
context 'when record does not exist' do
|
||||
let!(:category_1) { create :category }
|
||||
let!(:category_2) { create :category }
|
||||
let!(:record) { create :article, categories: [category_1, category_2] }
|
||||
|
||||
before { ::Rating::Rating.destroy_all }
|
||||
|
||||
it 'creates the cache' do
|
||||
record.rating_warm_up scoping: :categories
|
||||
|
||||
ratings = ::Rating::Rating.all
|
||||
|
||||
expect(ratings.map(&:scopeable)).to match_array [category_1, category_2]
|
||||
expect(ratings.map(&:resource)).to match_array [record, record]
|
||||
end
|
||||
|
||||
it 'returns the cached objects' do
|
||||
expect(record.rating_warm_up(scoping: :categories)).to eq ::Rating::Rating.all
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a non existent scoping is given' do
|
||||
let!(:record) { create :article }
|
||||
|
||||
it 'returns an empty array' do
|
||||
expect(record.rating_warm_up(scoping: :missing)).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context 'when scoping is given inside array' do
|
||||
let!(:category) { create :category }
|
||||
let!(:record) { create :article, categories: [category] }
|
||||
|
||||
it 'returns the cache' do
|
||||
expect(record.rating_warm_up(scoping: [:categories])).to eq ::Rating::Rating.all
|
||||
end
|
||||
end
|
||||
|
||||
context 'when scoping is given inside multiple arrays' do
|
||||
let!(:category) { create :category }
|
||||
let!(:record) { create :article, categories: [category] }
|
||||
|
||||
it 'returns the cache' do
|
||||
expect(record.rating_warm_up(scoping: [[:categories]])).to eq ::Rating::Rating.all
|
||||
end
|
||||
end
|
||||
|
||||
context 'when scoping is given with nil value together' do
|
||||
let!(:category) { create :category }
|
||||
let!(:record) { create :article, categories: [category] }
|
||||
|
||||
it 'returns the cache' do
|
||||
expect(record.rating_warm_up(scoping: [[:categories, nil], nil])).to eq ::Rating::Rating.all
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,6 +4,8 @@ class CreateCategoriesTable < ActiveRecord::Migration[5.0]
|
|||
def change
|
||||
create_table :categories do |t|
|
||||
t.string :name, null: false
|
||||
|
||||
t.references :article, foreign_key: true, index: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateCommentsTable < ActiveRecord::Migration[5.0]
|
||||
def change
|
||||
create_table :comments
|
||||
end
|
||||
end
|
|
@ -8,6 +8,7 @@ Dir[File.expand_path('db/migrate/*.rb', __dir__)].each { |file| require file }
|
|||
CreateArticlesTable.new.change
|
||||
CreateAuthorsTable.new.change
|
||||
CreateCategoriesTable.new.change
|
||||
CreateCommentsTable.new.change
|
||||
CreateRateTable.new.change
|
||||
CreateRatingTable.new.change
|
||||
CreateReviewRatingsTable.new.change
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Article < ::ActiveRecord::Base
|
||||
rating
|
||||
rating scoping: :categories
|
||||
|
||||
has_many :categories
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Category < ::ActiveRecord::Base
|
||||
belongs_to :article
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Comment < ::ActiveRecord::Base
|
||||
rating
|
||||
end
|
Loading…
Reference in New Issue