thumbs_up 0.6.5 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 11003465d42393c944f234e11e53ad2d772e4089
4
- data.tar.gz: 6ad0d8e91290c87792123ea3337c714fc5f5ee16
3
+ metadata.gz: 8f277083bf3a45e1984b0a21b5887ba74fb470d7
4
+ data.tar.gz: 10be5d4e7609d85e34dc7a711d592138a18d12e2
5
5
  SHA512:
6
- metadata.gz: fd79c8576b6e65baf65ac5da1921700ac5a07740a43b325f239c90dc4adc11f83480a48f1d3b49def1ff78983722d14609f3df219afa0f9fdd3ff25ce141cce1
7
- data.tar.gz: 7029585f3bbe640e98469557c0bfd5088f7f379d8e403ca73428ddf7d3dc6accb66952a287fa41a88bf4db87002bdfd8814e6bff2499f23f89ac3ac6ffbaba50
6
+ metadata.gz: 30cc98da6527c4b2bd1f58dd31fb0d79ac2da493ae7d26b3d8f4221df04f374f964059ed9de23adaa2137f391d6b2f8022f6d5b667dd6ad2a3b14c10fc8500b8
7
+ data.tar.gz: 421ce7730d7fa9438919a9dabaf5542d0b0916bab00126c67f6c1a1892059cc809fcbe23c55d9d570f653ed2025d1b944ac754220234d2154f3cb9c3c97e691b
data/README.md CHANGED
@@ -11,8 +11,8 @@ Allows an arbitrary number of entities (users, etc.) to vote on models.
11
11
  ### Mixins
12
12
  This plugin introduces three mixins to your recipe book:
13
13
 
14
- 1. **acts\_as\_voteable** : Intended for content objects like Posts, Comments, etc.
15
- 2. **acts\_as\_voter** : Intended for voting entities, like Users.
14
+ 1. **acts\_as\_voteable** : Intended for content objects like Posts, Comments, etc. * (See *Configuration* below for caveats)
15
+ 2. **acts\_as\_voter** : Intended for voting entities, like Users. * (See *Configuration* below for caveats)
16
16
  3. **has\_karma** : Adds some helpers to acts\_as\_voter models for calculating karma.
17
17
 
18
18
  ### Inspiration
@@ -31,6 +31,22 @@ Installation
31
31
  rails generate thumbs_up
32
32
  rake db:migrate
33
33
 
34
+ Configuration
35
+ =============
36
+
37
+ The relationship setup by the acts_as_voteable and acts_as_voter mixins both default to `votes`. This causes one to obscure the other if you have a single class that votes on other instances of the same class. If you have this scenario:
38
+
39
+ class User < ActiveRecord::Base
40
+ acts_as_voter # relationship :votes will be obscured by the same named relationship from acts_as_voteable :(
41
+ acts_as_voteable
42
+ end
43
+
44
+ Configure alternate relationship names in an initializer at `config/initializers/thumbs_up.rb`:
45
+
46
+ ThumbsUp.configuration.voteable_relationship_name = :votes_on # defaults to :votes
47
+ ThumbsUp.configuration.voter_relationship_name = :votes_by # defaults to :votes
48
+
49
+
34
50
  Usage
35
51
  =====
36
52
 
@@ -54,7 +70,9 @@ Usage
54
70
  # Each question has a submitter_id column that tracks the user who submitted it.
55
71
  # The option :weight value will be multiplied to any karma from that voteable model (defaults to 1).
56
72
  # You can track any voteable model.
57
- has_karma(:questions, :as => :submitter, :weight => 0.5)
73
+ has_karma :questions, :as => :submitter, :weight => 0.5
74
+ # Karma by default is only calculated from upvotes. If you pass an array to the weight option, you can count downvotes as well (below, downvotes count for half as much karma against you):
75
+ has_karma :questions, :as => :submitter, :weight => [ 1, 0.5 ]
58
76
  end
59
77
 
60
78
  class Robot < ActiveRecord::Base
@@ -65,24 +65,24 @@ module ThumbsUp
65
65
  module InstanceMethods
66
66
 
67
67
  # wraps the dynamic, configured, relationship name
68
- def _votes_by
68
+ def _votes_on
69
69
  self.send(ThumbsUp.configuration[:voteable_relationship_name])
70
70
  end
71
71
 
72
72
  def votes_for
73
- self._votes_by.where(:vote => true).count
73
+ self._votes_on.where(:vote => true).count
74
74
  end
75
75
 
76
76
  def votes_against
77
- self._votes_by.where(:vote => false).count
77
+ self._votes_on.where(:vote => false).count
78
78
  end
