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 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.