From 4c7d957c228b1c7ecd066f12ec3b0ff4d5b3fee7 Mon Sep 17 00:00:00 2001 From: Washington Botelho Date: Thu, 8 Feb 2018 20:44:39 -0200 Subject: [PATCH] feat: scoping support --- lib/rating/models/rating/extension.rb | 16 ++- spec/factories/comment.rb | 5 + spec/models/extension/after_create_spec.rb | 28 ----- spec/models/extension/after_save_spec.rb | 37 ++++++ spec/models/extension/rating_warm_up_spec.rb | 115 ++++++++++++++++++ .../db/migrate/create_categories_table.rb | 2 + .../db/migrate/create_comments_table.rb | 7 ++ spec/support/migrate.rb | 1 + spec/support/models/article.rb | 4 +- spec/support/models/category.rb | 1 + spec/support/models/comment.rb | 5 + 11 files changed, 190 insertions(+), 31 deletions(-) create mode 100644 spec/factories/comment.rb delete mode 100644 spec/models/extension/after_create_spec.rb create mode 100644 spec/models/extension/after_save_spec.rb create mode 100644 spec/models/extension/rating_warm_up_spec.rb create mode 100644 spec/support/db/migrate/create_comments_table.rb create mode 100644 spec/support/models/comment.rb diff --git a/lib/rating/models/rating/extension.rb b/lib/rating/models/rating/extension.rb index a8acca7..c25d6aa 100644 --- a/lib/rating/models/rating/extension.rb +++ b/lib/rating/models/rating/extension.rb @@ -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, diff --git a/spec/factories/comment.rb b/spec/factories/comment.rb new file mode 100644 index 0000000..257d3f6 --- /dev/null +++ b/spec/factories/comment.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :comment +end diff --git a/spec/models/extension/after_create_spec.rb b/spec/models/extension/after_create_spec.rb deleted file mode 100644 index cb68339..0000000 --- a/spec/models/extension/after_create_spec.rb +++ /dev/null @@ -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 diff --git a/spec/models/extension/after_save_spec.rb b/spec/models/extension/after_save_spec.rb new file mode 100644 index 0000000..daa5a89 --- /dev/null +++ b/spec/models/extension/after_save_spec.rb @@ -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 diff --git a/spec/models/extension/rating_warm_up_spec.rb b/spec/models/extension/rating_warm_up_spec.rb new file mode 100644 index 0000000..3040000 --- /dev/null +++ b/spec/models/extension/rating_warm_up_spec.rb @@ -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 diff --git a/spec/support/db/migrate/create_categories_table.rb b/spec/support/db/migrate/create_categories_table.rb index f76519a..5b6760c 100644 --- a/spec/support/db/migrate/create_categories_table.rb +++ b/spec/support/db/migrate/create_categories_table.rb @@ -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 diff --git a/spec/support/db/migrate/create_comments_table.rb b/spec/support/db/migrate/create_comments_table.rb new file mode 100644 index 0000000..c333099 --- /dev/null +++ b/spec/support/db/migrate/create_comments_table.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class CreateCommentsTable < ActiveRecord::Migration[5.0] + def change + create_table :comments + end +end diff --git a/spec/support/migrate.rb b/spec/support/migrate.rb index eb1f84f..1a24fbd 100644 --- a/spec/support/migrate.rb +++ b/spec/support/migrate.rb @@ -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 diff --git a/spec/support/models/article.rb b/spec/support/models/article.rb index 85e8d8c..6485453 100644 --- a/spec/support/models/article.rb +++ b/spec/support/models/article.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true class Article < ::ActiveRecord::Base - rating + rating scoping: :categories + + has_many :categories end diff --git a/spec/support/models/category.rb b/spec/support/models/category.rb index cff22d3..8230753 100644 --- a/spec/support/models/category.rb +++ b/spec/support/models/category.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true class Category < ::ActiveRecord::Base + belongs_to :article end diff --git a/spec/support/models/comment.rb b/spec/support/models/comment.rb new file mode 100644 index 0000000..2b2e650 --- /dev/null +++ b/spec/support/models/comment.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class Comment < ::ActiveRecord::Base + rating +end