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 CHANGED
@@ -1 +1,2 @@
1
- gemspec
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 plusminus of 1.
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('plusminus > 10')
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
- plusminus = voteable.plusminus # Votes for, minus votes against.
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
@@ -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
- def plusminus_tally
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("plusminus DESC")
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 plusminus")
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
- self.votes.size
101
+ votes.size
81
102
  end
82
103
 
83
104
  def voters_who_voted
84
- self.votes.map(&:voter).uniq
105
+ votes.map(&:voter).uniq
85
106
  end
86
107
 
87
108
  def voted_by?(voter)
@@ -1,3 +1,3 @@
1
1
  module ThumbsUp
2
- VERSION = '0.5.3'
2
+ VERSION = '0.5.4'
3
3
  end
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
- config = {
11
- :adapter => 'mysql2',
12
- :database => 'thumbs_up_test',
13
- :username => 'test',
14
- :password => 'test',
15
- :socket => '/tmp/mysql.sock'
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
@@ -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('plusminus ASC')[1]
275
- assert_equal item_against, Item.plusminus_tally.reorder('plusminus ASC')[0]
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.3
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-03-05 00:00:00.000000000 Z
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: &70260724487840 !ruby/object:Gem::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: *70260724487840
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: &70260724487260 !ruby/object:Gem::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: *70260724487260
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: &70260724486740 !ruby/object:Gem::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: *70260724486740
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: &70260724502580 !ruby/object:Gem::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: *70260724502580
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: &70260724501960 !ruby/object:Gem::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: *70260724501960
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/brady8/thumbs_up
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: 4476845312579799296
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.17
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.