thumbs_up 0.5.3 → 0.5.4
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.
- 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
|
+
[](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.
|