rating 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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