voting 0.1.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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/LICENSE +21 -0
  4. data/README.md +229 -0
  5. data/lib/generators/voting/install_generator.rb +19 -0
  6. data/lib/generators/voting/templates/db/migrate/create_voting_tables.rb +35 -0
  7. data/lib/voting.rb +10 -0
  8. data/lib/voting/models/voting/extension.rb +85 -0
  9. data/lib/voting/models/voting/vote.rb +45 -0
  10. data/lib/voting/models/voting/voting.rb +73 -0
  11. data/lib/voting/version.rb +5 -0
  12. data/spec/factories/article.rb +5 -0
  13. data/spec/factories/author.rb +5 -0
  14. data/spec/factories/category.rb +5 -0
  15. data/spec/factories/comment.rb +5 -0
  16. data/spec/factories/voting/vote.rb +8 -0
  17. data/spec/factories/voting/voting.rb +9 -0
  18. data/spec/models/extension/after_save_spec.rb +37 -0
  19. data/spec/models/extension/as_spec.rb +26 -0
  20. data/spec/models/extension/down_spec.rb +26 -0
  21. data/spec/models/extension/order_by_voting_spec.rb +94 -0
  22. data/spec/models/extension/up_spec.rb +26 -0
  23. data/spec/models/extension/vote_for_spec.rb +26 -0
  24. data/spec/models/extension/vote_spec.rb +26 -0
  25. data/spec/models/extension/voted_question_spec.rb +38 -0
  26. data/spec/models/extension/voted_records_spec.rb +12 -0
  27. data/spec/models/extension/voted_spec.rb +40 -0
  28. data/spec/models/extension/votes_records_spec.rb +12 -0
  29. data/spec/models/extension/votes_spec.rb +40 -0
  30. data/spec/models/extension/voting_records_spec.rb +12 -0
  31. data/spec/models/extension/voting_spec.rb +40 -0
  32. data/spec/models/extension/voting_warm_up_spec.rb +115 -0
  33. data/spec/models/vote/create_spec.rb +273 -0
  34. data/spec/models/vote/vote_for_spec.rb +40 -0
  35. data/spec/models/vote_spec.rb +27 -0
  36. data/spec/models/voting/update_voting_spec.rb +28 -0
  37. data/spec/models/voting/values_data_spec.rb +24 -0
  38. data/spec/models/voting_spec.rb +26 -0
  39. data/spec/rails_helper.rb +11 -0
  40. data/spec/support/common.rb +22 -0
  41. data/spec/support/database_cleaner.rb +19 -0
  42. data/spec/support/db/migrate/create_articles_table.rb +7 -0
  43. data/spec/support/db/migrate/create_authors_table.rb +7 -0
  44. data/spec/support/db/migrate/create_categories_table.rb +9 -0
  45. data/spec/support/db/migrate/create_comments_table.rb +7 -0
  46. data/spec/support/factory_bot.rb +9 -0
  47. data/spec/support/migrate.rb +11 -0
  48. data/spec/support/models/article.rb +7 -0
  49. data/spec/support/models/author.rb +5 -0
  50. data/spec/support/models/category.rb +5 -0
  51. data/spec/support/models/comment.rb +5 -0
  52. data/spec/support/shared_context/with_database_records.rb +22 -0
  53. data/spec/support/shoulda.rb +10 -0
  54. metadata +257 -0
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require 'support/shared_context/with_database_records'
5
+
6
+ RSpec.describe Voting::Extension, ':voted' do
7
+ include_context 'with_database_records'
8
+
9
+ context 'with no scope' do
10
+ it 'returns votes made by this author' do
11
+ expect(author_1.voted).to match_array [vote_1, vote_4]
12
+ end
13
+ end
14
+
15
+ context 'with scope' do
16
+ it 'returns scoped votes made by this author' do
17
+ expect(author_1.voted(scope: category)).to eq [vote_7]
18
+ end
19
+ end
20
+
21
+ context 'when destroy author' do
22
+ it 'destroys votes of that author' do
23
+ expect(Voting::Vote.where(author: author_1).count).to eq 3
24
+
25
+ author_1.destroy!
26
+
27
+ expect(Voting::Vote.where(author: author_1).count).to eq 0
28
+ end
29
+ end
30
+
31
+ context 'when destroy resource voted by author' do
32
+ it 'destroys votes of that resource' do
33
+ expect(Voting::Vote.where(resource: comment_1).count).to eq 5
34
+
35
+ comment_1.destroy!
36
+
37
+ expect(Voting::Vote.where(resource: comment_1).count).to eq 0
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require 'support/shared_context/with_database_records'
5
+
6
+ RSpec.describe Voting::Extension, '.votes_records' do
7
+ include_context 'with_database_records'
8
+
9
+ it 'returns all votes that this resource received' do
10
+ expect(comment_1.votes_records).to match_array [vote_1, vote_2, vote_3, vote_7, vote_8]
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require 'support/shared_context/with_database_records'
5
+
6
+ RSpec.describe Voting::Extension, ':votes' do
7
+ include_context 'with_database_records'
8
+
9
+ context 'with no scope' do
10
+ it 'returns votes that this resource received' do
11
+ expect(comment_1.votes).to match_array [vote_1, vote_2, vote_3]
12
+ end
13
+ end
14
+
15
+ context 'with scope' do
16
+ it 'returns scoped votes that this resource received' do
17
+ expect(comment_1.votes(scope: category)).to match_array [vote_7, vote_8]
18
+ end
19
+ end
20
+
21
+ context 'when destroy author' do
22
+ it 'destroys votes of that author' do
23
+ expect(Voting::Vote.where(author: author_1).count).to eq 3
24
+
25
+ author_1.destroy!
26
+
27
+ expect(Voting::Vote.where(author: author_1).count).to eq 0
28
+ end
29
+ end
30
+
31
+ context 'when destroy resource' do
32
+ it 'destroys votes of that resource' do
33
+ expect(Voting::Vote.where(resource: comment_1).count).to eq 5
34
+
35
+ comment_1.destroy!
36
+
37
+ expect(Voting::Vote.where(resource: comment_1).count).to eq 0
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require 'support/shared_context/with_database_records'
5
+
6
+ RSpec.describe Voting::Extension, '.voting' do
7
+ include_context 'with_database_records'
8
+
9
+ it 'returns all voting of this resource' do
10
+ expect(comment_1.voting_records).to match_array Voting::Voting.where(resource: comment_1)
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require 'support/shared_context/with_database_records'
5
+
6
+ RSpec.describe Voting::Extension, '.voting' do
7
+ include_context 'with_database_records'
8
+
9
+ context 'with no scope' do
10
+ it 'returns voting record' do
11
+ expect(comment_1.voting).to eq Voting::Voting.find_by(resource: comment_1, scopeable: nil)
12
+ end
13
+ end
14
+
15
+ context 'with scope' do
16
+ it 'returns scoped voting record' do
17
+ expect(comment_1.voting(scope: category)).to eq Voting::Voting.find_by(resource: comment_1, scopeable: category)
18
+ end
19
+ end
20
+
21
+ context 'when destroy author' do
22
+ it 'does not destroy resource voting' do
23
+ expect(Voting::Voting.where(resource: comment_1).count).to eq 2
24
+
25
+ author_1.destroy!
26
+
27
+ expect(Voting::Voting.where(resource: comment_1).count).to eq 2
28
+ end
29
+ end
30
+
31
+ context 'when destroy resource' do
32
+ it 'destroys resource voting too' do
33
+ expect(Voting::Voting.where(resource: comment_1).count).to eq 2
34
+
35
+ comment_1.destroy!
36
+
37
+ expect(Voting::Voting.where(resource: comment_1).count).to eq 0
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Voting::Extension, '.voting_warm_up' do
6
+ context 'when scoping is nil' do
7
+ context 'when update is made' do
8
+ let!(:record) { create :comment }
9
+ let!(:voting) { ::Voting::Voting.find_by resource: record }
10
+
11
+ it 'creates the cache' do
12
+ record.voting_warm_up scoping: nil
13
+
14
+ expect(::Voting::Voting.all).to eq [voting]
15
+ end
16
+
17
+ it 'returns the cached object' do
18
+ expect(record.voting_warm_up).to eq voting
19
+ end
20
+ end
21
+
22
+ context 'when record does not exist' do
23
+ let!(:record) { create :comment }
24
+
25
+ before { ::Voting::Voting.destroy_all }
26
+
27
+ it 'creates the cache' do
28
+ record.voting_warm_up scoping: nil
29
+
30
+ expect(::Voting::Voting.all.map(&:resource)).to eq [record]
31
+ end
32
+
33
+ it 'returns the cached object' do
34
+ expect(record.voting_warm_up).to eq ::Voting::Voting.last
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'when scoping is not nil' do
40
+ context 'when update is made' do
41
+ let!(:category_1) { create :category }
42
+ let!(:category_2) { create :category }
43
+ let!(:record) { create :article, categories: [category_1, category_2] }
44
+
45
+ it 'creates the cache' do
46
+ record.voting_warm_up scoping: :categories
47
+
48
+ votings = ::Voting::Voting.all
49
+
50
+ expect(votings.map(&:scopeable)).to match_array [category_1, category_2]
51
+ expect(votings.map(&:resource)).to match_array [record, record]
52
+ end
53
+
54
+ it 'returns the cached objects' do
55
+ expect(record.voting_warm_up(scoping: :categories)).to eq ::Voting::Voting.all
56
+ end
57
+ end
58
+
59
+ context 'when record does not exist' do
60
+ let!(:category_1) { create :category }
61
+ let!(:category_2) { create :category }
62
+ let!(:record) { create :article, categories: [category_1, category_2] }
63
+
64
+ before { ::Voting::Voting.destroy_all }
65
+
66
+ it 'creates the cache' do
67
+ record.voting_warm_up scoping: :categories
68
+
69
+ votings = ::Voting::Voting.all
70
+
71
+ expect(votings.map(&:scopeable)).to match_array [category_1, category_2]
72
+ expect(votings.map(&:resource)).to match_array [record, record]
73
+ end
74
+
75
+ it 'returns the cached objects' do
76
+ expect(record.voting_warm_up(scoping: :categories)).to eq ::Voting::Voting.all
77
+ end
78
+ end
79
+
80
+ context 'when a non existent scoping is given' do
81
+ let!(:record) { create :article }
82
+
83
+ it 'returns an empty array' do
84
+ expect(record.voting_warm_up(scoping: :missing)).to eq []
85
+ end
86
+ end
87
+
88
+ context 'when scoping is given inside array' do
89
+ let!(:category) { create :category }
90
+ let!(:record) { create :article, categories: [category] }
91
+
92
+ it 'returns the cache' do
93
+ expect(record.voting_warm_up(scoping: [:categories])).to eq ::Voting::Voting.all
94
+ end
95
+ end
96
+
97
+ context 'when scoping is given inside multiple arrays' do
98
+ let!(:category) { create :category }
99
+ let!(:record) { create :article, categories: [category] }
100
+
101
+ it 'returns the cache' do
102
+ expect(record.voting_warm_up(scoping: [[:categories]])).to eq ::Voting::Voting.all
103
+ end
104
+ end
105
+
106
+ context 'when scoping is given with nil value together' do
107
+ let!(:category) { create :category }
108
+ let!(:record) { create :article, categories: [category] }
109
+
110
+ it 'returns the cache' do
111
+ expect(record.voting_warm_up(scoping: [[:categories, nil], nil])).to eq ::Voting::Voting.all
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,273 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Voting::Vote, ':create' do
6
+ let!(:author) { create :author }
7
+
8
+ context 'with no scopeable' do
9
+ let!(:resource) { create :comment }
10
+
11
+ context 'when vote does not exist yet' do
12
+ it 'creates a vote entry' do
13
+ described_class.create author: author, resource: resource, value: 1
14
+
15
+ vote = described_class.last
16
+
17
+ expect(vote.author).to eq author
18
+ expect(vote.negative).to eq 0
19
+ expect(vote.positive).to eq 1
20
+ expect(vote.resource).to eq resource
21
+ end
22
+
23
+ it 'creates a voting entry' do
24
+ described_class.create author: author, resource: resource, value: 1
25
+
26
+ voting = Voting::Voting.last
27
+
28
+ expect(voting.estimate).to eq 0.2065432914738929
29
+ expect(voting.negative).to eq 0
30
+ expect(voting.positive).to eq 1
31
+ expect(voting.resource).to eq resource
32
+ end
33
+ end
34
+
35
+ context 'when vote already exists by other author' do
36
+ let!(:author_other) { create :author }
37
+
38
+ before { described_class.create author: author_other, resource: resource, value: -1 }
39
+
40
+ it 'creates one more vote entry' do
41
+ described_class.create author: author, resource: resource, value: 1
42
+
43
+ votes = described_class.all.order('created_at asc')
44
+
45
+ expect(votes.size).to eq 2
46
+
47
+ vote = votes[0]
48
+
49
+ expect(vote.author).to eq author_other
50
+ expect(vote.negative).to eq 1
51
+ expect(vote.positive).to eq 0
52
+ expect(vote.resource).to eq resource
53
+
54
+ vote = votes[1]
55
+
56
+ expect(vote.author).to eq author
57
+ expect(vote.negative).to eq 0
58
+ expect(vote.positive).to eq 1
59
+ expect(vote.resource).to eq resource
60
+ end
61
+
62
+ it 'updates the unique voting entry' do
63
+ described_class.create author: author, resource: resource, value: 1
64
+
65
+ voting = Voting::Voting.all
66
+
67
+ expect(voting.size).to eq 1
68
+
69
+ expect(voting[0].estimate).to eq 0.1711859764448097
70
+ expect(voting[0].negative).to eq 1
71
+ expect(voting[0].positive).to eq 1
72
+ expect(voting[0].resource).to eq resource
73
+ end
74
+ end
75
+
76
+ context 'when vote already exists by the same author' do
77
+ before { described_class.create author: author, resource: resource, value: -1 }
78
+
79
+ context 'when author vote with a different rate' do
80
+ it 'changes the actual vote' do
81
+ described_class.create author: author, resource: resource, value: 1
82
+
83
+ votes = described_class.all.order('created_at asc')
84
+
85
+ expect(votes.size).to eq 1
86
+
87
+ expect(votes[0].author).to eq author
88
+ expect(votes[0].negative).to eq 0
89
+ expect(votes[0].positive).to eq 1
90
+ expect(votes[0].resource).to eq resource
91
+ end
92
+
93
+ it 'updates the unique voting entry' do
94
+ described_class.create author: author, resource: resource, value: 1
95
+
96
+ voting = Voting::Voting.all
97
+
98
+ expect(voting.size).to eq 1
99
+
100
+ expect(voting[0].estimate).to eq 0.2065432914738929
101
+ expect(voting[0].negative).to eq 0
102
+ expect(voting[0].positive).to eq 1
103
+ expect(voting[0].resource).to eq resource
104
+ end
105
+ end
106
+
107
+ context 'when author vote with the same rate' do
108
+ it 'makes the vote be zero' do
109
+ described_class.create author: author, resource: resource, value: -1
110
+
111
+ votes = described_class.all.order('created_at asc')
112
+
113
+ expect(votes.size).to eq 1
114
+
115
+ expect(votes[0].author).to eq author
116
+ expect(votes[0].negative).to eq 0
117
+ expect(votes[0].positive).to eq 0
118
+ expect(votes[0].resource).to eq resource
119
+ end
120
+
121
+ it 'updates the unique voting entry' do
122
+ described_class.create author: author, resource: resource, value: -1
123
+
124
+ voting = Voting::Voting.all
125
+
126
+ expect(voting.size).to eq 1
127
+
128
+ expect(voting[0].estimate).to eq 0
129
+ expect(voting[0].negative).to eq 0
130
+ expect(voting[0].positive).to eq 0
131
+ expect(voting[0].resource).to eq resource
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ context 'with scopeable' do
138
+ let!(:scopeable) { create :category }
139
+ let!(:resource) { create :article }
140
+
141
+ context 'when vote does not exist yet' do
142
+ it 'creates a vote entry' do
143
+ described_class.create author: author, resource: resource, scopeable: scopeable, value: 1
144
+
145
+ vote = described_class.last
146
+
147
+ expect(vote.author).to eq author
148
+ expect(vote.negative).to eq 0
149
+ expect(vote.positive).to eq 1
150
+ expect(vote.resource).to eq resource
151
+ expect(vote.scopeable).to eq scopeable
152
+ end
153
+
154
+ it 'creates a voting entry' do
155
+ described_class.create author: author, resource: resource, scopeable: scopeable, value: 1
156
+
157
+ voting = Voting::Voting.last
158
+
159
+ expect(voting.estimate).to eq 0.2065432914738929
160
+ expect(voting.negative).to eq 0
161
+ expect(voting.positive).to eq 1
162
+ expect(voting.resource).to eq resource
163
+ expect(voting.scopeable).to eq scopeable
164
+ end
165
+ end
166
+
167
+ context 'when vote already exists by other author' do
168
+ let!(:author_other) { create :author }
169
+
170
+ before { described_class.create author: author_other, resource: resource, scopeable: scopeable, value: -1 }
171
+
172
+ it 'creates one more vote entry' do
173
+ described_class.create author: author, resource: resource, scopeable: scopeable, value: 1
174
+
175
+ votes = described_class.all.order('created_at asc')
176
+
177
+ expect(votes.size).to eq 2
178
+
179
+ vote = votes[0]
180
+
181
+ expect(vote.author).to eq author_other
182
+ expect(vote.negative).to eq 1
183
+ expect(vote.positive).to eq 0
184
+ expect(vote.resource).to eq resource
185
+ expect(vote.scopeable).to eq scopeable
186
+
187
+ vote = votes[1]
188
+
189
+ expect(vote.author).to eq author
190
+ expect(vote.negative).to eq 0
191
+ expect(vote.positive).to eq 1
192
+ expect(vote.resource).to eq resource
193
+ expect(vote.scopeable).to eq scopeable
194
+ end
195
+
196
+ it 'updates the unique voting entry' do
197
+ described_class.create author: author, resource: resource, scopeable: scopeable, value: 1
198
+
199
+ voting = Voting::Voting.all
200
+
201
+ expect(voting.size).to eq 1
202
+
203
+ expect(voting[0].estimate).to eq 0.1711859764448097
204
+ expect(voting[0].negative).to eq 1
205
+ expect(voting[0].positive).to eq 1
206
+ expect(voting[0].resource).to eq resource
207
+ expect(voting[0].scopeable).to eq scopeable
208
+ end
209
+ end
210
+
211
+ context 'when vote already exists by the same author' do
212
+ before { described_class.create author: author, resource: resource, scopeable: scopeable, value: -1 }
213
+
214
+ context 'when author vote with a different rate' do
215
+ it 'changes the actual vote' do
216
+ described_class.create author: author, resource: resource, scopeable: scopeable, value: 1
217
+
218
+ votes = described_class.all.order('created_at asc')
219
+
220
+ expect(votes.size).to eq 1
221
+
222
+ expect(votes[0].author).to eq author
223
+ expect(votes[0].negative).to eq 0
224
+ expect(votes[0].positive).to eq 1
225
+ expect(votes[0].resource).to eq resource
226
+ expect(votes[0].scopeable).to eq scopeable
227
+ end
228
+
229
+ it 'updates the unique voting entry' do
230
+ described_class.create author: author, resource: resource, scopeable: scopeable, value: 1
231
+
232
+ voting = Voting::Voting.all
233
+
234
+ expect(voting.size).to eq 1
235
+
236
+ expect(voting[0].estimate).to eq 0.2065432914738929
237
+ expect(voting[0].negative).to eq 0
238
+ expect(voting[0].positive).to eq 1
239
+ expect(voting[0].resource).to eq resource
240
+ expect(voting[0].scopeable).to eq scopeable
241
+ end
242
+ end
243
+
244
+ context 'when author vote with the same rate' do
245
+ it 'makes the vote be zero' do
246
+ described_class.create author: author, resource: resource, scopeable: scopeable, value: -1
247
+
248
+ votes = described_class.all.order('created_at asc')
249
+
250
+ expect(votes.size).to eq 1
251
+
252
+ expect(votes[0].author).to eq author
253
+ expect(votes[0].negative).to eq 0
254
+ expect(votes[0].positive).to eq 0
255
+ expect(votes[0].resource).to eq resource
256
+ end
257
+
258
+ it 'updates the unique voting entry' do
259
+ described_class.create author: author, resource: resource, scopeable: scopeable, value: -1
260
+
261
+ voting = Voting::Voting.all
262
+
263
+ expect(voting.size).to eq 1
264
+
265
+ expect(voting[0].estimate).to eq 0
266
+ expect(voting[0].negative).to eq 0
267
+ expect(voting[0].positive).to eq 0
268
+ expect(voting[0].resource).to eq resource
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end