vote 0.0.1 → 0.2.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 (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
  */