79
79
 
80
80
  def percent_for
81
- (votes_for.to_f * 100 / (self._votes_by.size + 0.0001)).round
81
+ (votes_for.to_f * 100 / (self._votes_on.size + 0.0001)).round
82
82
  end
83
83
 
84
84
  def percent_against
85
- (votes_against.to_f * 100 / (self._votes_by.size + 0.0001)).round
85
+ (votes_against.to_f * 100 / (self._votes_on.size + 0.0001)).round
86
86
  end
87
87
 
88
88
  # You'll probably want to use this method to display how 'good' a particular voteable
@@ -97,7 +97,7 @@ module ThumbsUp
97
97
  # http://evanmiller.org/how-not-to-sort-by-average-rating.html
98
98
  def ci_plusminus(confidence = 0.95)
99
99
  require 'statistics2'
100
- n = votes.size
100
+ n = self._votes_on.size
101
101
  if n == 0
102
102
  return 0
103
103
  end
@@ -107,19 +107,19 @@ module ThumbsUp
107
107
  end
108
108
 
109
109
  def votes_count
110
- votes.size
110
+ _votes_on.size
111
111
  end
112
112
 
113
113
  def voters_who_voted
114
- votes.map(&:voter).uniq
114
+ _votes_on.map(&:voter).uniq
115
115
  end
116
116
 
117
117
  def voters_who_voted_for
118
- votes.where(:vote => true).map(&:voter).uniq
118
+ _votes_on.where(:vote => true).map(&:voter).uniq
119
119
  end
120
120
 
121
121
  def voters_who_voted_against
122
- votes.where(:vote => false).map(&:voter).uniq
122
+ _votes_on.where(:vote => false).map(&:voter).uniq
123
123
  end
124
124
 
125
125
  def voted_by?(voter)
data/lib/acts_as_voter.rb CHANGED
@@ -31,8 +31,8 @@ module ThumbsUp #:nodoc:
31
31
  module InstanceMethods
32
32
 
33
33
  # wraps the dynamic, configured, relationship name
34
- def _votes_on
35
- self.send(ThumbsUp.configuration[:voteable_relationship_name])
34
+ def _votes_by
35
+ self.send(ThumbsUp.configuration[:voter_relationship_name])
36
36
  end
37
37
 
38
38
  # Usage user.vote_count(:up) # All +1 votes
data/lib/has_karma.rb CHANGED
@@ -13,7 +13,7 @@ module ThumbsUp #:nodoc:
13
13
  include ThumbsUp::Karma::InstanceMethods
14
14
  extend ThumbsUp::Karma::SingletonMethods
15
15
  self.karmic_objects ||= {}
16
- self.karmic_objects[voteable_type.to_s.classify.constantize] = [ (options[:as] ? options[:as].to_s.foreign_key : self.name.foreign_key), (options[:weight] || 1).to_f ]
16
+ self.karmic_objects[voteable_type.to_s.classify.constantize] = [ (options[:as] ? options[:as].to_s.foreign_key : self.name.foreign_key), [ (options[:weight] || 1) ].flatten.map(&:to_f) ]
17
17
  end
18
18
  end
19
19
 
@@ -30,10 +30,16 @@ module ThumbsUp #:nodoc:
30
30
  module InstanceMethods
31
31
  def karma(options = {})
32
32
  self.class.base_class.karmic_objects.collect do |object, attr|
33
- v = object.where(["#{Vote.table_name}.vote = ?", true]).where(["#{self.class.base_class.table_name}.#{self.class.base_class.primary_key} = ?", self.id])
33
+ v = object.where(["#{self.class.base_class.table_name}.#{self.class.base_class.primary_key} = ?", self.id])
34
34
  v = v.joins("INNER JOIN #{Vote.table_name} ON #{Vote.table_name}.voteable_type = '#{object.to_s}' AND #{Vote.table_name}.voteable_id = #{object.table_name}.#{object.primary_key}")
35
35
  v = v.joins("INNER JOIN #{self.class.base_class.table_name} ON #{self.class.base_class.table_name}.#{self.class.base_class.primary_key} = #{object.table_name}.#{attr[0]}")
36
- (v.count.to_f * attr[1]).round
36
+ upvotes = v.where(["#{Vote.table_name}.vote = ?", true])
37
+ downvotes = v.where(["#{Vote.table_name}.vote = ?", false])
38
+ if attr[1].length == 1 # Only count upvotes, not downvotes.
39
+ (upvotes.count.to_f * attr[1].first).round
40
+ else
41
+ (upvotes.count.to_f * attr[1].first - downvotes.count.to_f * attr[1].last).round
42
+ end
37
43
  end.sum
