ballot 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 (70) hide show
  1. checksums.yaml +7 -0
  2. data/Contributing.md +68 -0
  3. data/History.md +5 -0
  4. data/Licence.md +27 -0
  5. data/Manifest.txt +68 -0
  6. data/README.rdoc +264 -0
  7. data/Rakefile +71 -0
  8. data/bin/ballot_generator +9 -0
  9. data/lib/ballot.rb +25 -0
  10. data/lib/ballot/action_controller.rb +32 -0
  11. data/lib/ballot/active_record.rb +152 -0
  12. data/lib/ballot/active_record/votable.rb +145 -0
  13. data/lib/ballot/active_record/vote.rb +35 -0
  14. data/lib/ballot/active_record/voter.rb +99 -0
  15. data/lib/ballot/railtie.rb +19 -0
  16. data/lib/ballot/sequel.rb +170 -0
  17. data/lib/ballot/sequel/vote.rb +99 -0
  18. data/lib/ballot/votable.rb +445 -0
  19. data/lib/ballot/vote.rb +129 -0
  20. data/lib/ballot/voter.rb +320 -0
  21. data/lib/ballot/words.rb +32 -0
  22. data/lib/generators/ballot.rb +40 -0
  23. data/lib/generators/ballot/install/install_generator.rb +27 -0
  24. data/lib/generators/ballot/install/templates/active_record/migration.rb +19 -0
  25. data/lib/generators/ballot/install/templates/sequel/migration.rb +25 -0
  26. data/lib/generators/ballot/standalone.rb +89 -0
  27. data/lib/generators/ballot/standalone/support.rb +70 -0
  28. data/lib/generators/ballot/summary/summary_generator.rb +27 -0
  29. data/lib/generators/ballot/summary/templates/active_record/migration.rb +15 -0
  30. data/lib/generators/ballot/summary/templates/sequel/migration.rb +20 -0
  31. data/lib/sequel/plugins/ballot_votable.rb +180 -0
  32. data/lib/sequel/plugins/ballot_voter.rb +125 -0
  33. data/test/active_record/ballot_votable_test.rb +16 -0
  34. data/test/active_record/ballot_voter_test.rb +13 -0
  35. data/test/active_record/rails_generator_test.rb +28 -0
  36. data/test/active_record/votable_voter_test.rb +19 -0
  37. data/test/generators/rails-activerecord/Rakefile +2 -0
  38. data/test/generators/rails-activerecord/app/.keep +0 -0
  39. data/test/generators/rails-activerecord/bin/rails +5 -0
  40. data/test/generators/rails-activerecord/config/application.rb +17 -0
  41. data/test/generators/rails-activerecord/config/boot.rb +3 -0
  42. data/test/generators/rails-activerecord/config/database.yml +12 -0
  43. data/test/generators/rails-activerecord/config/environment.rb +3 -0
  44. data/test/generators/rails-activerecord/config/routes.rb +3 -0
  45. data/test/generators/rails-activerecord/config/secrets.yml +5 -0
  46. data/test/generators/rails-activerecord/db/seeds.rb +1 -0
  47. data/test/generators/rails-activerecord/log/.keep +0 -0
  48. data/test/generators/rails-sequel/Rakefile +2 -0
  49. data/test/generators/rails-sequel/app/.keep +0 -0
  50. data/test/generators/rails-sequel/bin/rails +5 -0
  51. data/test/generators/rails-sequel/config/application.rb +14 -0
  52. data/test/generators/rails-sequel/config/boot.rb +3 -0
  53. data/test/generators/rails-sequel/config/database.yml +12 -0
  54. data/test/generators/rails-sequel/config/environment.rb +3 -0
  55. data/test/generators/rails-sequel/config/routes.rb +3 -0
  56. data/test/generators/rails-sequel/config/secrets.yml +5 -0
  57. data/test/generators/rails-sequel/db/seeds.rb +1 -0
  58. data/test/generators/rails-sequel/log/.keep +0 -0
  59. data/test/minitest_config.rb +14 -0
  60. data/test/sequel/ballot_votable_test.rb +45 -0
  61. data/test/sequel/ballot_voter_test.rb +42 -0
  62. data/test/sequel/rails_generator_test.rb +25 -0
  63. data/test/sequel/votable_voter_test.rb +19 -0
  64. data/test/sequel/vote_test.rb +105 -0
  65. data/test/support/active_record_setup.rb +145 -0
  66. data/test/support/generators_setup.rb +129 -0
  67. data/test/support/sequel_setup.rb +164 -0
  68. data/test/support/shared_examples/votable_examples.rb +630 -0
  69. data/test/support/shared_examples/voter_examples.rb +600 -0
  70. metadata +333 -0
