vote 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -5
  3. data/app/assets/images/votable/down.png +0 -0
  4. data/app/assets/images/votable/up.png +0 -0
  5. data/app/assets/javascripts/vote.js +74 -0
  6. data/app/assets/stylesheets/vote.css.erb +39 -0
  7. data/app/controllers/vote_controller.rb +12 -0
  8. data/app/helpers/vote_helper.rb +2 -0
  9. data/app/models/concerns/voter.rb +8 -0
  10. data/app/models/concerns/voting.rb +60 -0
  11. data/app/models/vote.rb +34 -0
  12. data/app/views/application/_vote.html.erb +11 -0
  13. data/app/views/vote/create.jbuilder +3 -0
  14. data/config/routes.rb +1 -0
  15. data/db/migrate/20141211124855_create_votes.rb +11 -0
  16. data/lib/tasks/{vote_tasks.rake → votable_tasks.rake} +1 -1
  17. data/lib/votable.rb +40 -0
  18. data/lib/votable/engine.rb +23 -0
  19. data/lib/votable/version.rb +3 -0
  20. data/test/controllers/vote_controller_test.rb +42 -0
  21. data/test/dummy/app/assets/javascripts/application.js +1 -0
  22. data/test/dummy/app/assets/stylesheets/application.css +1 -0
  23. data/test/dummy/app/controllers/application_controller.rb +7 -0
  24. data/test/dummy/app/helpers/application_helper.rb +3 -0
  25. data/test/dummy/app/models/user.rb +3 -0
  26. data/test/dummy/app/models/votable_element.rb +3 -0
  27. data/test/dummy/app/views/application/index.html.erb +4 -0
  28. data/test/dummy/app/views/layouts/application.html.erb +2 -4
  29. data/test/dummy/bin/rails +1 -1
  30. data/test/dummy/config.ru +1 -1
  31. data/test/dummy/config/application.rb +1 -2
  32. data/test/dummy/config/environments/development.rb +2 -0
  33. data/test/dummy/config/environments/production.rb +13 -12
  34. data/test/dummy/config/environments/test.rb +5 -2
  35. data/test/dummy/config/initializers/cookies_serializer.rb +1 -1
  36. data/test/dummy/config/initializers/vote.rb +8 -0
  37. data/test/dummy/config/routes.rb +1 -54
  38. data/test/dummy/config/secrets.yml +2 -2
  39. data/test/dummy/db/migrate/20141213010752_create_votable_elements.rb +12 -0
  40. data/test/dummy/db/migrate/20141215044136_create_users.rb +7 -0
  41. data/test/dummy/db/schema.rb +37 -0
  42. data/test/dummy/db/test.sqlite3 +0 -0
  43. data/test/dummy/log/test.log +103348 -0
  44. data/test/dummy/test/controllers/application_controller_test.rb +8 -0
  45. data/test/dummy/test/models/user_test.rb +19 -0
  46. data/test/dummy/test/models/votable_element_test.rb +45 -0
  47. data/test/dummy/tmp/cache/AA2/F51/votable_elements%2F1-20121211044855000000000%2Fupvote_status%2Fusers%2F1-20160405081953000000000 +1 -0
  48. data/test/dummy/tmp/cache/B75/FD1/votable_elements%2F1-20121211044855000000000%2Fdownvote_status%2Fusers%2F1-20160405081953000000000 +1 -0
  49. data/test/dummy/tmp/cache/E62/CB0/votable_elements%2F1-20121211044855000000000%2Frank +1 -0
  50. data/test/dummy/tmp/cache/ED2/B00/votable_elements%2F1-20121211044855000000000%2Fscore +0 -0
  51. data/test/dummy/tmp/cache/assets/sprockets/v3.0/-El-FN8jCFI2MGZVBIAI7IXXoa6FMof2jAZ8pmFTnvc.cache +2 -0
  52. data/test/dummy/tmp/cache/assets/sprockets/v3.0/0PTD3CT8q_GeqdproBnHSznOl0RAc5XsWJRNXUZlhKk.cache +1 -0
  53. data/test/dummy/tmp/cache/assets/sprockets/v3.0/0cNj-urje0NIW6l9-XzOZ3MZFk8IW3IS3Aqmu2KO9uM.cache +1 -0
  54. data/test/dummy/tmp/cache/assets/sprockets/v3.0/2BUd5YwUcwnCma29onQK5ngTHf4L8LTm_-_yF59Ihvk.cache +1 -0
  55. data/test/dummy/tmp/cache/assets/sprockets/v3.0/3QDmR4itzqXeTwXzP2mthzdMkIUJlCmukDDwRJkWX08.cache +0 -0
  56. data/test/dummy/tmp/cache/assets/sprockets/v3.0/5F-Dvi8UFsPxupWA-vmvs4_LwrzSlUINfRa_sOh0qWQ.cache +2 -0
  57. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Ax3LCzomcKayBxHqxe31l2IJwZoW4nuQxKQUEqrfJ_U.cache +0 -0
  58. data/test/dummy/tmp/cache/assets/sprockets/v3.0/B1TtiKillm1zDiVtSssFTC8RJ_Vz8lT6NVf5DVdXS6U.cache +2 -0
  59. data/test/dummy/tmp/cache/assets/sprockets/v3.0/BniTfFx9DFvpDq1GbWUh0UNc5s-h9Li2ChlvkHY9L1U.cache +2 -0
  60. data/test/dummy/tmp/cache/assets/sprockets/v3.0/C3KeicVpYPPCXwzyknMhr-TZA0MEe7ESvxM0TwIjnnk.cache +3 -0
  61. data/test/dummy/tmp/cache/assets/sprockets/v3.0/CLpvZ2bZ_21aWGn2ZxT4G9yizskQygHYnwmHhh1kx6s.cache +0 -0
  62. data/test/dummy/tmp/cache/assets/sprockets/v3.0/CgRpeuDhfS36mxhVvQ1WW8tu04zq1UiPPrMbDId0M1A.cache +1 -0
  63. data/test/dummy/tmp/cache/assets/sprockets/v3.0/DbduVB5B2EGTotBVD_NmdLP5bmcR_Fmzd6gvxTjej6g.cache +1 -0
  64. data/test/dummy/tmp/cache/assets/sprockets/v3.0/GO8J-ex_JAVxHP3tAE9z3IqCSk_5TM-g57Lj0TxHxZI.cache +0 -0
  65. data/test/dummy/tmp/cache/assets/sprockets/v3.0/ISNcxbpHMEavSkcmlLsk_jyi77vzsYZvtfD_9KZ5mXs.cache +1 -0
  66. data/test/dummy/tmp/cache/assets/sprockets/v3.0/K6BH-neaelwjs6igeryeJdVcIKyFD-MVThotsSw1_lk.cache +1 -0
  67. data/test/dummy/tmp/cache/assets/sprockets/v3.0/KBws9RFVZfk7GKM9zx1o6y_CnZd67UUu5prC4yjNjMY.cache +0 -0
  68. data/test/dummy/tmp/cache/assets/sprockets/v3.0/PFGYJMQpvdoSny5yVe7DkBsD4syJWncluaACPMySG8U.cache +1 -0
  69. data/test/dummy/tmp/cache/assets/sprockets/v3.0/PPHah0f_950rDPIcgdZdrPG5mWCDMQHh2FXQweMpROo.cache +1 -0
  70. data/test/dummy/tmp/cache/assets/sprockets/v3.0/PxC7DH90XyhNTBlNYTvhfCGZH4_0CpTt-sMdvk4p9p8.cache +3 -0
  71. data/test/dummy/tmp/cache/assets/sprockets/v3.0/SRCZC-AxKnmzBBH0YsaAgrKNPgBaUA3ZVf0176wZIco.cache +4 -0
  72. data/test/dummy/tmp/cache/assets/sprockets/v3.0/UEUIg4LMdLy9aVXNi6rEZxbz6vKDG6UXb-77GYPqp5g.cache +1 -0
  73. data/test/dummy/tmp/cache/assets/sprockets/v3.0/WdavtqaV1L7kWEMgm7u2IJkLB90lAAInW3gfgpmmHJE.cache +1 -0
  74. data/test/dummy/tmp/cache/assets/sprockets/v3.0/YoGhfupdxiennx6ywdf4x6wZ6BKonR8Bj9D3ox4lB7I.cache +1 -0
  75. data/test/dummy/tmp/cache/assets/sprockets/v3.0/Zz-TNS30yi0kfNbbOy-ZOSNtRVrGwQ8SMRN0lAmDy6s.cache +1 -0
  76. data/test/dummy/tmp/cache/assets/sprockets/v3.0/_AbDOrmJPRR-S07op411AFFEfecRcUSSMSFxYxLR4xo.cache +1 -0
  77. data/test/dummy/tmp/cache/assets/sprockets/v3.0/a3Pjrd87T2nqEtGLz6In4Iu6oLYwPuJLSE94i6mW3Ck.cache +0 -0
  78. data/test/dummy/tmp/cache/assets/sprockets/v3.0/bh2dbqBpnMmczJOa3eVntNbRIt8YM04aOIyUCpgR1gA.cache +4 -0
  79. data/test/dummy/tmp/cache/assets/sprockets/v3.0/dMlY4p7rRb_1fw3vfaGEcOJi4SXJGXsvoT_U44tvmWI.cache +0 -0
  80. data/test/dummy/tmp/cache/assets/sprockets/v3.0/eJjawTfEdbNH8xgCPPd8On11oZn40fhBCwo9bGN5TX8.cache +3 -0
  81. data/test/dummy/tmp/cache/assets/sprockets/v3.0/ex4XwuMzvixZ4giNEjUZ8wL2k7ZulQ5dNe3N4tKK10A.cache +0 -0
  82. data/test/dummy/tmp/cache/assets/sprockets/v3.0/g4wJFnucd8DQd0XvI58w3-incEUHgMg1e7XUWUKLm1M.cache +0 -0
  83. data/test/dummy/tmp/cache/assets/sprockets/v3.0/i9Xav1zGItoosb4Uez1_nmEtI4JCbhS6gopxCL3d-L0.cache +0 -0
  84. data/test/dummy/tmp/cache/assets/sprockets/v3.0/iDvGPQQhkaKSTxHP6wx8e-kPLAynWb11FGEIVbT_nQM.cache +1 -0
  85. data/test/dummy/tmp/cache/assets/sprockets/v3.0/iHAmk9HoMqCnuIHexHa_hNeCtQeLF7kGwl_kDrMiBEo.cache +1 -0
  86. data/test/dummy/tmp/cache/assets/sprockets/v3.0/m7s4E3k9E_72VrHkjaxcSTXRanKL5vBMabG9tmRy6rk.cache +0 -0
  87. data/test/dummy/tmp/cache/assets/sprockets/v3.0/piEIzZ9NNm29Zi6xtGNty7KEv4YTNagsTYeEbQat_dc.cache +1 -0
  88. data/test/dummy/tmp/cache/assets/sprockets/v3.0/q20dDzc7Fj9zVO9zcXN-BKyGjDcb0O1lA0Vw1rjdibA.cache +1 -0
  89. data/test/dummy/tmp/cache/assets/sprockets/v3.0/qSwhfxs3qwfNAdqtVhXpsGYQhTtv005jgxepgGmiknk.cache +0 -0
  90. data/test/dummy/tmp/cache/assets/sprockets/v3.0/r4RUnbdoTR15L2hseR5YzdO1k1wYv459i9ZZdhXYnQ4.cache +2 -0
  91. data/test/dummy/tmp/cache/assets/sprockets/v3.0/sFQXxhpurv7b4zdluUTH4uVkavZMPuMWP6XXVxlfOO0.cache +1 -0
  92. data/test/dummy/tmp/cache/assets/sprockets/v3.0/sHVyX31KKYvBELSzYnhHb8do4H7LGpFrFdfLflit64o.cache +3 -0
  93. data/test/dummy/tmp/cache/assets/sprockets/v3.0/u0xKjvsn83XKhAaZKn63CKyhnTtlsakjTkKFnI4ZhU4.cache +0 -0
  94. data/test/dummy/tmp/cache/assets/sprockets/v3.0/ujpQUvM3t6mL2Kp9EXlasLYk8s5dzS0KM584wg7DIns.cache +0 -0
  95. data/test/dummy/tmp/cache/assets/sprockets/v3.0/w4VvG1yL_2f1uZpy1BI2RdSZTac9VRDRR87geqHO8mY.cache +0 -0
  96. data/test/fixtures/users.yml +11 -0
  97. data/test/fixtures/votable_elements.yml +14 -0
  98. data/test/fixtures/votes.yml +35 -0
  99. data/test/helpers/vote_helper_test.rb +4 -0
  100. data/test/integration/navigation_test.rb +0 -1
  101. data/test/models/vote_test.rb +86 -0
  102. data/test/test_helper.rb +13 -6
  103. data/test/votable_test.rb +7 -0
  104. metadata +182 -22
  105. data/README.rdoc +0 -3
  106. data/lib/vote.rb +0 -4
  107. data/lib/vote/engine.rb +0 -4
  108. data/lib/vote/version.rb +0 -3
  109. data/test/vote_test.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 51d09c11a38c409fffc5db3c465c8943e9200c39