38
44
  end
39
45
  end
data/lib/thumbs_up.rb CHANGED
@@ -17,8 +17,8 @@ module ThumbsUp
17
17
  #
18
18
  # @example
19
19
  # ThumbsUp.configure do |config|
20
- # config.voteable_relationship_name = :votes_by
21
- # config.voter_relationship_name = :votes_on
20
+ # config.voteable_relationship_name = :votes_on
21
+ # config.voter_relationship_name = :votes_by
22
22
  # end
23
23
  def configure
24
24
  yield(configuration)
@@ -1,3 +1,3 @@
1
1
  module ThumbsUp
2
- VERSION = '0.6.5'
2
+ VERSION = '0.6.6'
3
3
  end
data/test/test_helper.rb CHANGED
@@ -89,6 +89,24 @@ ActiveRecord::Schema.define do
89
89
  t.string :name
90
90
  t.string :description
91
91
  end
92
+
93
+ create_table :user_customs, :force => true do |t|
94
+ t.string :name
95
+ t.timestamps
96
+ end
97
+
98
+ create_table :item_customs, :force => true do |t|
99
+ t.integer :user_id
100
+ t.string :name
101
+ t.string :description
102
+ end
103
+
104
+ create_table :other_item_customs, :force => true do |t|
105
+ t.integer :user_id
106
+ t.string :name
107
+ t.string :description
108
+ end
109
+
92
110
  end
93
111
 
94
112
  require 'thumbs_up'
@@ -110,20 +128,61 @@ class Vote < ActiveRecord::Base
110
128
  end
111
129
 
112
130
  class Item < ActiveRecord::Base
131
+ # This is default, however because the setting is app-wide, and changed elsewhere, we need to be explicit
132
+ ThumbsUp.configuration.voteable_relationship_name = :votes
133
+ ThumbsUp.configuration.voter_relationship_name = :votes
113
134
  acts_as_voteable
114
135
  belongs_to :user
115
136
  end
116
137
 
117
138
  class OtherItem < ActiveRecord::Base
139
+ # This is default, however because the setting is app-wide, and changed elsewhere, we need to be explicit
140
+ ThumbsUp.configuration.voteable_relationship_name = :votes
141
+ ThumbsUp.configuration.voter_relationship_name = :votes
118
142
  acts_as_voteable
119
143
  belongs_to :user
120
144
  end
121
145
 
122
146
  class User < ActiveRecord::Base
147
+ # This is default, however because the setting is app-wide, and changed elsewhere, we need to be explicit
148
+ ThumbsUp.configuration.voteable_relationship_name = :votes
149
+ ThumbsUp.configuration.voter_relationship_name = :votes
123
150
  acts_as_voter
124
151
  has_many :items
125
152
  has_karma(:items)
126
153
  end
127
154
 
155
+ class ItemCustom < ActiveRecord::Base
156
+ ThumbsUp.configuration.voteable_relationship_name = :votes_on
157
+ ThumbsUp.configuration.voter_relationship_name = :votes_by
158
+ acts_as_voteable
159
+ belongs_to :user
160
+ end
161
+
162
+ class OtherItemCustom < ActiveRecord::Base
163
+ ThumbsUp.configuration.voteable_relationship_name = :votes_on
164
+ ThumbsUp.configuration.voter_relationship_name = :votes_by
165
+ acts_as_voteable
166
+ belongs_to :user
167
+ end
168
+
169
+ class UserCustom < ActiveRecord::Base
170
+ ThumbsUp.configuration.voteable_relationship_name = :votes_on
171
+ ThumbsUp.configuration.voter_relationship_name = :votes_by
172
+ acts_as_voter
173
+ has_many :items
174
+ has_karma :items
175
+
176
+ def self.weighted_has_karma
177
+ self.karmic_objects = nil
178
+ has_karma :items, :weight => [ 10, 15 ]
179
+ end
180
+
181
+ def self.upvote_only_has_karma
182
+ self.karmic_objects = nil
183
+ has_karma :items, :weight => 1.3
184
+ end
185
+ end
186
+
128
187
  class Test::Unit::TestCase
129
188
  end
@@ -8,6 +8,10 @@ class TestThumbsUp < Test::Unit::TestCase
8
8
  end
9
9
 
10
10
  def test_acts_as_voter_instance_methods
11
+ # Because these are set in several places we need to ensure the defaults are set here.
12
+ ThumbsUp.configuration.voteable_relationship_name = :votes
13
+ ThumbsUp.configuration.voter_relationship_name = :votes
14
+
11
15
  user_for = User.create(:name => 'david')