@@ -0,0 +1,630 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VotableExamples
4
+ def describe_votable_models(models, focus: false)
5
+ describe '.ballot_votable?' do
6
+ models.each do |model|
7
+ self.focus if focus && respond_to?(:focus)
8
+ it "#{model}.ballot_votable? is true" do
9
+ assert_true model.ballot_votable?
10
+ end
11
+
12
+ self.focus if focus && respond_to?(:focus)
13
+ it "#{model}#ballot_votable? is true" do
14
+ assert_true model.new.ballot_votable?
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ def describe_non_votable_models(models, focus: false)
21
+ describe '.ballot_votable?' do
22
+ models.each do |model|
23
+ self.focus if focus && respond_to?(:focus)
24
+ it "#{model}.ballot_votable? is false" do
25
+ assert_false model.ballot_votable?
26
+ end
27
+
28
+ self.focus if focus && respond_to?(:focus)
29
+ it "#{model}.ballot_votable? is false" do
30
+ assert_false model.new.ballot_votable?
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def describe_ballot_votable(klass = nil, focus: false, &block)
37
+ msg = 'a votable model'
38
+ klass && msg = "#{klass} is #{msg}"
39
+
40
+ describe msg do
41
+ instance_exec(&block) if block
42
+
43
+ describe '#ballot_by' do
44
+ self.focus if focus && respond_to?(:focus)
45
+ it 'is false when no voter is provided' do
46
+ assert_false votable.ballot_by
47
+ end
48
+
49
+ self.focus if focus && respond_to?(:focus)
50
+ it 'is false when the voter is not a model' do
51
+ assert_false votable.ballot_by voter: Class.new
52
+ end
53
+
54
+ self.focus if focus && respond_to?(:focus)
55
+ it 'is false when the voter is not a voter' do
56
+ assert_false votable.ballot_by voter: not_voter
57
+ end
58
+
59
+ self.focus if focus && respond_to?(:focus)
60
+ it 'creates a vote' do
61
+ assert_true votable.ballot_by voter: voter
62
+ assert_true votable.ballots_for.any?
63
+ assert_equal(1, votable_dataset(votable).count)
64
+ end
65
+
66
+ self.focus if focus && respond_to?(:focus)
67
+ it 'creates only one vote per person' do
68
+ assert_true votable.ballot_by voter: voter, vote: 'yes'
69
+ assert_true votable.ballot_by voter: voter, vote: 'no'
70
+ assert_equal(1, votable_dataset(votable).count)
71
+ end
72
+
73
+ self.focus if focus && respond_to?(:focus)
74
+ it 'creates only one vote per person, unless duplicates are allowed' do
75
+ assert_true votable.ballot_by voter: voter, vote: 'yes'
76
+ assert_true votable.ballot_by voter: voter, vote: 'no', duplicate: true
77
+ assert_equal(2, votable_dataset(votable).count)
78
+ end
79
+
80
+ self.focus if focus && respond_to?(:focus)
81
+ it 'creates a scoped vote' do
82
+ assert_true votable.ballot_by voter: voter, scope: 'rank'
83
+ assert_true votable_dataset(votable).where(scope: 'rank').any?
84
+ end
85
+
86
+ self.focus if focus && respond_to?(:focus)
87
+ it 'creates only one scoped vote per person' do
88
+ assert_true votable.ballot_by voter: voter, scope: 'rank'
89
+ assert_true votable.ballot_by voter: voter, scope: 'rank', vote: 'no'
90
+ assert_equal(1, votable_dataset(votable).where(scope: 'rank').count)
91
+ end
92
+
93
+ self.focus if focus && respond_to?(:focus)
94
+ it 'creates only one scoped vote per person, unless duplicates are allowed' do
95
+ assert_true votable.ballot_by voter: voter, scope: 'rank'
96
+ assert_true(
97
+ votable.ballot_by(voter: voter, scope: 'rank', vote: 'no', duplicate: true)
98
+ )
99
+ assert_equal(2, votable_dataset(votable).where(scope: 'rank').count)
100
+ end
101
+
102
+ self.focus if focus && respond_to?(:focus)
103
+ it 'creates multiple votes with different scopes' do
104
+ assert_true votable.ballot_by voter: voter, scope: 'weekly_rank'
105
+ assert_true votable.ballot_by voter: voter, scope: 'monthly_rank'
106
+ assert_equal(2, votable_dataset(votable).count)
107
+ end
108
+
109
+ self.focus if focus && respond_to?(:focus)
110
+ it 'records separate votes for separate voters' do
111
+ assert_true votable.ballot_by voter: voter
112
+ assert_true votable.ballot_by voter: voter2
113
+ assert_equal(2, votable_dataset(votable).count)
114
+ end
115
+
116
+ self.focus if focus && respond_to?(:focus)
117
+ it 'uses a default vote weight of 1' do
118
+ votable.up_ballot_by voter
119
+ assert_equal(1, votable_dataset(votable).first.weight)
120
+ end
121
+ end
122
+
123
+ describe '#up_ballot_by' do
124
+ self.focus if focus && respond_to?(:focus)
125
+ it 'creates an up vote for the user' do
126
+ assert_true votable.up_ballot_by voter
127
+ assert_true votable_dataset(votable).where(
128
+ voter_id: voter.id,
129
+ voter_type: voter.class.name,
130
+ vote: true
131
+ ).any?
132
+ end
133
+ end
134
+
135
+ describe '#down_ballot_by' do
136
+ self.focus if focus && respond_to?(:focus)
137
+ it 'creates a down vote for the user' do
138
+ assert_true votable.down_ballot_by voter
139
+ assert_true votable_dataset(votable).where(
140
+ voter_id: voter.id,
141
+ voter_type: voter.class.name,
142
+ vote: false
143
+ ).any?
144
+ end
145
+ end
146
+
147
+ describe '#up_ballots_for' do
148
+ self.focus if focus && respond_to?(:focus)
149
+ it 'includes only positive votes' do
150
+ votable.up_ballot_by voter
151
+ votable.down_ballot_by voter2
152
+ assert_equal(1, votable.up_ballots_for.count)
153
+ assert_true votable.up_ballots_for.
154
+ where(voter_id: voter.id, voter_type: voter.class.name).any?
155
+ end
156
+ end
157
+
158
+ describe '#down_ballots_for' do
159
+ self.focus if focus && respond_to?(:focus)
160
+ it 'includes only positive votes' do
161
+ votable.up_ballot_by voter
162
+ votable.down_ballot_by voter2
163
+ assert_equal(1, votable.down_ballots_for.count)
164
+ assert_true votable.down_ballots_for.
165
+ where(voter_id: voter2.id, voter_type: voter2.class.name).any?
166
+ end
167
+ end
168
+
169
+ describe '#ballot_registered?' do
170
+ self.focus if focus && respond_to?(:focus)
171
+ it 'counts the vote as registered for the first vote' do
172
+ assert_true votable.up_ballot_by voter
173
+ assert_true votable.ballot_registered?
174
+ end
175
+
176
+ self.focus if focus && respond_to?(:focus)
177
+ it 'does not count a second unchanged vote as registered' do
178
+ assert_true votable.up_ballot_by voter
179
+ assert_false votable.up_ballot_by voter
180
+ assert_false votable.ballot_registered?
181
+ end
182
+
183
+ self.focus if focus && respond_to?(:focus)
184
+ it 'counts changed votes as registered' do
185
+ assert_true votable.up_ballot_by voter
186
+ assert_true votable.down_ballot_by voter
187
+ assert_true votable.ballot_registered?
188
+ end
189
+
190
+ self.focus if focus && respond_to?(:focus)
191
+ it 'counts vote weight changes as registered' do
192
+ assert_true votable.up_ballot_by voter
193
+ assert_true votable.up_ballot_by voter, weight: 2
194
+ assert_true votable.ballot_registered?
195
+ end
196
+
197
+ self.focus if focus && respond_to?(:focus)
198
+ it 'belongs only to a particular instance' do
199
+ votable.down_ballot_by voter
200
+ votable2 = votable.dup
201
+ votable2.up_ballot_by voter
202
+ votable2.up_ballot_by voter
203
+
204
+ assert_true votable.ballot_registered?
205
+ assert_false votable2.ballot_registered?
206
+ end
207
+ end
208
+
209
+ describe '#ballot_by?' do
210
+ self.focus if focus && respond_to?(:focus)
211
+ it 'is true if the user voted' do
212
+ assert_true votable.up_ballot_by voter
213
+ assert_true votable.ballot_by?(voter)
214
+ assert_true votable.down_ballot_by voter
215
+ assert_true votable.ballot_by?(voter)
216
+ end
217
+
218
+ self.focus if focus && respond_to?(:focus)
219
+ it 'is false if the user did not vote' do
220
+ assert_false votable.ballot_by?(voter)
221
+ end
222
+ end
223
+
224
+ describe '#up_ballot_by?' do
225
+ self.focus if focus && respond_to?(:focus)
226
+ it 'is true if the user voted up' do
227
+ assert_true votable.up_ballot_by voter
228
+ assert_true votable.up_ballot_by?(voter)
229
+ end
230
+
231
+ self.focus if focus && respond_to?(:focus)
232
+ it 'is false if the user did not vote' do
233
+ assert_false votable.up_ballot_by?(voter)
234
+ end
235
+
236
+ it 'is false if the user voted down' do
237
+ assert_true votable.down_ballot_by voter
238
+ assert_false votable.up_ballot_by?(voter)
239
+ end
240
+ end
241
+
242
+ describe '#down_ballot_by?' do
243
+ self.focus if focus && respond_to?(:focus)
244
+ it 'is true if the user voted down' do
245
+ assert_true votable.down_ballot_by voter
246
+ assert_true votable.down_ballot_by?(voter)
247
+ end
248
+
249
+ self.focus if focus && respond_to?(:focus)
250
+ it 'is false if the user did not vote' do
251
+ assert_false votable.down_ballot_by?(voter)
252
+ end
253
+
254
+ it 'is false if the user voted up' do
255
+ assert_true votable.up_ballot_by voter
256
+ assert_false votable.down_ballot_by?(voter)
257
+ end
258
+ end
259
+
260
+ describe '#remove_ballot_by' do
261
+ self.focus if focus && respond_to?(:focus)
262
+ it 'removes an up vote' do
263
+ assert_true votable.up_ballot_by voter
264
+ assert_true votable.remove_ballot_by voter: voter
265
+ assert_true votable_dataset(votable).none?
266
+ end
267
+
268
+ self.focus if focus && respond_to?(:focus)
269
+ it 'removes a down vote' do
270
+ assert_true votable.down_ballot_by voter
271
+ assert_true votable.remove_ballot_by voter: voter
272
+ assert_true votable_dataset(votable).none?
273
+ end
274
+
275
+ self.focus if focus && respond_to?(:focus)
276
+ it 'sets ballot_registered? to false' do
277
+ assert_true votable.up_ballot_by voter
278
+ assert_true votable.remove_ballot_by voter: voter
279
+ assert_false votable.ballot_registered?
280
+ end
281
+
282
+ self.focus if focus && respond_to?(:focus)
283
+ it 'removes only a single vote' do
284
+ assert_true votable.up_ballot_by voter
285
+ assert_true votable.up_ballot_by voter2
286
+ assert_true votable.remove_ballot_by voter: voter
287
+ assert_true votable_dataset(votable).any?
288
+ assert_true votable_dataset(votable).where(
289
+ voter_id: voter.id,
290
+ voter_type: voter.class.name
291
+ ).none?
292
+ assert_true votable_dataset(votable).where(
293
+ voter_id: voter2.id,
294
+ voter_type: voter2.class.name
295
+ ).any?
296
+ end
297
+ end
298
+
299
+ describe '#remove_ballot_by' do
300
+ self.focus if focus && respond_to?(:focus)
301
+ it 'removes an up vote' do
302
+ assert_true votable.up_ballot_by voter
303
+ assert_true votable.remove_ballot_by voter
304
+ assert_true votable_dataset(votable).none?
305
+ end
306
+
307
+ self.focus if focus && respond_to?(:focus)
308
+ it 'removes a down vote' do
309
+ assert_true votable.down_ballot_by voter
310
+ assert_true votable.remove_ballot_by voter
311
+ assert_true votable_dataset(votable).none?
312
+ end
313
+
314
+ self.focus if focus && respond_to?(:focus)
315
+ it 'sets ballot_registered? to false' do
316
+ assert_true votable.up_ballot_by voter
317
+ assert_true votable.remove_ballot_by voter
318
+ assert_false votable.ballot_registered?
319
+ end
320
+
321
+ self.focus if focus && respond_to?(:focus)
322
+ it 'removes only a single vote' do
323
+ assert_true votable.up_ballot_by voter
324
+ assert_true votable.up_ballot_by voter2
325
+ assert_true votable.remove_ballot_by voter
326
+ assert_true votable_dataset(votable).any?
327
+ assert_true votable_dataset(votable).where(
328
+ voter_id: voter.id,
329
+ voter_type: voter.class.name
330
+ ).none?
331
+ assert_true votable_dataset(votable).where(
332
+ voter_id: voter2.id,
333
+ voter_type: voter2.class.name
334
+ ).any?
335
+ end
336
+ end
337
+
338
+ describe '#ballots_by_class' do
339
+ self.focus if focus
340
+ it 'returns the set of votes by this class' do
341
+ voter.cast_up_ballot_for votable
342
+ voter2.cast_down_ballot_for votable
343
+
344
+ assert_equal [ voter_dataset(voter).first, voter_dataset(voter2).first ],
345
+ votable.ballots_by_class(voter.class).all
346
+ end
347
+ end
348
+
349
+ describe '#up_ballots_by_class' do
350
+ self.focus if focus
351
+ it 'returns the set of up votes for this class' do
352
+ voter.cast_up_ballot_for votable
353
+ voter2.cast_down_ballot_for votable
354
+
355
+ assert_equal [ voter_dataset(voter).first ],
356
+ votable.up_ballots_by_class(voter.class).all
357
+ end
358
+ end
359
+
360
+ describe '#down_ballots_by_class' do
361
+ self.focus if focus
362
+ it 'returns the set of down votes for this class' do
363
+ voter.cast_up_ballot_for votable
364
+ voter2.cast_down_ballot_for votable
365
+
366
+ assert_equal [ voter_dataset(voter2).first ],
367
+ votable.down_ballots_by_class(voter.class).all
368
+ end
369
+ end
370
+
371
+ describe '#ballot_voters' do
372
+ self.focus if focus && respond_to?(:focus)
373
+ it 'returns the set of voters' do
374
+ votable.up_ballot_by voter
375
+ votable.down_ballot_by voter2
376
+
377
+ assert_equal [ voter, voter2 ], votable.ballot_voters
378
+ end
379
+ end
380
+
381
+ describe '#up_ballot_voters' do
382
+ self.focus if focus && respond_to?(:focus)
383
+ it 'returns the set of voters' do
384
+ votable.up_ballot_by voter
385
+ votable.down_ballot_by voter2
386
+
387
+ assert_equal [ voter ], votable.up_ballot_voters
388
+ end
389
+ end
390
+
391
+ describe '#down_ballot_voters' do
392
+ self.focus if focus && respond_to?(:focus)
393
+ it 'returns the set of voters' do
394
+ votable.up_ballot_by voter
395
+ votable.down_ballot_by voter2
396
+
397
+ assert_equal [ voter2 ], votable.down_ballot_voters
398
+ end
399
+ end
400
+ end
401
+ end
402
+
403
+ def describe_ballot_votable_sti_support(focus: false)
404
+ describe 'STI support' do
405
+ describe '#ballot_by' do
406
+ self.focus if focus && respond_to?(:focus)
407
+ it 'works with STI models' do
408
+ sti_votable.ballot_by voter: voter
409
+ assert_true votable_dataset(sti_votable).any?
410
+ end
411
+
412
+ self.focus if focus && respond_to?(:focus)
413
+ it 'works with Child STI models' do
414
+ child_of_sti_votable.ballot_by voter: voter
415
+ assert_true child_of_sti_votable.ballots_for.any?
416
+ end
417
+
418
+ self.focus if focus && respond_to?(:focus)
419
+ it 'works with votable children of non-votable STI models' do
420
+ votable_child_of_sti_not_votable.ballot_by voter: voter
421
+ assert_true votable_child_of_sti_not_votable.ballots_for.any?
422
+ end
423
+ end
424
+ end
425
+ end
426
+
427
+ def describe_cached_ballot_summary(using_model, focus: false)
428
+ describe 'with cached_ballot_summary' do
429
+ self.focus if focus && respond_to?(:focus)
430
+ it 'does not update cached votes summary if there is no summary column' do
431
+ instance_stub using_model, :total_ballots do
432
+ votable.up_ballot_by voter
433
+ end
434
+
435
+ assert_instance_called using_model, :total_ballots, 0
436
+ end
437
+
438
+ self.focus if focus && respond_to?(:focus)
439
+ it 'updates the cached total' do
440
+ assert_equal(0, votable_cache.total_ballots)
441
+
442
+ votable_cache.up_ballot_by voter
443
+ assert_equal(1, votable_cache.total_ballots)
444
+
445
+ votable_cache.down_ballot_by voter2
446
+ assert_equal(2, votable_cache.total_ballots)
447
+
448
+ votable_cache.remove_ballot_by voter
449
+ assert_equal(1, votable_cache.total_ballots)
450
+ end
451
+
452
+ self.focus if focus && respond_to?(:focus)
453
+ it 'updates the cached up votes' do
454
+ assert_equal(0, votable_cache.total_up_ballots)
455
+
456
+ votable_cache.up_ballot_by voter
457
+ assert_equal(1, votable_cache.total_up_ballots)
458
+
459
+ votable_cache.down_ballot_by voter2
460
+ assert_equal(1, votable_cache.total_up_ballots)
461
+ end
462
+
463
+ self.focus if focus && respond_to?(:focus)
464
+ it 'downdates the cached down votes' do
465
+ assert_equal(0, votable_cache.total_down_ballots)
466
+
467
+ votable_cache.up_ballot_by voter
468
+ assert_equal(0, votable_cache.total_down_ballots)
469
+
470
+ votable_cache.down_ballot_by voter2
471
+ assert_equal(1, votable_cache.total_down_ballots)
472
+ end
473
+
474
+ self.focus if focus && respond_to?(:focus)
475
+ it 'updates the cached score' do
476
+ assert_equal(0, votable_cache.ballot_score)
477
+
478
+ votable_cache.down_ballot_by voter
479
+ assert_equal(-1, votable_cache.ballot_score)
480
+
481
+ votable_cache.down_ballot_by voter2
482
+ assert_equal(-2, votable_cache.ballot_score)
483
+
484
+ votable_cache.remove_ballot_by voter
485
+ assert_equal(-1, votable_cache.ballot_score)
486
+ end
487
+
488
+ self.focus if focus && respond_to?(:focus)
489
+ it 'updates the weighted total' do
490
+ assert_equal(0, votable_cache.weighted_ballot_total)
491
+
492
+ votable_cache.up_ballot_by voter
493
+ assert_equal(1, votable_cache.weighted_ballot_total)
494
+
495
+ votable_cache.down_ballot_by voter2, weight: 2
496
+ assert_equal(3, votable_cache.weighted_ballot_total)
497
+
498
+ votable_cache.remove_ballot_by voter
499
+ assert_equal(2, votable_cache.weighted_ballot_total)
500
+ end
501
+
502
+ self.focus if focus && respond_to?(:focus)
503
+ it 'updates the weighted score' do
504
+ assert_equal(0, votable_cache.weighted_ballot_score)
505
+
506
+ assert_true votable_cache.down_ballot_by voter
507
+ assert_equal(-1, votable_cache.weighted_ballot_score)
508
+
509
+ votable_cache.down_ballot_by voter2, weight: 2
510
+ assert_equal(-3, votable_cache.weighted_ballot_score)
511
+
512
+ votable_cache.remove_ballot_by voter
513
+ assert_equal(-2, votable_cache.weighted_ballot_score)
514
+ end
515
+
516
+ describe 'under a scope' do
517
+ self.focus if focus && respond_to?(:focus)
518
+ it 'does not update cached votes summary if there is no summary column' do
519
+ instance_stub using_model, :total_ballots do
520
+ votable.up_ballot_by voter, scope: 'scoped'
521
+ end
522
+
523
+ assert_instance_called using_model, :total_ballots, 0
524
+ end
525
+
526
+ self.focus if focus && respond_to?(:focus)
527
+ it 'does not affect the unscoped count' do
528
+ assert_equal(0, votable_cache.total_ballots)
529
+
530
+ votable_cache.up_ballot_by voter, scope: 'scoped'
531
+ assert_equal(0, votable_cache.total_ballots)
532
+ end
533
+
534
+ self.focus if focus && respond_to?(:focus)
535
+ it 'updates the cached total' do
536
+ assert_equal(0, votable_cache.total_ballots('scoped'))
537
+
538
+ votable_cache.up_ballot_by voter, scope: 'scoped'
539
+ assert_equal(1, votable_cache.total_ballots('scoped'))
540
+
541
+ votable_cache.down_ballot_by voter2, scope: 'scoped'
542
+ assert_equal(2, votable_cache.total_ballots('scoped'))
543
+
544
+ votable_cache.remove_ballot_by voter, scope: 'scoped'
545
+ assert_equal(1, votable_cache.total_ballots('scoped'))
546
+ end
547
+
548
+ self.focus if focus && respond_to?(:focus)
549
+ it 'updates the cached up votes' do
550
+ assert_equal(0, votable_cache.total_up_ballots('scoped'))
551
+
552
+ votable_cache.up_ballot_by voter, scope: 'scoped'
553
+ assert_equal(1, votable_cache.total_up_ballots('scoped'))
554
+
555
+ votable_cache.down_ballot_by voter2
556
+ assert_equal(1, votable_cache.total_up_ballots('scoped'))
557
+ end
558
+
559
+ self.focus if focus && respond_to?(:focus)
560
+ it 'downdates the cached down votes' do
561
+ assert_equal(0, votable_cache.total_down_ballots('scoped'))
562
+
563
+ votable_cache.up_ballot_by voter, scope: 'scoped'
564
+ assert_equal(0, votable_cache.total_down_ballots('scoped'))
565
+
566
+ assert_true votable_cache.down_ballot_by voter2, scope: 'scoped'
567
+ assert_equal(1, votable_cache.total_down_ballots('scoped'))
568
+ end
569
+
570
+ self.focus if focus && respond_to?(:focus)
571
+ it 'updates the cached score' do
572
+ assert_equal(0, votable_cache.ballot_score('scoped'))
573
+
574
+ votable_cache.down_ballot_by voter, scope: 'scoped'
575
+ assert_equal(-1, votable_cache.ballot_score('scoped'))
576
+
577
+ votable_cache.down_ballot_by voter2, scope: 'scoped'
578
+ assert_equal(-2, votable_cache.ballot_score('scoped'))
579
+
580
+ votable_cache.remove_ballot_by voter, scope: 'scoped'
581
+ assert_equal(-1, votable_cache.ballot_score('scoped'))
582
+ end
583
+
584
+ self.focus if focus && respond_to?(:focus)
585
+ it 'updates the weighted total' do
586
+ assert_equal(0, votable_cache.weighted_ballot_total('scoped'))
587
+
588
+ votable_cache.up_ballot_by voter, scope: 'scoped'
589
+ assert_equal(1, votable_cache.weighted_ballot_total('scoped'))
590
+
591
+ votable_cache.down_ballot_by voter2, weight: 2, scope: 'scoped'
592
+ assert_equal(3, votable_cache.weighted_ballot_total('scoped'))
593
+
594
+ votable_cache.remove_ballot_by voter, scope: 'scoped'
595
+ assert_equal(2, votable_cache.weighted_ballot_total('scoped'))
596
+ end
597
+
598
+ self.focus if focus && respond_to?(:focus)
599
+ it 'updates the weighted score' do
600
+ assert_equal(0, votable_cache.weighted_ballot_score('scoped'))
601
+ votable_cache.down_ballot_by voter, scope: 'scoped'
602
+ assert_equal(-1, votable_cache.weighted_ballot_score('scoped'))
603
+
604
+ votable_cache.down_ballot_by voter2, weight: 2, scope: 'scoped'
605
+ assert_equal(-3, votable_cache.weighted_ballot_score('scoped'))
606
+
607
+ votable_cache.remove_ballot_by voter, scope: 'scoped'
608
+ assert_equal(-2, votable_cache.weighted_ballot_score('scoped'))
609
+ end
610
+ end
611
+
612
+ self.focus if focus && respond_to?(:focus)
613
+ it 'README example' do
614
+ voter.cast_ballot_for votable, weight: 4
615
+ voter2.cast_ballot_for votable, vote: false
616
+
617
+ assert_equal(2, votable.total_ballots)
618
+ assert_equal(1, votable.total_up_ballots)
619
+ assert_equal(1, votable.total_down_ballots)
620
+ assert_equal(0, votable.ballot_score)
621
+ assert_equal(5, votable.weighted_ballot_total)
622
+ assert_equal(3, votable.weighted_ballot_score)
623
+ assert_in_delta(1.5, votable.weighted_ballot_average)
624
+ end
625
+ end
626
+ end
627
+
628
+ ::Minitest::SequelSpec.extend self
629
+ ::Minitest::ActiveRecordSpec.extend self
630
+ end