4
- data.tar.gz: 1996f876bca6339c9648ca93fbe58027ae3d03ce
3
+ metadata.gz: f492ffc6df02f2b36eb5bce6535dd736f89b5b49
4
+ data.tar.gz: 33ce6da072b7b4c4b0d762614e16a03eaa5c4ad9
5
5
  SHA512:
6
- metadata.gz: 457d82db8aad6622e239da9c5b7965d734f22e1e8225cf4d3fb9a1d898db72a1cd2d6cb388fce88985e3b904b3b4d715bd32467649b44e8ef3bda5bf07fe6108
7
- data.tar.gz: 6bc938f3df69381d5f5459eff4590071d7ca92747ae090e79c3bfac8cbfa355f0c3c1b36272e9f7446f9d49398066bc3db334efa3dee0f0ac2e441e52862550b
6
+ metadata.gz: e9b96eb34c8168348db87d7d03d7b002527c875511ef29c0334b9d0f8da85a39893d138730d09ee630e77d16b076b4a94af2ff11a390e84cc75191900b1476c4
7
+ data.tar.gz: d96f009f7722010206d3d3a568ac8d4ff26b3908e3fd5ae8b5de7891d0b74b4fe078e4f19c3320f6f1f69bb4c81270bb683aed22ef71285dfd964a1a3de6bf3c
data/Rakefile CHANGED
@@ -8,17 +8,15 @@ require 'rdoc/task'
8
8
 
