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 +5 -5
- data/CHANGELOG.md +6 -0
- data/README.md +18 -0
- data/lib/rating/models/rating/extension.rb +6 -2
- data/lib/rating/models/rating/rating.rb +26 -16
- data/lib/rating/version.rb +1 -1
- data/spec/factories/global.rb +6 -0
- data/spec/models/extension/unscoped_rating_spec.rb +121 -0
- data/spec/support/db/migrate/create_categories_table.rb +1 -0
- data/spec/support/db/migrate/create_globals_table.rb +8 -0
- data/spec/support/migrate.rb +3 -0
- data/spec/support/models/category.rb +1 -0
- data/spec/support/models/global.rb +7 -0
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6a9d42d0b74d27eec90696480e08f53c4ff5cfd8df12bf52b8fe7d993b483479
|
4
|
+
data.tar.gz: fcbaeb118ff6e0c37fcc7ea8d8d9a658e52e4e712bd5251fe948a4ad81a34878
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cad83ae5235eb83be5fb3fbd729391e9919430d169236f04c5a3a68eeb21e58aece8ef067f72cd513df75103c1777d61c3f51f0e5d0edcff9b19eeb8203ac2b6
|
7
|
+
data.tar.gz: 0da9112657b71a5f5af0abc739d75f780f0a3d3501de94c189ae787074284aae5850bf48f3fe3c8f40d9acf573019acfa5d5a704a307ba0da4882d37353b46ea
|
data/CHANGELOG.md
CHANGED
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(
|
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
|
22
|
-
distinct_count = how_many_resource_received_votes_sql
|
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
|
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 = ?
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
data/lib/rating/version.rb
CHANGED
@@ -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
|
data/spec/support/migrate.rb
CHANGED
@@ -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
|
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.
|
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-
|
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.
|
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
|