thumbs_up 0.4.1 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +13 -0
- data/README.markdown +7 -8
- data/Rakefile +2 -10
- data/VERSION +1 -1
- data/lib/acts_as_voteable.rb +63 -51
- data/lib/has_karma.rb +1 -1
- data/test/test_helper.rb +69 -0
- data/test/test_thumbs_up.rb +229 -0
- data/thumbs_up.gemspec +39 -27
- metadata +71 -34
- data/.gitignore +0 -3
data/Gemfile
ADDED
data/README.markdown
CHANGED
@@ -87,7 +87,7 @@ You can easily retrieve voteable object collections based on the properties of t
|
|
87
87
|
:order => "items.name DESC"
|
88
88
|
})
|
89
89
|
|
90
|
-
This will select the Items with between 1 and 10,000 votes, the votes having been cast within the last two weeks (not including today), then display the 10 last items in an alphabetical list.
|
90
|
+
This will select the Items with between 1 and 10,000 votes, the votes having been cast within the last two weeks (not including today), then display the 10 last items in an alphabetical list. This tallies all votes, regardless of whether they are +1 (up) or -1 (down).
|
91
91
|
|
92
92
|
##### Tally Options:
|
93
93
|
:start_at - Restrict the votes to those created after a certain time
|
@@ -98,13 +98,12 @@ This will select the Items with between 1 and 10,000 votes, the votes having bee
|
|
98
98
|
:at_least - Item must have at least X votes
|
99
99
|
:at_most - Item may not have more than X votes
|
100
100
|
|
101
|
-
##### Tallying Rank
|
101
|
+
##### Tallying Rank ("Plusminus")
|
102
102
|
|
103
|
-
|
104
|
-
|
105
|
-
downvotes will have a rating in this instance 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
|
104
|
+
downvotes will have a plusminus of 1.
|
106
105
|
|
107
|
-
#####
|
106
|
+
##### Plusminus Tally Options:
|
108
107
|
:start_at - Restrict the votes to those created after a certain time
|
109
108
|
:end_at - Restrict the votes to those created before a certain time
|
110
109
|
:conditions - A piece of SQL conditions to add to the query
|
@@ -117,7 +116,7 @@ downvotes will have a rating in this instance of 1.
|
|
117
116
|
|
118
117
|
positiveVoteCount = voteable.votes_for
|
119
118
|
negativeVoteCount = voteable.votes_against
|
120
|
-
plusminus = voteable.plusminus # Votes for minus votes against.
|
119
|
+
plusminus = voteable.plusminus # Votes for, minus votes against.
|
121
120
|
|
122
121
|
voter.voted_for?(voteable) # True if the voter voted for this object.
|
123
122
|
voter.vote_count(:up | :down | :all) # returns the count of +1, -1, or all votes
|
@@ -134,7 +133,7 @@ ThumbsUp by default only allows one vote per user. This can be changed by removi
|
|
134
133
|
|
135
134
|
validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
|
136
135
|
|
137
|
-
#### In the migration:
|
136
|
+
#### In the migration, the unique index:
|
138
137
|
|
139
138
|
add_index :votes, ["voter_id", "voter_type", "voteable_id", "voteable_type"], :unique => true, :name => "uniq_one_vote_only"
|
140
139
|
|
data/Rakefile
CHANGED
@@ -26,21 +26,13 @@ Jeweler::RubygemsDotOrgTasks.new
|
|
26
26
|
require 'rake/testtask'
|
27
27
|
Rake::TestTask.new(:test) do |test|
|
28
28
|
test.libs << 'lib' << 'test'
|
29
|
-
test.
|
29
|
+
test.test_files = Dir.glob("test/**/*_test.rb")
|
30
30
|
test.verbose = true
|
31
31
|
end
|
32
32
|
|
33
|
-
require '
|
34
|
-
Rcov::RcovTask.new do |test|
|
35
|
-
test.libs << 'test'
|
36
|
-
test.pattern = 'test/**/test_*.rb'
|
37
|
-
test.verbose = true
|
38
|
-
end
|
39
|
-
|
40
|
-
require 'rake/rdoctask'
|
33
|
+
require 'rdoc/task'
|
41
34
|
Rake::RDocTask.new do |rdoc|
|
42
35
|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
43
|
-
|
44
36
|
rdoc.rdoc_dir = 'rdoc'
|
45
37
|
rdoc.title = "leaderboard #{version}"
|
46
38
|
rdoc.rdoc_files.include('README*')
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.3
|
data/lib/acts_as_voteable.rb
CHANGED
@@ -17,57 +17,61 @@ module ThumbsUp
|
|
17
17
|
module SingletonMethods
|
18
18
|
|
19
19
|
# The point of this function is to return rankings based on the difference between up and down votes
|
20
|
-
# assuming equal weighting (i.e. a user with 1 up vote and 1 down vote has a Vote_Total of 0.
|
20
|
+
# assuming equal weighting (i.e. a user with 1 up vote and 1 down vote has a Vote_Total of 0.
|
21
21
|
# First the votes table is joined twiced so that the Vote_Total can be calculated for every ID
|
22
|
-
# Then this table is joined against the specific table passed to this function to allow for
|
22
|
+
# Then this table is joined against the specific table passed to this function to allow for
|
23
23
|
# ranking of the items within that table based on the difference between up and down votes.
|
24
24
|
# Options:
|
25
|
-
# :start_at
|
26
|
-
# :end_at
|
27
|
-
# :
|
28
|
-
# :
|
29
|
-
# :
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
25
|
+
# :start_at - Restrict the votes to those created after a certain time
|
26
|
+
# :end_at - Restrict the votes to those created before a certain time
|
27
|
+
# :ascending - Default false - normal order DESC (i.e. highest rank to lowest)
|
28
|
+
# :at_least - Default 1 - Item must have at least X votes
|
29
|
+
# :at_most - Item may not have more than X votes
|
30
|
+
def plusminus_tally(*args)
|
31
|
+
options = args.extract_options!
|
32
|
+
|
33
|
+
tsub0 = Vote
|
34
|
+
tsub0 = tsub0.where("vote = ?", false)
|
35
|
+
tsub0 = tsub0.where("voteable_type = ?", self.name)
|
36
|
+
tsub0 = tsub0.group("voteable_id")
|
37
|
+
tsub0 = tsub0.select("DISTINCT voteable_id, COUNT(vote) as votes_against")
|
38
|
+
|
39
|
+
tsub1 = Vote
|
40
|
+
tsub1 = tsub1.where("vote = ?", true)
|
41
|
+
tsub1 = tsub1.where("voteable_type = ?", self.name)
|
42
|
+
tsub1 = tsub1.group("voteable_id")
|
43
|
+
tsub1 = tsub1.select("DISTINCT voteable_id, COUNT(vote) as votes_for")
|
44
|
+
|
45
|
+
t = self.joins("LEFT OUTER JOIN (SELECT DISTINCT #{Vote.table_name}.*,
|
46
|
+
(COALESCE(vfor.votes_for, 0)-COALESCE(against.votes_against, 0)) AS vote_total
|
47
|
+
FROM (#{Vote.table_name} LEFT JOIN
|
48
|
+
(#{tsub0.to_sql}) AS against ON #{Vote.table_name}.voteable_id = against.voteable_id)
|
49
|
+
LEFT JOIN
|
50
|
+
(#{tsub1.to_sql}) as vfor ON #{Vote.table_name}.voteable_id = vfor.voteable_id)
|
51
|
+
AS joined_#{Vote.table_name} ON #{self.table_name}.#{self.primary_key} =
|
52
|
+
joined_#{Vote.table_name}.voteable_id")
|
53
|
+
|
54
|
+
t = t.where("joined_#{Vote.table_name}.voteable_type = '#{self.name}'")
|
55
|
+
t = t.group("joined_#{Vote.table_name}.voteable_id, joined_#{Vote.table_name}.vote_total, #{column_names_for_tally}")
|
56
|
+
t = t.where("joined_#{Vote.table_name}.created_at >= ?", options[:start_at]) if options[:start_at]
|
57
|
+
t = t.where("joined_#{Vote.table_name}.created_at <= ?", options[:end_at]) if options[:end_at]
|
58
|
+
t = options[:ascending] ? t.order("joined_#{Vote.table_name}.vote_total") : t.order("joined_#{Vote.table_name}.vote_total DESC")
|
59
|
+
|
60
|
+
t = t.having([
|
61
|
+
"COUNT(joined_#{Vote.table_name}.voteable_id) > 0",
|
62
|
+
(options[:at_least] ?
|
63
|
+
"joined_#{Vote.table_name}.vote_total >= #{sanitize(options[:at_least])}" : nil
|
64
|
+
),
|
65
|
+
(options[:at_most] ?
|
66
|
+
"joined_#{Vote.table_name}.vote_total <= #{sanitize(options[:at_most])}" : nil
|
67
|
+
)
|
68
|
+
].compact.join(' AND '))
|
69
|
+
|
70
|
+
t.select("#{self.table_name}.*, joined_#{Vote.table_name}.vote_total")
|
70
71
|
end
|
72
|
+
|
73
|
+
# #rank_tally is depreciated.
|
74
|
+
alias_method :rank_tally, :plusminus_tally
|
71
75
|
|
72
76
|
# Calculate the vote counts for all voteables of my type.
|
73
77
|
# This method returns all voteables with at least one vote.
|
@@ -83,22 +87,22 @@ module ThumbsUp
|
|
83
87
|
# :at_most - Item may not have more than X votes
|
84
88
|
def tally(*args)
|
85
89
|
options = args.extract_options!
|
86
|
-
|
90
|
+
|
87
91
|
# Use the explicit SQL statement throughout for Postgresql compatibility.
|
88
92
|
vote_count = "COUNT(#{Vote.table_name}.voteable_id)"
|
89
|
-
|
93
|
+
|
90
94
|
t = self.where("#{Vote.table_name}.voteable_type = '#{self.name}'")
|
91
95
|
|
92
96
|
# We join so that you can order by columns on the voteable model.
|
93
97
|
t = t.joins("LEFT OUTER JOIN #{Vote.table_name} ON #{self.table_name}.#{self.primary_key} = #{Vote.table_name}.voteable_id")
|
94
|
-
|
98
|
+
|
95
99
|
t = t.group("#{Vote.table_name}.voteable_id, #{column_names_for_tally}")
|
96
100
|
t = t.limit(options[:limit]) if options[:limit]
|
97
101
|
t = t.where("#{Vote.table_name}.created_at >= ?", options[:start_at]) if options[:start_at]
|
98
102
|
t = t.where("#{Vote.table_name}.created_at <= ?", options[:end_at]) if options[:end_at]
|
99
103
|
t = t.where(options[:conditions]) if options[:conditions]
|
100
104
|
t = options[:order] ? t.order(options[:order]) : t.order("#{vote_count} DESC")
|
101
|
-
|
105
|
+
|
102
106
|
# I haven't been able to confirm this bug yet, but Arel (2.0.7) currently blows up
|
103
107
|
# with multiple 'having' clauses. So we hack them all into one for now.
|
104
108
|
# If you have a more elegant solution, a pull request on Github would be greatly appreciated.
|
@@ -129,6 +133,14 @@ module ThumbsUp
|
|
129
133
|
Vote.where(:voteable_id => id, :voteable_type => self.class.name, :vote => false).count
|
130
134
|
end
|
131
135
|
|
136
|
+
def percent_for
|
137
|
+
(votes_for.to_f * 100 / (self.votes.size + 0.0001)).round
|
138
|
+
end
|
139
|
+
|
140
|
+
def percent_against
|
141
|
+
(votes_against.to_f * 100 / (self.votes.size + 0.0001)).round
|
142
|
+
end
|
143
|
+
|
132
144
|
# You'll probably want to use this method to display how 'good' a particular voteable
|
133
145
|
# is, and/or sort based on it.
|
134
146
|
def plusminus
|
data/lib/has_karma.rb
CHANGED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
|
8
|
+
require 'active_record'
|
9
|
+
|
10
|
+
ActiveRecord::Base.establish_connection(
|
11
|
+
:adapter => "sqlite3",
|
12
|
+
:database => ":memory:"
|
13
|
+
)
|
14
|
+
|
15
|
+
ActiveRecord::Migration.verbose = false
|
16
|
+
|
17
|
+
ActiveRecord::Schema.define do
|
18
|
+
create_table :votes, :force => true do |t|
|
19
|
+
t.boolean :vote, :default => false
|
20
|
+
t.references :voteable, :polymorphic => true, :null => false
|
21
|
+
t.references :voter, :polymorphic => true
|
22
|
+
t.timestamps
|
23
|
+
end
|
24
|
+
|
25
|
+
add_index :votes, [:voter_id, :voter_type]
|
26
|
+
add_index :votes, [:voteable_id, :voteable_type]
|
27
|
+
|
28
|
+
# Comment out the line below to allow multiple votes per voter on a single entity.
|
29
|
+
add_index :votes, [:voter_id, :voter_type, :voteable_id, :voteable_type], :unique => true, :name => 'fk_one_vote_per_user_per_entity'
|
30
|
+
|
31
|
+
create_table :users, :force => true do |t|
|
32
|
+
t.string :name
|
33
|
+
t.timestamps
|
34
|
+
end
|
35
|
+
|
36
|
+
create_table :items, :force => true do |t|
|
37
|
+
t.string :name
|
38
|
+
t.string :description
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
require 'thumbs_up'
|
43
|
+
|
44
|
+
class Vote < ActiveRecord::Base
|
45
|
+
|
46
|
+
scope :for_voter, lambda { |*args| where(["voter_id = ? AND voter_type = ?", args.first.id, args.first.class.name]) }
|
47
|
+
scope :for_voteable, lambda { |*args| where(["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.class.name]) }
|
48
|
+
scope :recent, lambda { |*args| where(["created_at > ?", (args.first || 2.weeks.ago)]) }
|
49
|
+
scope :descending, order("created_at DESC")
|
50
|
+
|
51
|
+
belongs_to :voteable, :polymorphic => true
|
52
|
+
belongs_to :voter, :polymorphic => true
|
53
|
+
|
54
|
+
attr_accessible :vote, :voter, :voteable
|
55
|
+
|
56
|
+
# Comment out the line below to allow multiple votes per user.
|
57
|
+
validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
|
58
|
+
end
|
59
|
+
|
60
|
+
class User < ActiveRecord::Base
|
61
|
+
acts_as_voter
|
62
|
+
end
|
63
|
+
|
64
|
+
class Item < ActiveRecord::Base
|
65
|
+
acts_as_voteable
|
66
|
+
end
|
67
|
+
|
68
|
+
class Test::Unit::TestCase
|
69
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'test_helper')
|
2
|
+
|
3
|
+
class TestThumbsUp < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
Vote.delete_all
|
6
|
+
User.delete_all
|
7
|
+
Item.delete_all
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_acts_as_voter_instance_methods
|
11
|
+
user_for = User.create(:name => 'david')
|
12
|
+
user_against = User.create(:name => 'brady')
|
13
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
14
|
+
|
15
|
+
assert_not_nil user_for.vote_for(item)
|
16
|
+
assert_raises(ActiveRecord::RecordInvalid) do
|
17
|
+
user_for.vote_for(item)
|
18
|
+
end
|
19
|
+
assert_equal true, user_for.voted_for?(item)
|
20
|
+
assert_equal false, user_for.voted_against?(item)
|
21
|
+
assert_equal true, user_for.voted_on?(item)
|
22
|
+
assert_equal 1, user_for.vote_count
|
23
|
+
assert_equal 1, user_for.vote_count(:up)
|
24
|
+
assert_equal 0, user_for.vote_count(:down)
|
25
|
+
assert_equal true, user_for.voted_which_way?(item, :up)
|
26
|
+
assert_equal false, user_for.voted_which_way?(item, :down)
|
27
|
+
assert_raises(ArgumentError) do
|
28
|
+
user_for.voted_which_way?(item, :foo)
|
29
|
+
end
|
30
|
+
|
31
|
+
assert_not_nil user_against.vote_against(item)
|
32
|
+
assert_raises(ActiveRecord::RecordInvalid) do
|
33
|
+
user_against.vote_against(item)
|
34
|
+
end
|
35
|
+
assert_equal false, user_against.voted_for?(item)
|
36
|
+
assert_equal true, user_against.voted_against?(item)
|
37
|
+
assert_equal true, user_against.voted_on?(item)
|
38
|
+
assert_equal 1, user_against.vote_count
|
39
|
+
assert_equal 0, user_against.vote_count(:up)
|
40
|
+
assert_equal 1, user_against.vote_count(:down)
|
41
|
+
assert_equal false, user_against.voted_which_way?(item, :up)
|
42
|
+
assert_equal true, user_against.voted_which_way?(item, :down)
|
43
|
+
assert_raises(ArgumentError) do
|
44
|
+
user_against.voted_which_way?(item, :foo)
|
45
|
+
end
|
46
|
+
|
47
|
+
assert_not_nil user_against.vote_exclusively_for(item)
|
48
|
+
assert_equal true, user_against.voted_for?(item)
|
49
|
+
|
50
|
+
assert_not_nil user_for.vote_exclusively_against(item)
|
51
|
+
assert_equal true, user_for.voted_against?(item)
|
52
|
+
|
53
|
+
user_for.clear_votes(item)
|
54
|
+
assert_equal 0, user_for.vote_count
|
55
|
+
|
56
|
+
user_against.clear_votes(item)
|
57
|
+
assert_equal 0, user_against.vote_count
|
58
|
+
|
59
|
+
assert_raises(ArgumentError) do
|
60
|
+
user_for.vote(item, {:direction => :foo})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_acts_as_voteable_instance_methods
|
65
|
+
user_for = User.create(:name => 'david')
|
66
|
+
another_user_for = User.create(:name => 'name')
|
67
|
+
user_against = User.create(:name => 'brady')
|
68
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
69
|
+
|
70
|
+
user_for.vote_for(item)
|
71
|
+
another_user_for.vote_for(item)
|
72
|
+
|
73
|
+
assert_equal 2, item.votes_for
|
74
|
+
assert_equal 0, item.votes_against
|
75
|
+
assert_equal 2, item.plusminus
|
76
|
+
|
77
|
+
user_against.vote_against(item)
|
78
|
+
|
79
|
+
assert_equal 1, item.votes_against
|
80
|
+
assert_equal 1, item.plusminus
|
81
|
+
|
82
|
+
assert_equal 3, item.votes_count
|
83
|
+
|
84
|
+
assert_equal 67, item.percent_for
|
85
|
+
assert_equal 33, item.percent_against
|
86
|
+
|
87
|
+
voters_who_voted = item.voters_who_voted
|
88
|
+
assert_equal 3, voters_who_voted.size
|
89
|
+
assert voters_who_voted.include?(user_for)
|
90
|
+
assert voters_who_voted.include?(another_user_for)
|
91
|
+
assert voters_who_voted.include?(user_against)
|
92
|
+
|
93
|
+
non_voting_user = User.create(:name => 'random')
|
94
|
+
|
95
|
+
assert_equal true, item.voted_by?(user_for)
|
96
|
+
assert_equal true, item.voted_by?(another_user_for)
|
97
|
+
assert_equal true, item.voted_by?(user_against)
|
98
|
+
assert_equal false, item.voted_by?(non_voting_user)
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_tally_empty
|
102
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
103
|
+
|
104
|
+
assert_equal 0, Item.tally.length
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_tally_starts_at
|
108
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
109
|
+
user = User.create(:name => 'david')
|
110
|
+
|
111
|
+
vote = user.vote_for(item)
|
112
|
+
vote.created_at = 3.days.ago
|
113
|
+
vote.save
|
114
|
+
|
115
|
+
assert_equal 0, Item.tally(:start_at => 2.days.ago).length
|
116
|
+
assert_equal 1, Item.tally(:start_at => 4.days.ago).length
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_tally_end_at
|
120
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
121
|
+
user = User.create(:name => 'david')
|
122
|
+
|
123
|
+
vote = user.vote_for(item)
|
124
|
+
vote.created_at = 3.days.from_now
|
125
|
+
vote.save
|
126
|
+
|
127
|
+
assert_equal 0, Item.tally(:end_at => 2.days.from_now).length
|
128
|
+
assert_equal 1, Item.tally(:end_at => 4.days.from_now).length
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_tally_between_start_at_end_at
|
132
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
133
|
+
another_item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
134
|
+
user = User.create(:name => 'david')
|
135
|
+
|
136
|
+
vote = user.vote_for(item)
|
137
|
+
vote.created_at = 2.days.ago
|
138
|
+
vote.save
|
139
|
+
|
140
|
+
vote = user.vote_for(another_item)
|
141
|
+
vote.created_at = 3.days.from_now
|
142
|
+
vote.save
|
143
|
+
|
144
|
+
assert_equal 1, Item.tally(:start_at => 3.days.ago, :end_at => 2.days.from_now).length
|
145
|
+
assert_equal 2, Item.tally(:start_at => 3.days.ago, :end_at => 4.days.from_now).length
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_rank_tally_empty
|
149
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
150
|
+
|
151
|
+
assert_equal 0, Item.rank_tally.length
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_rank_tally_starts_at
|
155
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
156
|
+
user = User.create(:name => 'david')
|
157
|
+
|
158
|
+
vote = user.vote_for(item)
|
159
|
+
vote.created_at = 3.days.ago
|
160
|
+
vote.save
|
161
|
+
|
162
|
+
assert_equal 0, Item.rank_tally(:start_at => 2.days.ago).length
|
163
|
+
assert_equal 1, Item.rank_tally(:start_at => 4.days.ago).length
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_rank_tally_end_at
|
167
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
168
|
+
user = User.create(:name => 'david')
|
169
|
+
|
170
|
+
vote = user.vote_for(item)
|
171
|
+
vote.created_at = 3.days.from_now
|
172
|
+
vote.save
|
173
|
+
|
174
|
+
assert_equal 0, Item.rank_tally(:end_at => 2.days.from_now).length
|
175
|
+
assert_equal 1, Item.rank_tally(:end_at => 4.days.from_now).length
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_rank_tally_between_start_at_end_at
|
179
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
180
|
+
another_item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
181
|
+
user = User.create(:name => 'david')
|
182
|
+
|
183
|
+
vote = user.vote_for(item)
|
184
|
+
vote.created_at = 2.days.ago
|
185
|
+
vote.save
|
186
|
+
|
187
|
+
vote = user.vote_for(another_item)
|
188
|
+
vote.created_at = 3.days.from_now
|
189
|
+
vote.save
|
190
|
+
|
191
|
+
assert_equal 1, Item.rank_tally(:start_at => 3.days.ago, :end_at => 2.days.from_now).length
|
192
|
+
assert_equal 2, Item.rank_tally(:start_at => 3.days.ago, :end_at => 4.days.from_now).length
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_rank_tally_inclusion
|
196
|
+
user = User.create(:name => 'david')
|
197
|
+
item = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
198
|
+
item_not_included = Item.create(:name => 'Playstation', :description => 'Playstation console')
|
199
|
+
|
200
|
+
assert_not_nil user.vote_for(item)
|
201
|
+
|
202
|
+
assert (Item.rank_tally.include? item)
|
203
|
+
assert (not Item.rank_tally.include? item_not_included)
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_rank_tally_default_ordering
|
207
|
+
user = User.create(:name => 'david')
|
208
|
+
item_for = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
209
|
+
item_against = Item.create(:name => 'Playstation', :description => 'Playstation console')
|
210
|
+
|
211
|
+
assert_not_nil user.vote_for(item_for)
|
212
|
+
assert_not_nil user.vote_against(item_against)
|
213
|
+
|
214
|
+
assert_equal item_for, Item.rank_tally[0]
|
215
|
+
assert_equal item_against, Item.rank_tally[1]
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_rank_tally_ascending_ordering
|
219
|
+
user = User.create(:name => 'david')
|
220
|
+
item_for = Item.create(:name => 'XBOX', :description => 'XBOX console')
|
221
|
+
item_against = Item.create(:name => 'Playstation', :description => 'Playstation console')
|
222
|
+
|
223
|
+
assert_not_nil user.vote_for(item_for)
|
224
|
+
assert_not_nil user.vote_against(item_against)
|
225
|
+
|
226
|
+
assert_equal item_for, Item.rank_tally(:ascending => true)[1]
|
227
|
+
assert_equal item_against, Item.rank_tally(:ascending => true)[0]
|
228
|
+
end
|
229
|
+
end
|
data/thumbs_up.gemspec
CHANGED
@@ -1,51 +1,63 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version =
|
7
|
+
s.name = "thumbs_up"
|
8
|
+
s.version = "0.4.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Brady Bouchard", "Peter Jackson", "Cosmin Radoi", "Bence Nagy", "Rob Maddox", "Wojciech
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
s.email =
|
11
|
+
s.authors = ["Brady Bouchard", "Peter Jackson", "Cosmin Radoi", "Bence Nagy", "Rob Maddox", "Wojciech Wn\u{119}trzak"]
|
12
|
+
s.date = "2011-09-15"
|
13
|
+
s.description = "ThumbsUp provides dead-simple voting capabilities to ActiveRecord models with karma calculation, a la stackoverflow.com."
|
14
|
+
s.email = "brady@ldawn.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"README.markdown"
|
17
17
|
]
|
18
18
|
s.files = [
|
19
|
-
".
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
19
|
+
"CHANGELOG.markdown",
|
20
|
+
"Gemfile",
|
21
|
+
"MIT-LICENSE",
|
22
|
+
"README.markdown",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"lib/acts_as_voteable.rb",
|
26
|
+
"lib/acts_as_voter.rb",
|
27
|
+
"lib/generators/thumbs_up/templates/migration.rb",
|
28
|
+
"lib/generators/thumbs_up/templates/vote.rb",
|
29
|
+
"lib/generators/thumbs_up/thumbs_up_generator.rb",
|
30
|
+
"lib/has_karma.rb",
|
31
|
+
"lib/thumbs_up.rb",
|
32
|
+
"rails/init.rb",
|
33
|
+
"test/test_helper.rb",
|
34
|
+
"test/test_thumbs_up.rb",
|
35
|
+
"thumbs_up.gemspec"
|
34
36
|
]
|
35
|
-
s.homepage =
|
36
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.homepage = "http://github.com/brady8/thumbs_up"
|
37
38
|
s.require_paths = ["lib"]
|
38
|
-
s.rubygems_version =
|
39
|
-
s.summary =
|
39
|
+
s.rubygems_version = "1.8.10"
|
40
|
+
s.summary = "Voting for ActiveRecord with multiple vote sources and karma calculation."
|
40
41
|
|
41
42
|
if s.respond_to? :specification_version then
|
42
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
43
|
s.specification_version = 3
|
44
44
|
|
45
45
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
46
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 0"])
|
47
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
46
50
|
else
|
51
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
52
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
53
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
54
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
47
55
|
end
|
48
56
|
else
|
57
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
58
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
59
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
60
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
49
61
|
end
|
50
62
|
end
|
51
63
|
|
metadata
CHANGED
@@ -1,35 +1,75 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: thumbs_up
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.3
|
4
5
|
prerelease:
|
5
|
-
version: 0.4.1
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Brady Bouchard
|
9
9
|
- Peter Jackson
|
10
10
|
- Cosmin Radoi
|
11
11
|
- Bence Nagy
|
12
12
|
- Rob Maddox
|
13
|
-
-
|
13
|
+
- Wojciech Wnętrzak
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
date: 2011-09-15 00:00:00.000000000 Z
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: activerecord
|
21
|
+
requirement: &70315678927000 !ruby/object:Gem::Requirement
|
22
|
+
none: false
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
type: :runtime
|
28
|
+
prerelease: false
|
29
|
+
version_requirements: *70315678927000
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: &70315678926440 !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: *70315678926440
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: jeweler
|
43
|
+
requirement: &70315678925900 !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: *70315678925900
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: simplecov
|
54
|
+
requirement: &70315678925220 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: *70315678925220
|
63
|
+
description: ThumbsUp provides dead-simple voting capabilities to ActiveRecord models
|
64
|
+
with karma calculation, a la stackoverflow.com.
|
23
65
|
email: brady@ldawn.com
|
24
66
|
executables: []
|
25
|
-
|
26
67
|
extensions: []
|
27
|
-
|
28
|
-
extra_rdoc_files:
|
68
|
+
extra_rdoc_files:
|
29
69
|
- README.markdown
|
30
|
-
files:
|
31
|
-
- .gitignore
|
70
|
+
files:
|
32
71
|
- CHANGELOG.markdown
|
72
|
+
- Gemfile
|
33
73
|
- MIT-LICENSE
|
34
74
|
- README.markdown
|
35
75
|
- Rakefile
|
@@ -42,34 +82,31 @@ files:
|
|
42
82
|
- lib/has_karma.rb
|
43
83
|
- lib/thumbs_up.rb
|
44
84
|
- rails/init.rb
|
85
|
+
- test/test_helper.rb
|
86
|
+
- test/test_thumbs_up.rb
|
45
87
|
- thumbs_up.gemspec
|
46
|
-
has_rdoc: true
|
47
88
|
homepage: http://github.com/brady8/thumbs_up
|
48
89
|
licenses: []
|
49
|
-
|
50
90
|
post_install_message:
|
51
|
-
rdoc_options:
|
52
|
-
|
53
|
-
require_paths:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
54
93
|
- lib
|
55
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
95
|
none: false
|
57
|
-
requirements:
|
58
|
-
- -
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version:
|
61
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
101
|
none: false
|
63
|
-
requirements:
|
64
|
-
- -
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version:
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
67
106
|
requirements: []
|
68
|
-
|
69
107
|
rubyforge_project:
|
70
|
-
rubygems_version: 1.
|
108
|
+
rubygems_version: 1.8.10
|
71
109
|
signing_key:
|
72
110
|
specification_version: 3
|
73
111
|
summary: Voting for ActiveRecord with multiple vote sources and karma calculation.
|
74
112
|
test_files: []
|
75
|
-
|
data/.gitignore
DELETED