12
16
  user_against = User.create(:name => 'brady')
13
17
  item = Item.create(:name => 'XBOX', :description => 'XBOX console')
@@ -79,6 +83,10 @@ class TestThumbsUp < Test::Unit::TestCase
79
83
  end
80
84
 
81
85
  def test_acts_as_voteable_instance_methods
86
+ # Because these are set in several places we need to ensure the defaults are set here.
87
+ ThumbsUp.configuration.voteable_relationship_name = :votes
88
+ ThumbsUp.configuration.voter_relationship_name = :votes
89
+
82
90
  item = Item.create(:name => 'XBOX', :description => 'XBOX console')
83
91
 
84
92
  assert_equal 0, item.ci_plusminus
@@ -138,6 +146,139 @@ class TestThumbsUp < Test::Unit::TestCase
138
146
  assert_equal false, item.voted_by?(non_voting_user)
139
147
  end
140
148
 
149
+ def test_acts_as_voter_configuration
150
+ ThumbsUp.configuration.voteable_relationship_name = :votes_on
151
+ ThumbsUp.configuration.voter_relationship_name = :votes_by
152
+
153
+ user_for = UserCustom.create(:name => 'david')
154
+ user_against = UserCustom.create(:name => 'brady')
155
+ item = ItemCustom.create(:name => 'XBOX', :description => 'XBOX console')
156
+
157
+ # We have changed the name of the relationship, so `votes` is not defined.
158
+ assert_raises(NoMethodError) do
159
+ user_for.votes
160
+ end
161
+
162
+ assert_not_nil user_for.vote_for(item)
163
+ assert_raises(ActiveRecord::RecordInvalid) do
164
+ user_for.vote_for(item)
165
+ end
166
+ assert_equal true, user_for.voted_for?(item)
167
+ assert_equal false, user_for.voted_against?(item)
168
+ assert_equal true, user_for.voted_on?(item)
169
+ assert_equal 1, user_for.vote_count
170
+ assert_equal 1, user_for.vote_count(:up)
171
+ assert_equal 0, user_for.vote_count(:down)
172
+ assert_equal true, user_for.voted_which_way?(item, :up)
173
+ assert_equal false, user_for.voted_which_way?(item, :down)
174
+ assert_equal 1, user_for.votes_by.where(:voteable_type => 'ItemCustom').count
175
+ assert_equal 0, user_for.votes_by.where(:voteable_type => 'AnotherItem').count
176
+ assert_raises(ArgumentError) do
177
+ user_for.voted_which_way?(item, :foo)
178
+ end
179
+
180
+ assert_not_nil user_against.vote_against(item)
181
+ assert_raises(ActiveRecord::RecordInvalid) do
182
+ user_against.vote_against(item)
183
+ end
184
+ assert_equal false, user_against.voted_for?(item)
185
+ assert_equal true, user_against.voted_against?(item)
186
+ assert_equal true, user_against.voted_on?(item)
187
+ assert_equal 1, user_against.vote_count
188
+ assert_equal 0, user_against.vote_count(:up)
189
+ assert_equal 1, user_against.vote_count(:down)
190
+ assert_equal false, user_against.voted_which_way?(item, :up)
191
+ assert_equal true, user_against.voted_which_way?(item, :down)
192
+ assert_raises(ArgumentError) do
193
+ user_against.voted_which_way?(item, :foo)
194
+ end
195
+
196
+ assert_not_nil user_against.vote_exclusively_for(item)
197
+ assert_equal true, user_against.voted_for?(item)
198
+
199
+ assert_not_nil user_for.vote_exclusively_against(item)
200
+ assert_equal true, user_for.voted_against?(item)
201
+
202
+ user_for.unvote_for(item)
203
+ assert_equal 0, user_for.vote_count
204
+
205
+ user_against.unvote_for(item)
206
+ assert_equal 0, user_against.vote_count
207
+
208
+ assert_raises(ArgumentError) do
209
+ user_for.vote(item, {:direction => :foo})
210
+ end
211
+ end
212
+
213
+ def test_acts_as_voteable_configuration
214
+ ThumbsUp.configuration.voteable_relationship_name = :votes_on
215
+ ThumbsUp.configuration.voter_relationship_name = :votes_by
216
+
217
+ item = ItemCustom.create(:name => 'XBOX', :description => 'XBOX console')
218
+
219
+ assert_equal 0, item.ci_plusminus
220
+
221
+ user_for = UserCustom.create(:name => 'david')
222
+ another_user_for = UserCustom.create(:name => 'name')
223
+ user_against = UserCustom.create(:name => 'brady')
224
+ another_user_against = UserCustom.create(:name => 'name')
225
+
226
+ # We have changed the name of the relationship, so `votes` is not defined.
227
+ assert_raises(NoMethodError) do
228
+ item.votes
229
+ end
230
+
231
+ user_for.vote_for(item)
232
+ another_user_for.vote_for(item)
233
+ # Use #reload to force reloading of votes from the database,
234
+ # otherwise these tests fail after "assert_equal 0, item.ci_plusminus" caches
235
+ # the votes. We hack this as caching is the correct behavious, per-request,
236
+ # in production.
237
+ item.reload
238
+
239
+ assert_equal 2, item.votes_for
240
+ assert_equal 0, item.votes_against
241
+ assert_equal 2, item.plusminus
242
+ assert_in_delta 0.34, item.ci_plusminus, 0.01
243
+
244
+ user_against.vote_against(item)
245
+
246
+ assert_equal 1, item.votes_against
247
+ assert_equal 1, item.plusminus
248
+ assert_in_delta 0.20, item.ci_plusminus, 0.01
249
+
250
+ assert_equal 3, item.votes_count
251
+
252
+ assert_equal 67, item.percent_for
253
+ assert_equal 33, item.percent_against
254
+
255
+ voters_who_voted = item.voters_who_voted
256
+ assert_equal 3, voters_who_voted.size
257
+ assert voters_who_voted.include?(user_for)
258
+ assert voters_who_voted.include?(another_user_for)
259
+ assert voters_who_voted.include?(user_against)
260
+
261
+ voters_who_voted_for = item.voters_who_voted_for
262
+ assert_equal 2, voters_who_voted_for.size
263
+ assert voters_who_voted_for.include?(user_for)
264
+ assert voters_who_voted_for.include?(another_user_for)
265
+
266
+ another_user_against.vote_against(item)
267
+
268
+ voters_who_voted_against = item.voters_who_voted_against
269
+ assert_equal 2, voters_who_voted_against.size
270
+ assert voters_who_voted_against.include?(user_against)
271
+ assert voters_who_voted_against.include?(another_user_against)
272
+
273
+ non_voting_user = UserCustom.create(:name => 'voteable_configuration')
274
+
275
+ assert_equal true, item.voted_by?(user_for)
276
+ assert_equal true, item.voted_by?(another_user_for)
277
+ assert_equal true, item.voted_by?(user_against)
278
+ assert_equal false, item.voted_by?(non_voting_user)
279
+ end
280
+
281
+ # Duplicated method name, why?
141
282
  def test_tally_empty
