thumbs_up 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -1
- data/README.md +6 -3
- data/lib/acts_as_voteable.rb +27 -6
- data/lib/thumbs_up/version.rb +1 -1
- data/test/test_helper.rb +15 -7
- data/test/thumbs_up_test.rb +29 -3
- metadata +56 -15
data/Gemfile
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
gemspec
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
ThumbsUp
|
2
2
|
=======
|
3
3
|
|
4
|
+
[![Build Status](https://secure.travis-ci.org/bouchard/thumbs_up.png)](http://travis-ci.org/bouchard/thumbs_up)
|
5
|
+
|
4
6
|
**Note: Version 0.5.x is a breaking change for #plusminus_tally and #tally, with > 50% speedups.**
|
5
7
|
|
6
8
|
A ridiculously straightforward and simple package 'o' code to enable voting in your application, a la stackoverflow.com, etc.
|
@@ -98,15 +100,16 @@ This will select the Items with less than 10 votes, the votes having been cast w
|
|
98
100
|
|
99
101
|
**You most likely want to use this over the normal tally**
|
100
102
|
|
101
|
-
This is similar to tallying votes, but this will return voteable object collections based on the sum of the differences between up and down votes (ups are +1, downs are -1). For Instance, a voteable with 3 upvotes and 2 downvotes will have a
|
103
|
+
This is similar to tallying votes, but this will return voteable object collections based on the sum of the differences between up and down votes (ups are +1, downs are -1). For Instance, a voteable with 3 upvotes and 2 downvotes will have a plusminus_tally of 1.
|
102
104
|
|
103
|
-
@items = Item.plusminus_tally.limit(10).where('created_at > ?', 2.days.ago).having('
|
105
|
+
@items = Item.plusminus_tally.limit(10).where('created_at > ?', 2.days.ago).having('plusminus_tally > 10')
|
104
106
|
|
105
107
|
#### Lower level queries
|
106
108
|
|
107
109
|
positiveVoteCount = voteable.votes_for
|
108
110
|
negativeVoteCount = voteable.votes_against
|
109
|
-
|
111
|
+
# Votes for minus votes against. If you want more than a few model instances' worth, use `plusminus_tally` instead.
|
112
|
+
plusminus = voteable.plusminus
|
110
113
|
|
111
114
|
voter.voted_for?(voteable) # True if the voter voted for this object.
|
112
115
|
voter.vote_count(:up | :down | :all) # returns the count of +1, -1, or all votes
|
data/lib/acts_as_voteable.rb
CHANGED
@@ -20,12 +20,18 @@ module ThumbsUp
|
|
20
20
|
# This returns an Arel relation, so you can add conditions as you like chained on to
|
21
21
|
# this method call.
|
22
22
|
# i.e. Posts.tally.where('votes.created_at > ?', 2.days.ago)
|
23
|
-
|
23
|
+
# You can also have the upvotes and downvotes returned separately in the same query:
|
24
|
+
# Post.plusminus_tally(:separate_updown => true)
|
25
|
+
def plusminus_tally(params = {})
|
24
26
|
t = self.joins("LEFT OUTER JOIN #{Vote.table_name} ON #{self.table_name}.id = #{Vote.table_name}.voteable_id")
|
25
|
-
t = t.order("
|
27
|
+
t = t.order("plusminus_tally DESC")
|
26
28
|
t = t.group("#{self.table_name}.id")
|
27
29
|
t = t.select("#{self.table_name}.*")
|
28
|
-
t = t.select("SUM(CASE CAST(#{Vote.table_name}.vote AS UNSIGNED) WHEN 1 THEN 1 WHEN 0 THEN -1 ELSE 0 END) AS
|
30
|
+
t = t.select("SUM(CASE CAST(#{Vote.table_name}.vote AS UNSIGNED) WHEN 1 THEN 1 WHEN 0 THEN -1 ELSE 0 END) AS plusminus_tally")
|
31
|
+
if params[:separate_updown]
|
32
|
+
t = t.select("SUM(CASE CAST(#{Vote.table_name}.vote AS UNSIGNED) WHEN 1 THEN 1 WHEN 0 THEN 0 ELSE 0 END) AS up")
|
33
|
+
t = t.select("SUM(CASE CAST(#{Vote.table_name}.vote AS UNSIGNED) WHEN 1 THEN 0 WHEN 0 THEN 1 ELSE 0 END) AS down")
|
34
|
+
end
|
29
35
|
t = t.select("COUNT(#{Vote.table_name}.id) AS vote_count")
|
30
36
|
end
|
31
37
|
|
@@ -72,16 +78,31 @@ module ThumbsUp
|
|
72
78
|
|
73
79
|
# You'll probably want to use this method to display how 'good' a particular voteable
|
74
80
|
# is, and/or sort based on it.
|
81
|
+
# If you're using this for a lot of voteables, then you'd best use the #plusminus_tally
|
82
|
+
# method above.
|
75
83
|
def plusminus
|
76
|
-
votes_for - votes_against
|
84
|
+
respond_to?(:plusminus_tally) ? plusminus_tally : (votes_for - votes_against)
|
85
|
+
end
|
86
|
+
|
87
|
+
# The lower bound of a Wilson Score with a default confidence interval of 95%. Gives a more accurate representation of average rating (plusminus) based on the number of positive ratings and total ratings.
|
88
|
+
# http://evanmiller.org/how-not-to-sort-by-average-rating.html
|
89
|
+
def ci_plusminus(confidence = 0.95)
|
90
|
+
require 'statistics2'
|
91
|
+
n = votes.size
|
92
|
+
if n == 0
|
93
|
+
return 0
|
94
|
+
end
|
95
|
+
z = Statistics2.pnormaldist(1 - (1 - confidence) / 2)
|
96
|
+
phat = 1.0 * votes_for / n
|
97
|
+
(phat + z * z / (2 * n) - z * Math.sqrt((phat * (1 - phat) + z * z / (4 * n)) / n)) / (1 + z * z / n)
|
77
98
|
end
|
78
99
|
|
79
100
|
def votes_count
|
80
|
-
|
101
|
+
votes.size
|
81
102
|
end
|
82
103
|
|
83
104
|
def voters_who_voted
|
84
|
-
|
105
|
+
votes.map(&:voter).uniq
|
85
106
|
end
|
86
107
|
|
87
108
|
def voted_by?(voter)
|
data/lib/thumbs_up/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -7,13 +7,21 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
7
7
|
|
8
8
|
require 'active_record'
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
if ENV['TRAVIS']
|
11
|
+
config = {
|
12
|
+
:adapter => 'mysql2',
|
13
|
+
:database => 'thumbs_up_test',
|
14
|
+
:username => 'root'
|
15
|
+
}
|
16
|
+
else
|
17
|
+
config = {
|
18
|
+
:adapter => 'mysql2',
|
19
|
+
:database => 'thumbs_up_test',
|
20
|
+
:username => 'test',
|
21
|
+
:password => 'test',
|
22
|
+
:socket => '/tmp/mysql.sock'
|
23
|
+
}
|
24
|
+
end
|
17
25
|
|
18
26
|
ActiveRecord::Base.establish_connection(config)
|
19
27
|
ActiveRecord::Base.connection.drop_database config[:database] rescue nil
|
data/test/thumbs_up_test.rb
CHANGED
@@ -73,11 +73,13 @@ class TestThumbsUp < Test::Unit::TestCase
|
|
73
73
|
assert_equal 2, item.votes_for
|
74
74
|
assert_equal 0, item.votes_against
|
75
75
|
assert_equal 2, item.plusminus
|
76
|
+
assert_in_delta 0.34, item.ci_plusminus, 0.01
|
76
77
|
|
77
78
|
user_against.vote_against(item)
|
78
79
|
|
79
80
|
assert_equal 1, item.votes_against
|
80
81
|
assert_equal 1, item.plusminus
|
82
|
+
assert_in_delta 0.20, item.ci_plusminus, 0.01
|
81
83
|
|
82
84
|
assert_equal 3, item.votes_count
|
83
85
|
|
@@ -154,6 +156,18 @@ class TestThumbsUp < Test::Unit::TestCase
|
|
154
156
|
assert_equal 2, Item.tally.where('created_at > ?', 3.days.ago).where('created_at < ?', 4.days.from_now).length
|
155
157
|
end
|
156
158
|
|
159
|
+
def test_tally_count
|
160
|
+
Item.tally.except(:order).count
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_tally_any
|
164
|
+
Item.tally.except(:order).any?
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_tally_empty
|
168
|
+
Item.tally.except(:order).empty?
|
169
|
+
end
|
170
|
+
|
157
171
|
def test_plusminus_tally_not_empty_without_conditions
|
158
172
|
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
159
173
|
assert_equal 1, Item.plusminus_tally.length
|
@@ -271,10 +285,22 @@ class TestThumbsUp < Test::Unit::TestCase
|
|
271
285
|
assert_not_nil user.vote_for(item_for)
|
272
286
|
assert_not_nil user.vote_against(item_against)
|
273
287
|
|
274
|
-
assert_equal item_for, Item.plusminus_tally.reorder('
|
275
|
-
assert_equal item_against, Item.plusminus_tally.reorder('
|
288
|
+
assert_equal item_for, Item.plusminus_tally.reorder('plusminus_tally ASC')[1]
|
289
|
+
assert_equal item_against, Item.plusminus_tally.reorder('plusminus_tally ASC')[0]
|
276
290
|
end
|
277
|
-
|
291
|
+
|
292
|
+
def test_plusminus_tally_count
|
293
|
+
Item.plusminus_tally.except(:order).count
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_plusminus_tally_any
|
297
|
+
Item.plusminus_tally.except(:order).any?
|
298
|
+
end
|
299
|
+
|
300
|
+
def test_plusminus_tally_empty
|
301
|
+
Item.plusminus_tally.except(:order).empty?
|
302
|
+
end
|
303
|
+
|
278
304
|
def test_karma
|
279
305
|
users = (0..1).map{ |u| User.create(:name => "User #{u}") }
|
280
306
|
items = (0..1).map{ |u| users[0].items.create(:name => "Item #{u}", :description => "Item #{u}") }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thumbs_up
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -14,11 +14,11 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2012-
|
17
|
+
date: 2012-04-05 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: activerecord
|
21
|
-
requirement:
|
21
|
+
requirement: !ruby/object:Gem::Requirement
|
22
22
|
none: false
|
23
23
|
requirements:
|
24
24
|
- - ! '>='
|
@@ -26,10 +26,31 @@ dependencies:
|
|
26
26
|
version: '0'
|
27
27
|
type: :runtime
|
28
28
|
prerelease: false
|
29
|
-
version_requirements:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: statistics2
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
30
51
|
- !ruby/object:Gem::Dependency
|
31
52
|
name: simplecov
|
32
|
-
requirement:
|
53
|
+
requirement: !ruby/object:Gem::Requirement
|
33
54
|
none: false
|
34
55
|
requirements:
|
35
56
|
- - ! '>='
|
@@ -37,10 +58,15 @@ dependencies:
|
|
37
58
|
version: '0'
|
38
59
|
type: :development
|
39
60
|
prerelease: false
|
40
|
-
version_requirements:
|
61
|
+
version_requirements: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
41
67
|
- !ruby/object:Gem::Dependency
|
42
68
|
name: bundler
|
43
|
-
requirement:
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
44
70
|
none: false
|
45
71
|
requirements:
|
46
72
|
- - ! '>='
|
@@ -48,10 +74,15 @@ dependencies:
|
|
48
74
|
version: '0'
|
49
75
|
type: :development
|
50
76
|
prerelease: false
|
51
|
-
version_requirements:
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
52
83
|
- !ruby/object:Gem::Dependency
|
53
84
|
name: mysql2
|
54
|
-
requirement:
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
55
86
|
none: false
|
56
87
|
requirements:
|
57
88
|
- - ! '>='
|
@@ -59,10 +90,15 @@ dependencies:
|
|
59
90
|
version: '0'
|
60
91
|
type: :development
|
61
92
|
prerelease: false
|
62
|
-
version_requirements:
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
63
99
|
- !ruby/object:Gem::Dependency
|
64
100
|
name: rake
|
65
|
-
requirement:
|
101
|
+
requirement: !ruby/object:Gem::Requirement
|
66
102
|
none: false
|
67
103
|
requirements:
|
68
104
|
- - ! '>='
|
@@ -70,7 +106,12 @@ dependencies:
|
|
70
106
|
version: '0'
|
71
107
|
type: :development
|
72
108
|
prerelease: false
|
73
|
-
version_requirements:
|
109
|
+
version_requirements: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
74
115
|
description: ThumbsUp provides dead-simple voting capabilities to ActiveRecord models
|
75
116
|
with karma calculation, a la stackoverflow.com.
|
76
117
|
email:
|
@@ -95,7 +136,7 @@ files:
|
|
95
136
|
- MIT-LICENSE
|
96
137
|
- README.md
|
97
138
|
- Rakefile
|
98
|
-
homepage: http://github.com/
|
139
|
+
homepage: http://github.com/bouchard/thumbs_up
|
99
140
|
licenses: []
|
100
141
|
post_install_message:
|
101
142
|
rdoc_options: []
|
@@ -109,7 +150,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
109
150
|
version: '0'
|
110
151
|
segments:
|
111
152
|
- 0
|
112
|
-
hash:
|
153
|
+
hash: 557327561442508507
|
113
154
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
155
|
none: false
|
115
156
|
requirements:
|
@@ -118,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
159
|
version: 1.7.0
|
119
160
|
requirements: []
|
120
161
|
rubyforge_project:
|
121
|
-
rubygems_version: 1.8.
|
162
|
+
rubygems_version: 1.8.21
|
122
163
|
signing_key:
|
123
164
|
specification_version: 3
|
124
165
|
summary: Voting for ActiveRecord with multiple vote sources and karma calculation.
|