9
9
  RDoc::Task.new(:rdoc) do |rdoc|
10
10
  rdoc.rdoc_dir = 'rdoc'
11
- rdoc.title = 'Vote'
11
+ rdoc.title = 'Votable'
12
12
  rdoc.options << '--line-numbers'
13
13
  rdoc.rdoc_files.include('README.rdoc')
14
14
  rdoc.rdoc_files.include('lib/**/*.rb')
15
15
  end
16
16
 
17
- APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
17
+ APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
18
18
  load 'rails/tasks/engine.rake'
19
19
 
20
-
21
-
22
20
  Bundler::GemHelper.install_tasks
23
21
 
24
22
  require 'rake/testtask'
@@ -30,5 +28,4 @@ Rake::TestTask.new(:test) do |t|
30
28
  t.verbose = false
31
29
  end
32
30
 
33
-
34
31
  task default: :test
@@ -0,0 +1,74 @@
1
+ var vote = function(type, id, direction) {
2
+ upvote_qs = '#upvote_' + type + '_' + id;
3
+ downvote_qs = '#downvote_' + type + '_' + id;
4
+
5
+ active_class = 'vote-active';
6
+
7
+ if (direction) {
8
+ if (document.querySelector(upvote_qs).getAttribute('class') == 'vote-up ' + active_class) {
9
+ removeClass(upvote_qs, active_class);
10
+ removeClass(upvote_qs, active_class);
11
+ } else {
12
+ addClass(upvote_qs, active_class);
13
+ removeClass(downvote_qs, active_class);
14
+ }
15
+ } else {
16
+ if (document.querySelector(downvote_qs).getAttribute('class') == 'vote-down ' + active_class) {
17
+ removeClass(downvote_qs, active_class);
18
+ removeClass(downvote_qs, active_class);
19
+ } else {
20
+ addClass(downvote_qs, active_class);
21
+ removeClass(upvote_qs, active_class);
22
+ }
23
+ }
24
+
25
+ var request = new XMLHttpRequest();
26
+ request.open('POST', '/vote/create', true);
27
+ request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
28
+ var csrf_token = document.querySelector("meta[name='csrf-token']");
29
+ if (csrf_token) {
30
+ request.setRequestHeader('X-CSRF-Token', csrf_token.content);
31
+ }
32
+ request.onreadystatechange = (function() {
33
+ if (request.readyState == 4) {
34
+ if (request.status == 200) {
35
+ response = JSON.parse(request.responseText);
36
+ if (response) {
37
+ document.getElementById('score_' + type + '_' + id).innerHTML = response.score;
38
+ } else {
39
+ handle_signed_out_user();
40
+ }
41
+ } else {
42
+ console.log('There was an error performing your request.');
43
+ }
44
+ }
45
+ });
46
+ request.send('vote_type=' + type + '&vote_id=' + id + '&direction=' + direction);
47
+ };
48
+
49
+ // Override this and include your desired behavior (redirect to /sign-in, etc.)
50
+ var handle_signed_out_user = function() {
51
+ console.log('Please sign in to vote');
52
+ };
53
+
54
+ var addClass = function(el, className) {
55
+ if (document.contains(document.querySelector(el))) {
56
+ nav = document.querySelector(el);
57
+ if (nav.classList) {
58
+ nav.classList.add(className);
59
+ } else {
60
+ nav.className += ' ' + classTar;
61
+ }
62
+ }
63
+ }
64
+
65
+ var removeClass = function(el, className) {
66
+ if (document.contains(document.querySelector(el))) {
67
+ nav = document.querySelector(el);
68
+ if (nav.classList) {
69
+ nav.classList.remove(className);
70
+ } else {
71
+ nav.className = nav.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,39 @@
1
+ .vote {
2
+ border-width: 1px;
3
+ border-color: #000;
4
+ }
5
+
6
+ .vote * {
7
+ text-align: center;
8
+ }
9
+
10
+ .vote-up, .vote-down {
11
+ cursor: pointer;
12
+ width: 45px;
13
+ height: 10px;
14
+ margin: 0 auto;
15
+
16
+ background-size: 100%;
17
+ }
18
+
19
+ .vote-up {
20
+ background-image: url("<%=asset_path('votable/up.png')%>");
21
+ background-position: 0 top;
22
+ }
23
+
24
+ .vote-up.vote-active {
25
+ background-position: 0 15px;
26
+ }
27
+
28
+ .vote-count {
29
+ display: block;
30
+ }
31
+
32
+ .vote-down {
33
+ background-image: url("<%=asset_path('votable/down.png')%>");
34
+ background-position: 0 bottom;
35
+ }
36
+
37
+ .vote-down.vote-active {
38
+ background-position: 0 25px;
39
+ }
@@ -0,0 +1,12 @@
1
+ class VoteController < ApplicationController
2
+ layout false
3
+
4
+ def create
5
+ # if Votable.concerned_classes.include? params[:vote_type]
6
+ @votable_element = params[:vote_type].constantize.__send__(:find_by_id, params[:vote_id])
7
+ @votable_element.votes.create(user_id: current_user.try(:id), direction: params[:direction])
8
+ # else
9
+ # redirect_to root_path
10
+ # end
11
+ end
12
+ end
@@ -0,0 +1,2 @@
1
+ module VoteHelper
2
+ end
@@ -0,0 +1,8 @@
1
+ module Voter
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :votes,
6
+ class_name: Vote
7
+ end
8
+ end
@@ -0,0 +1,60 @@
1
+ module Voting
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :votes,
6
+ as: :vote,
7
+ class_name: Vote,
8
+ dependent: :destroy
9
+
10
+ has_many :upvotes,
11
+ -> { where(direction: true) },
12
+ as: :vote,
13
+ class_name: Vote
14
+
15
+ has_many :downvotes,
16
+ -> { where(direction: false) },
17
+ as: :vote,
18
+ class_name: Vote
19
+
20
+ def score
21
+ upvotes.count - downvotes.count
22
+ end
23
+
24
+ def rank
25
+ order = Math.log10(
26
+ [score.abs, 1].max
27
+ )
28
+
29
+ sign = if score > 0
30
+ 1
31
+ elsif score < 0
32
+ -1
33
+ else
34
+ 0
35
+ end
36
+
37
+ seconds = created_at.to_i - Votable.rank_epoch
38
+
39
+ (
40
+ order + sign * seconds
41
+ ).round(11)
42
+ end
43
+
44
+ def upvote_status(user)
45
+ if user
46
+ upvotes.where(user_id: user.id).exists?
47
+ else
48
+ false
49
+ end
50
+ end
51
+
52
+ def downvote_status(user)
53
+ if user
54
+ downvotes.where(user_id: user.id).exists?
55
+ else
56
+ false
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,34 @@
1
+ class Vote < ActiveRecord::Base
2
+ belongs_to :vote, polymorphic: true
3
+
4
+ validates :vote_type, presence: true
5
+ validates :vote_id, presence: true
6
+ validates :direction, inclusion: [true, false], if: 'Votable.enable_upvotes && Votable.enable_downvotes'
7
+ validates :direction, inclusion: [true], if: 'Votable.enable_upvotes && !Votable.enable_downvotes'
8
+ validates :direction, inclusion: [false], if: '!Votable.enable_upvotes && Votable.enable_downvotes'
9
+
10
+ before_create :remove_existing, if: 'Votable.remove_existing_votes'
11
+ after_create :update_cache_columns, if: 'Votable.enable_cache_columns'
12
+ after_destroy :update_cache_columns, if: 'Votable.enable_cache_columns', prepend: true
13
+
14
+ private
15
+
16
+ def remove_existing
17
+ votes = Vote.where(user_id: user_id, vote_type: vote_type, vote_id: vote_id)
18
+ opposing = votes.where(direction: !direction).count
19
+ existing = votes.count
20
+ votes.destroy_all
21
+
22
+ false if existing > 0 && opposing == 0
23
+ end
24
+
25
+ def update_cache_columns
26
+ @element = vote_type.camelize.constantize.__send__(:find_by_id, vote_id)
27
+ @element.update(
28
+ upvotes_count: @element.upvotes.count,
29
+ downvotes_count: @element.downvotes.count,
30
+ score: @element.score,
31
+ rank: @element.rank
32
+ )
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ <div class="vote">
2
+ <% unless Votable.enable_upvotes == false %>
3
+ <div id="upvote_<%=element.class%>_<%=element.id%>" onclick="vote('<%=element.class%>', <%=element.id%>, true);" class="vote-up <%=('vote-active' if element.upvote_status(current_user))%>"></div>
4
+ <% end %>
5
+ <% unless Votable.show_score == false %>
6
+ <div id="score_<%=element.class%>_<%=element.id%>" class="vote-count"><%=element.score%></div>
7
+ <% end %>
8
+ <% unless Votable.enable_downvotes == false %>
9
+ <div id="downvote_<%=element.class%>_<%=element.id%>" onclick="vote('<%=element.class%>', <%=element.id%>, false);" class="vote-down <%=('vote-active' if element.downvote_status(current_user))%>"></div>
10
+ <% end %>
11
+ </div>
@@ -0,0 +1,3 @@
1
+ json.upvotes @votable_element.upvotes
2
+ json.downvotes @votable_element.downvotes
3
+ json.score @votable_element.score
@@ -1,2 +1,3 @@
1
1
  Rails.application.routes.draw do
2
+ post 'vote/create', to: 'vote#create'
2
3
  end
@@ -0,0 +1,11 @@
1
+ class CreateVotes < ActiveRecord::Migration
2
+ def change
3
+ create_table :votes do |t|
4
+ t.integer :user_id
5
+ t.boolean :direction, null: false
6
+ t.references :vote, polymorphic: true
7
+
8
+ t.timestamps null: false
9
+ end
10
+ end
11
+ end
@@ -1,4 +1,4 @@
1
1
  # desc "Explaining what the task does"
2
- # task :vote do
2
+ # task :votable do
3
3
  # # Task goes here
4
4
  # end
@@ -0,0 +1,40 @@
1
+ require 'votable/engine'
2
+ require 'jbuilder'
3
+
4
+ module Votable
5
+ mattr_accessor :concerned_classes do
6
+ []
7
+ end
8
+
9
+ mattr_accessor :rank_epoch do
10
+ 0
11
+ end
12
+
13
+ mattr_accessor :remove_existing_votes do
14
+ true
15
+ end
16
+
17
+ mattr_accessor :autoload_migrations do
18
+ false
19
+ end
20
+
21
+ mattr_accessor :autoload_decorators do
22
+ false
23
+ end
24
+
25
+ mattr_accessor :enable_cache_columns do
26
+ false
27
+ end
28
+
29
+ mattr_accessor :enable_upvotes do
30
+ true
31
+ end
32
+
33
+ mattr_accessor :enable_downvotes do
34
+ true
35
+ end
36
+
37
+ mattr_accessor :show_score do
38
+ true
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ module Votable
2
+ class Engine < ::Rails::Engine
3
+ initializer 'autoload migrations' do |app|
4
+ if Votable.autoload_migrations
5
+ return if app.root.to_s == root.to_s
6
+
7
+ config.paths['db/migrate'].expanded.each do |path|
8
+ app.config.paths['db/migrate'].push(path)
9
+ end
10
+ end
11
+ end
12
+
13
+ initializer 'autoload decorators' do |_app|
14
+ if Votable.autoload_decorators
15
+ config.to_prepare do
16
+ Dir.glob(Rails.root + 'app/decorators/**/*_decorator*.rb').each do |c|
17
+ require_dependency(c)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Votable
2
+ VERSION = '0.2.0'.freeze
3
+ end
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+
3
+ class VoteControllerTest < ActionController::TestCase
4
+ setup do
5
+ Votable.remove_existing_votes = true
6
+ Votable.concerned_classes = [VotableElement]
7
+ end
8
+
9
+ test 'can post votes' do
10
+ assert_difference 'Vote.count' do
11
+ post :create, direction: 'true', vote_type: 'VotableElement', vote_id: 3
12
+ assert_equal 'application/json', response.content_type
13
+ assert_response :success
14
+ end
15
+ end
16
+
17
+ test 'depending on settings, remove duplicate votes' do
18
+ assert_difference 'Vote.count', -1 do
19
+ post :create, direction: 'false', vote_type: 'VotableElement', vote_id: 1
20
+ assert_equal 'application/json', response.content_type
21
+ assert_response :success
22
+ end
23
+ end
24
+
25
+ test "depending on settings, don't remove duplicate votes" do
26
+ Votable.remove_existing_votes = false
27
+
28
+ assert_difference 'Vote.count' do
29
+ post :create, direction: 'true', vote_type: 'VotableElement', vote_id: 1
30
+ assert_equal 'application/json', response.content_type
31
+ assert_response :success
32
+ end
33
+ end
34
+
35
+ test "doesn't toggle off when casting opposing vote" do
36
+ assert_no_difference 'Vote.count' do
37
+ post :create, direction: 'true', vote_type: 'VotableElement', vote_id: 1
38
+ assert_equal 'application/json', response.content_type
39
+ assert_response :success
40
+ end
41
+ end
42
+ end
@@ -11,3 +11,4 @@
11
11
  // about supported directives.
12
12
  //
13
13
  //= require_tree .
14
+ //= require vote
@@ -12,4 +12,5 @@
12
12
  *
13
13
  *= require_tree .
14
14
  *= require_self
15
+ *= require vote
15
16
  */