142
283
  item = Item.create(:name => 'XBOX', :description => 'XBOX console')
143
284
  # COUNT(#{Vote.table_name}.id) is equivalent to aliased column `vote_count` - Postgres
@@ -399,6 +540,28 @@ class TestThumbsUp < Test::Unit::TestCase
399
540
  assert_equal 0, users[1].karma
400
541
  end
401
542
 
543
+ def test_karma_with_upvote_weights
544
+ User.upvote_only_has_karma
545
+ users = (0..1).map{ |u| User.create(:name => "User #{u}") }
546
+ items = (0..1).map{ |u| users[0].items.create(:name => "Item #{u}", :description => "Item #{u}") }
547
+ users.each{ |u| items.each { |i| u.vote_for(i) } }
548
+
549
+ assert_equal (4 * 1.3).round, users[0].karma
550
+ assert_equal 0, users[1].karma
551
+ end
552
+
553
+ def test_karma_with_both_upvote_and_downvote_weights
554
+ User.weighted_has_karma
555
+ for_users = (0..1).map{ |u| User.create(:name => "For User #{u}") }
556
+ against_users = (0..2).map{ |u| User.create(:name => "Against User #{u}") }
557
+ items = (0..1).map{ |u| for_users[0].items.create(:name => "Item #{u}", :description => "Item #{u}") }
558
+ for_users.each{ |u| items.each { |i| u.vote_for(i) } }
559
+ against_users.each{ |u| items.each { |i| u.vote_against(i) } }
560
+
561
+ assert_equal 2 * (10 * 2 - 15 * 3).round, for_users[0].karma
562
+ assert_equal 0, for_users[1].karma
563
+ end
564
+
402
565
  def test_plusminus_tally_scopes_by_voteable_type
403
566
  user = User.create(:name => 'david')
404
567
  item = Item.create(:name => 'XBOX', :description => 'XBOX console')
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.6.5
4
+ version: 0.6.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brady Bouchard
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-07-05 00:00:00.000000000 Z
16
+ date: 2013-09-02 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: activerecord