vote 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +2 -5
- data/app/assets/images/votable/down.png +0 -0
- data/app/assets/images/votable/up.png +0 -0
- data/app/assets/javascripts/vote.js +74 -0
- data/app/assets/stylesheets/vote.css.erb +39 -0
- data/app/controllers/vote_controller.rb +12 -0
- data/app/helpers/vote_helper.rb +2 -0
- data/app/models/concerns/voter.rb +8 -0
- data/app/models/concerns/voting.rb +60 -0
- data/app/models/vote.rb +34 -0
- data/app/views/application/_vote.html.erb +11 -0
- data/app/views/vote/create.jbuilder +3 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20141211124855_create_votes.rb +11 -0
- data/lib/tasks/{vote_tasks.rake → votable_tasks.rake} +1 -1
- data/lib/votable.rb +40 -0
- data/lib/votable/engine.rb +23 -0
- data/lib/votable/version.rb +3 -0
- data/test/controllers/vote_controller_test.rb +42 -0
- data/test/dummy/app/assets/javascripts/application.js +1 -0
- data/test/dummy/app/assets/stylesheets/application.css +1 -0
- data/test/dummy/app/controllers/application_controller.rb +7 -0
- data/test/dummy/app/helpers/application_helper.rb +3 -0
- data/test/dummy/app/models/user.rb +3 -0
- data/test/dummy/app/models/votable_element.rb +3 -0
- data/test/dummy/app/views/application/index.html.erb +4 -0
- data/test/dummy/app/views/layouts/application.html.erb +2 -4
- data/test/dummy/bin/rails +1 -1
- data/test/dummy/config.ru +1 -1
- data/test/dummy/config/application.rb +1 -2
- data/test/dummy/config/environments/development.rb +2 -0
- data/test/dummy/config/environments/production.rb +13 -12
- data/test/dummy/config/environments/test.rb +5 -2
- data/test/dummy/config/initializers/cookies_serializer.rb +1 -1
- data/test/dummy/config/initializers/vote.rb +8 -0
- data/test/dummy/config/routes.rb +1 -54
- data/test/dummy/config/secrets.yml +2 -2
- data/test/dummy/db/migrate/20141213010752_create_votable_elements.rb +12 -0
- data/test/dummy/db/migrate/20141215044136_create_users.rb +7 -0
- data/test/dummy/db/schema.rb +37 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +103348 -0
- data/test/dummy/test/controllers/application_controller_test.rb +8 -0
- data/test/dummy/test/models/user_test.rb +19 -0
- data/test/dummy/test/models/votable_element_test.rb +45 -0
- data/test/dummy/tmp/cache/AA2/F51/votable_elements%2F1-20121211044855000000000%2Fupvote_status%2Fusers%2F1-20160405081953000000000 +1 -0
- data/test/dummy/tmp/cache/B75/FD1/votable_elements%2F1-20121211044855000000000%2Fdownvote_status%2Fusers%2F1-20160405081953000000000 +1 -0
- data/test/dummy/tmp/cache/E62/CB0/votable_elements%2F1-20121211044855000000000%2Frank +1 -0
- data/test/dummy/tmp/cache/ED2/B00/votable_elements%2F1-20121211044855000000000%2Fscore +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/-El-FN8jCFI2MGZVBIAI7IXXoa6FMof2jAZ8pmFTnvc.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/0PTD3CT8q_GeqdproBnHSznOl0RAc5XsWJRNXUZlhKk.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/0cNj-urje0NIW6l9-XzOZ3MZFk8IW3IS3Aqmu2KO9uM.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/2BUd5YwUcwnCma29onQK5ngTHf4L8LTm_-_yF59Ihvk.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/3QDmR4itzqXeTwXzP2mthzdMkIUJlCmukDDwRJkWX08.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/5F-Dvi8UFsPxupWA-vmvs4_LwrzSlUINfRa_sOh0qWQ.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Ax3LCzomcKayBxHqxe31l2IJwZoW4nuQxKQUEqrfJ_U.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/B1TtiKillm1zDiVtSssFTC8RJ_Vz8lT6NVf5DVdXS6U.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/BniTfFx9DFvpDq1GbWUh0UNc5s-h9Li2ChlvkHY9L1U.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/C3KeicVpYPPCXwzyknMhr-TZA0MEe7ESvxM0TwIjnnk.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/CLpvZ2bZ_21aWGn2ZxT4G9yizskQygHYnwmHhh1kx6s.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/CgRpeuDhfS36mxhVvQ1WW8tu04zq1UiPPrMbDId0M1A.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/DbduVB5B2EGTotBVD_NmdLP5bmcR_Fmzd6gvxTjej6g.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/GO8J-ex_JAVxHP3tAE9z3IqCSk_5TM-g57Lj0TxHxZI.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/ISNcxbpHMEavSkcmlLsk_jyi77vzsYZvtfD_9KZ5mXs.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/K6BH-neaelwjs6igeryeJdVcIKyFD-MVThotsSw1_lk.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/KBws9RFVZfk7GKM9zx1o6y_CnZd67UUu5prC4yjNjMY.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/PFGYJMQpvdoSny5yVe7DkBsD4syJWncluaACPMySG8U.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/PPHah0f_950rDPIcgdZdrPG5mWCDMQHh2FXQweMpROo.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/PxC7DH90XyhNTBlNYTvhfCGZH4_0CpTt-sMdvk4p9p8.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/SRCZC-AxKnmzBBH0YsaAgrKNPgBaUA3ZVf0176wZIco.cache +4 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/UEUIg4LMdLy9aVXNi6rEZxbz6vKDG6UXb-77GYPqp5g.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/WdavtqaV1L7kWEMgm7u2IJkLB90lAAInW3gfgpmmHJE.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/YoGhfupdxiennx6ywdf4x6wZ6BKonR8Bj9D3ox4lB7I.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/Zz-TNS30yi0kfNbbOy-ZOSNtRVrGwQ8SMRN0lAmDy6s.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/_AbDOrmJPRR-S07op411AFFEfecRcUSSMSFxYxLR4xo.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/a3Pjrd87T2nqEtGLz6In4Iu6oLYwPuJLSE94i6mW3Ck.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/bh2dbqBpnMmczJOa3eVntNbRIt8YM04aOIyUCpgR1gA.cache +4 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/dMlY4p7rRb_1fw3vfaGEcOJi4SXJGXsvoT_U44tvmWI.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/eJjawTfEdbNH8xgCPPd8On11oZn40fhBCwo9bGN5TX8.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/ex4XwuMzvixZ4giNEjUZ8wL2k7ZulQ5dNe3N4tKK10A.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/g4wJFnucd8DQd0XvI58w3-incEUHgMg1e7XUWUKLm1M.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/i9Xav1zGItoosb4Uez1_nmEtI4JCbhS6gopxCL3d-L0.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/iDvGPQQhkaKSTxHP6wx8e-kPLAynWb11FGEIVbT_nQM.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/iHAmk9HoMqCnuIHexHa_hNeCtQeLF7kGwl_kDrMiBEo.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/m7s4E3k9E_72VrHkjaxcSTXRanKL5vBMabG9tmRy6rk.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/piEIzZ9NNm29Zi6xtGNty7KEv4YTNagsTYeEbQat_dc.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/q20dDzc7Fj9zVO9zcXN-BKyGjDcb0O1lA0Vw1rjdibA.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/qSwhfxs3qwfNAdqtVhXpsGYQhTtv005jgxepgGmiknk.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/r4RUnbdoTR15L2hseR5YzdO1k1wYv459i9ZZdhXYnQ4.cache +2 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/sFQXxhpurv7b4zdluUTH4uVkavZMPuMWP6XXVxlfOO0.cache +1 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/sHVyX31KKYvBELSzYnhHb8do4H7LGpFrFdfLflit64o.cache +3 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/u0xKjvsn83XKhAaZKn63CKyhnTtlsakjTkKFnI4ZhU4.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/ujpQUvM3t6mL2Kp9EXlasLYk8s5dzS0KM584wg7DIns.cache +0 -0
- data/test/dummy/tmp/cache/assets/sprockets/v3.0/w4VvG1yL_2f1uZpy1BI2RdSZTac9VRDRR87geqHO8mY.cache +0 -0
- data/test/fixtures/users.yml +11 -0
- data/test/fixtures/votable_elements.yml +14 -0
- data/test/fixtures/votes.yml +35 -0
- data/test/helpers/vote_helper_test.rb +4 -0
- data/test/integration/navigation_test.rb +0 -1
- data/test/models/vote_test.rb +86 -0
- data/test/test_helper.rb +13 -6
- data/test/votable_test.rb +7 -0
- metadata +182 -22
- data/README.rdoc +0 -3
- data/lib/vote.rb +0 -4
- data/lib/vote/engine.rb +0 -4
- data/lib/vote/version.rb +0 -3
- data/test/vote_test.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f492ffc6df02f2b36eb5bce6535dd736f89b5b49
|
4
|
+
data.tar.gz: 33ce6da072b7b4c4b0d762614e16a03eaa5c4ad9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 = '
|
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(
|
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
|
Binary file
|
Binary file
|
@@ -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,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
|
data/app/models/vote.rb
ADDED
@@ -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>
|
data/config/routes.rb
CHANGED
data/lib/votable.rb
ADDED
@@ -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,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
|