rating 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c384b931b0e023652ca3ae1025de72e2371f5c21
4
- data.tar.gz: 01cefd2f71309f2a9a1dc09f01f058cc00322619
2
+ SHA256:
3
+ metadata.gz: 6a9d42d0b74d27eec90696480e08f53c4ff5cfd8df12bf52b8fe7d993b483479
4
+ data.tar.gz: fcbaeb118ff6e0c37fcc7ea8d8d9a658e52e4e712bd5251fe948a4ad81a34878
5
5
  SHA512:
6
- metadata.gz: 98af2a274b9fc2cca342546016ba431d58e718463ac773ec02f99bdb7830634e08a8fd8cadffba68ac3e047eba14407c29787dd913192b730b3d98a4b2bffd50
7
- data.tar.gz: 4f8a8ae57dc1e14719739b3defbf850abb606c4d37d36d71922b11d91776ccd65866c9f2f17819ba5b9d046f91551ef70e216b9e28fd8b1fdd0cc437af36e12d
6
+ metadata.gz: cad83ae5235eb83be5fb3fbd729391e9919430d169236f04c5a3a68eeb21e58aece8ef067f72cd513df75103c1777d61c3f51f0e5d0edcff9b19eeb8203ac2b6
7
+ data.tar.gz: 0da9112657b71a5f5af0abc739d75f780f0a3d3501de94c189ae787074284aae5850bf48f3fe3c8f40d9acf573019acfa5d5a704a307ba0da4882d37353b46ea
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## v0.8.0
2
+
3
+ ### News
4
+
5
+ - Adds `unscoped_rating` option to calculate the rating counting all resource record ignoring the scope.
6
+
1
7
  ## v0.7.0
2
8
 
3
9
  ### News
data/README.md CHANGED
@@ -392,6 +392,24 @@ rating:
392
392
  - scope_2
