ballot 1.0

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