393
393
  ```
394
394
 
395
+ ### Unscoped Rating
396
+
397
+ All rating values are grouped by the scope, but you can disable it and group all of them.
398
+
399
+ ```ruby
400
+ rating unscoped_rating: true
401
+
402
+ author = User.last
403
+ resource = Article.last
404
+ scope = Category.last
405
+
406
+ author.rate resource, 1, scope: scope
407
+ author.rate resource, 2, scope: scope
408
+ author.rate resource, 3
409
+ ```
410
+
411
+ Now the `sum` will be `6` and the `total` will be `3` because all rating will be calculated into just one rating record ignoring the `scopeable` object. The rating record is always saved on the record with `scopeable` as `nil`.
412
+
395
413
  ## Love it!
396
414
 
397
415
  Via [PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=X8HEP2878NDEG&item_name=rating) or [Patreon](https://www.patreon.com/wbotelhos). Thanks! (:
@@ -55,8 +55,8 @@ module Rating
55
55
  end
56
56
 
57
57
  module ClassMethods
58
- def rating(as: nil, scoping: nil)
59
- after_create -> { rating_warm_up scoping: scoping }, unless: -> { as == :author }
58
+ def rating(options = {})
59
+ after_create -> { rating_warm_up scoping: options[:scoping] }, unless: -> { options[:as] == :author }
60
60
 
61
61
  has_many :rating_records,
62
62
  as: :resource,
@@ -78,6 +78,10 @@ module Rating
78
78
  .where(Rating.table_name => { scopeable_id: scope&.id, scopeable_type: scope&.class&.base_class&.name })
79
79
  .order("#{Rating.table_name}.#{column} #{direction}")
80
80
  }
81
+
82
+ define_method :rating_options do
83
+ options
84
+ end
81
85
  end
82
86
  end
83
87
  end
@@ -18,8 +18,8 @@ module Rating
18
18
 
19
19
  class << self
20
20
  def averager_data(resource, scopeable)
21
- total_count = how_many_resource_received_votes_sql?(distinct: false, scopeable: scopeable)
22
- distinct_count = how_many_resource_received_votes_sql?(distinct: true, scopeable: scopeable)
21
+ total_count = how_many_resource_received_votes_sql(distinct: false, resource: resource, scopeable: scopeable)
22
+ distinct_count = how_many_resource_received_votes_sql(distinct: true, resource: resource, scopeable: scopeable)
23
23
  values = { resource_type: resource.class.base_class.name }
24
24
 
25
25
  values[:scopeable_type] = scopeable.class.base_class.name unless scopeable.nil?
@@ -29,7 +29,7 @@ module Rating
29
29
  (CAST(#{total_count} AS DECIMAL(17, 14)) / #{distinct_count}) count_avg,
30
30
  COALESCE(AVG(value), 0) rating_avg
31
31
  FROM #{rate_table_name}
32
- WHERE resource_type = :resource_type AND #{scope_type_query(scopeable)}
32
+ WHERE resource_type = :resource_type #{scope_type_query(resource, scopeable)}
33
33
  ).squish
34
34
 
35
35
  execute_sql [sql, values]
@@ -48,29 +48,26 @@ module Rating
48
48
  end
49
49
 
50
50
  def values_data(resource, scopeable)
51
- scope_query = if scopeable.nil?
52
- 'scopeable_type is NULL AND scopeable_id is NULL'
53
- else
54
- 'scopeable_type = ? AND scopeable_id = ?'
55
- end
56
-
57
51
  sql = %(
58
52
  SELECT
59
53
  COALESCE(AVG(value), 0) rating_avg,
60
54
  COALESCE(SUM(value), 0) rating_sum,
61
55
  COUNT(1) rating_count
62
56
  FROM #{rate_table_name}
63
- WHERE resource_type = ? AND resource_id = ? AND #{scope_query}
57
+ WHERE resource_type = ? AND resource_id = ? #{scope_type_and_id_query(resource, scopeable)}
64
58
  ).squish
65
59
 
66
60
  values = [sql, resource.class.base_class.name, resource.id]
67
- values += [scopeable.class.base_class.name, scopeable.id] unless scopeable.nil?
61
+ values += [scopeable.class.base_class.name, scopeable.id] unless scopeable.nil? || unscoped_rating?(resource)
68
62
 
69
63
  execute_sql values
70
64
  end
71
65
 
72
66
  def update_rating(resource, scopeable)
73
- record = find_or_initialize_by(resource: resource, scopeable: scopeable)
67
+ attributes = { resource: resource }
68
+ attributes[:scopeable] = unscoped_rating?(resource) ? nil : scopeable
69
+
70
+ record = find_or_initialize_by(attributes)
74
71
  result = data(resource, scopeable)
75
72
 
76
73
  record.average = result[:average]
@@ -97,13 +94,17 @@ module Rating
97
94
  Rate.find_by_sql(sql).first
98
95
  end
99
96
 
100
- def how_many_resource_received_votes_sql?(distinct:, scopeable:)
97
+ def unscoped_rating?(resource)
98
+ resource.rating_options[:unscoped_rating]
99
+ end
100
+
101
+ def how_many_resource_received_votes_sql(distinct:, resource:, scopeable:)
101
102
  count = distinct ? 'COUNT(DISTINCT resource_id)' : 'COUNT(1)'
102
103
 
103
104
  %((
104
105
  SELECT GREATEST(#{count}, 1)
105
106
  FROM #{rate_table_name}
106
- WHERE resource_type = :resource_type AND #{scope_type_query(scopeable)}
107
+ WHERE resource_type = :resource_type #{scope_type_query(resource, scopeable)}
107
108
  ))
108
109
  end
109
110
 
@@ -111,8 +112,17 @@ module Rating
111
112
  @rate_table_name ||= Rate.table_name
112
113
  end
113
114
 
114
- def scope_type_query(scopeable)
115
- scopeable.nil? ? 'scopeable_type is NULL' : 'scopeable_type = :scopeable_type'
115
+ def scope_type_query(resource, scopeable)
116
+ return '' if unscoped_rating?(resource)
117
+
118
+ scopeable.nil? ? 'AND scopeable_type is NULL' : 'AND scopeable_type = :scopeable_type'
119
+ end
120
+
121
+ def scope_type_and_id_query(resource, scopeable)
122
+ return '' if unscoped_rating?(resource)
123
+ return 'AND scopeable_type is NULL AND scopeable_id is NULL' if scopeable.nil?
124
+
125
+ 'AND scopeable_type = ? AND scopeable_id = ?'
116
126
  end
117
127
  end
118
128
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rating
4
- VERSION = '0.7.0'
4
+ VERSION = '0.8.0'
5
5
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :global do
5
+ end
6
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Rating::Extension, 'unscoped_rating' do
6
+ let!(:author_1) { create :author }
7
+ let!(:author_2) { create :author }
8
+ let!(:scope) { create :category }
9
+
10
+ context 'when is false' do
11
+ let!(:resource) { create :article }
12
+
13
+ it 'groups in different line record' do
14
+ author_1.rate resource, 1, scope: scope
15
+ author_2.rate resource, 2, scope: scope
16
+ author_1.rate resource, 5
17
+
18
+ ratings = Rating::Rating.all.order('id')
19
+
20
+ expect(ratings.size).to eq 2
21
+
22
+ rating = ratings[0]
23
+
24
+ expect(rating.average.to_s).to eq '1.5'
25
+ expect(rating.estimate.to_s).to eq '1.5'
26
+ expect(rating.resource).to eq resource
27
+ expect(rating.scopeable).to eq scope
28
+ expect(rating.sum).to eq 3
29
+ expect(rating.total).to eq 2
30
+
31
+ rating = ratings[1]
32
+
33
+ expect(rating.average.to_s).to eq '5.0'
34
+ expect(rating.estimate.to_s).to eq '5.0'
35
+ expect(rating.resource).to eq resource
36
+ expect(rating.scopeable).to eq nil
37
+ expect(rating.sum).to eq 5
38
+ expect(rating.total).to eq 1
39
+ end
40
+ end
41
+
42
+ context 'when is true' do
43
+ let!(:resource) { create :global }
44
+
45
+ it 'groups in the same line record' do
46
+ author_1.rate resource, 1, scope: scope
47
+ author_2.rate resource, 2, scope: scope
48
+ author_1.rate resource, 5
49
+
50
+ ratings = Rating::Rating.all.order('id')
51
+
52
+ expect(ratings.size).to eq 1
53
+
54
+ rating = ratings[0]
55
+
56
+ expect(rating.average.to_s).to eq '2.6666666666666667'
57
+ expect(rating.estimate.to_s).to eq '2.6666666666666667'
58
+ expect(rating.resource).to eq resource
59
+ expect(rating.scopeable).to eq nil
60
+ expect(rating.sum).to eq 8
61
+ expect(rating.total).to eq 3
62
+ end
63
+ end
64
+
65
+ context 'when is true' do
66
+ let!(:resource) { create :global }
67
+
68
+ it 'groups in the same line record' do
69
+ author_1.rate resource, 1, scope: scope
70
+ author_2.rate resource, 2, scope: scope
71
+ author_1.rate resource, 5
72
+
73
+ ratings = Rating::Rating.all.order('id')
74
+
75
+ expect(ratings.size).to eq 1
76
+
77
+ rating = ratings[0]
78
+
79
+ expect(rating.average.to_s).to eq '2.6666666666666667'
80
+ expect(rating.estimate.to_s).to eq '2.6666666666666667'
81
+ expect(rating.resource).to eq resource
82
+ expect(rating.scopeable).to eq nil
83
+ expect(rating.sum).to eq 8
84
+ expect(rating.total).to eq 3
85
+ end
86
+ end
87
+
88
+ context 'when is true and have a non scopeable record first on dabase' do
89
+ let!(:resource) { create :global }
90
+
91
+ before { ::Rating::Rating.create resource: resource, scopeable: scope }
92
+
93
+ it 'groups in the line with no scope' do
94
+ author_1.rate resource, 1, scope: scope
95
+ author_2.rate resource, 2, scope: scope
96
+ author_1.rate resource, 5
97
+
98
+ ratings = Rating::Rating.all.order('id')
99
+
100
+ expect(ratings.size).to eq 2
101
+
102
+ rating = ratings[0]
103
+
104
+ expect(rating.average.to_s).to eq '0.0'
105
+ expect(rating.estimate.to_s).to eq '0.0'
106
+ expect(rating.resource).to eq resource
107
+ expect(rating.scopeable).to eq scope
108
+ expect(rating.sum).to eq 0
109
+ expect(rating.total).to eq 0
110
+
111
+ rating = ratings[1]
112
+
113
+ expect(rating.average.to_s).to eq '2.6666666666666667'
114
+ expect(rating.estimate.to_s).to eq '2.6666666666666667'
115
+ expect(rating.resource).to eq resource
116
+ expect(rating.scopeable).to eq nil
117
+ expect(rating.sum).to eq 8
118
+ expect(rating.total).to eq 3
119
+ end
120
+ end
121
+ end
@@ -6,6 +6,7 @@ class CreateCategoriesTable < ActiveRecord::Migration[5.0]
6
6
  t.string :name, null: false
7
7
 
8
8
  t.references :article, foreign_key: true, index: true
9
+ t.references :global, foreign_key: true, index: true
9
10
  end
10
11
  end
11
12
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateGlobalsTable < ActiveRecord::Migration[5.0]
4
+ def change
5
+ create_table :globals do |t|
6
+ end
7
+ end
8
+ end
@@ -7,7 +7,10 @@ Dir[File.expand_path('db/migrate/*.rb', __dir__)].each { |file| require file }
7
7
 
8
8
  CreateArticlesTable.new.change
9
9
  CreateAuthorsTable.new.change
10
+
11
+ CreateGlobalsTable.new.change
10
12
  CreateCategoriesTable.new.change
13
+
11
14
  CreateCommentsTable.new.change
12
15
  CreateRateTable.new.change
13
16
  CreateRatingTable.new.change
@@ -2,4 +2,5 @@
2
2
 
3
3
  class Category < ::ActiveRecord::Base
4
4
  belongs_to :article
5
+ belongs_to :global
5
6
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Global < ::ActiveRecord::Base
4
+ rating scoping: :categories, unscoped_rating: true
5
+
6
+ has_many :categories
7
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rating
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Washington Botelho
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-02 00:00:00.000000000 Z
11
+ date: 2018-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -153,6 +153,7 @@ files:
153
153
  - spec/factories/author.rb
154
154
  - spec/factories/category.rb
155
155
  - spec/factories/comment.rb
156
+ - spec/factories/global.rb
156
157
  - spec/factories/rating/rate.rb
157
158
  - spec/factories/rating/rating.rb
158
159
  - spec/models/extension/after_create_spec.rb
@@ -167,6 +168,7 @@ files:
167
168
  - spec/models/extension/rating_records_spec.rb
168
169
  - spec/models/extension/rating_spec.rb
169
170
  - spec/models/extension/rating_warm_up_spec.rb
171
+ - spec/models/extension/unscoped_rating_spec.rb
170
172
  - spec/models/rate/create_spec.rb
171
173
  - spec/models/rate/rate_for_spec.rb
172
174
  - spec/models/rate_spec.rb
@@ -184,6 +186,7 @@ files:
184
186
  - spec/support/db/migrate/create_authors_table.rb
185
187
  - spec/support/db/migrate/create_categories_table.rb
186
188
  - spec/support/db/migrate/create_comments_table.rb
189
+ - spec/support/db/migrate/create_globals_table.rb
187
190
  - spec/support/db/migrate/create_review_ratings_table.rb
188
191
  - spec/support/db/migrate/create_reviews_table.rb
189
192
  - spec/support/factory_bot.rb
@@ -192,6 +195,7 @@ files:
192
195
  - spec/support/models/author.rb
193
196
  - spec/support/models/category.rb
194
197
  - spec/support/models/comment.rb
198
+ - spec/support/models/global.rb
195
199
  - spec/support/models/review.rb
196
200
  - spec/support/models/review_rating.rb
197
201
  - spec/support/shared_context/with_database_records.rb
@@ -216,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
216
220
  version: '0'
217
221
  requirements: []
218
222
  rubyforge_project:
219
- rubygems_version: 2.6.14
223
+ rubygems_version: 2.7.3
220
224
  signing_key:
221
225
  specification_version: 4
222
226
  summary: A true Bayesian rating system with scope and cache enabled.
@@ -235,6 +239,7 @@ test_files:
235
239
  - spec/models/extension/rated_question_spec.rb
236
240
  - spec/models/extension/rating_warm_up_spec.rb
237
241
  - spec/models/extension/order_by_rating_spec.rb
242
+ - spec/models/extension/unscoped_rating_spec.rb
238
243
  - spec/models/extension/rate_spec.rb
239
244
  - spec/models/extension/rate_for_spec.rb
240
245
  - spec/models/extension/after_create_spec.rb
@@ -252,6 +257,7 @@ test_files:
252
257
  - spec/support/models/author.rb
253
258
  - spec/support/models/review.rb
254
259
  - spec/support/models/comment.rb
260
+ - spec/support/models/global.rb
255
261
  - spec/support/models/review_rating.rb
256
262
  - spec/support/common.rb
257
263
  - spec/support/db/migrate/create_categories_table.rb
@@ -262,6 +268,7 @@ test_files:
262
268
  - spec/support/db/migrate/add_extra_fields_on_rating_rates_table.rb
263
269
  - spec/support/db/migrate/create_reviews_table.rb
264
270
  - spec/support/db/migrate/create_comments_table.rb
271
+ - spec/support/db/migrate/create_globals_table.rb
265
272
  - spec/support/database_cleaner.rb
266
273
  - spec/support/shared_context/with_database_records.rb
267
274
  - spec/factories/article.rb
@@ -270,4 +277,5 @@ test_files:
270
277
  - spec/factories/rating/rating.rb
271
278
  - spec/factories/author.rb
272
279
  - spec/factories/comment.rb
280
+ - spec/factories/global.rb
273
281
  - spec/rails_helper.rb