thumbs_up 0.3.2 → 0.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/.gitignore +1 -0
- data/README.markdown +19 -1
- data/Rakefile +46 -15
- data/VERSION +1 -1
- data/lib/acts_as_voteable.rb +54 -0
- data/lib/acts_as_voter.rb +1 -1
- data/lib/generators/thumbs_up/templates/migration.rb +3 -1
- data/lib/generators/thumbs_up/templates/vote.rb +2 -1
- data/lib/generators/thumbs_up/thumbs_up_generator.rb +2 -0
- data/rails/init.rb +3 -3
- metadata +4 -12
data/.gitignore
CHANGED
data/README.markdown
CHANGED
|
@@ -65,7 +65,7 @@ Usage
|
|
|
65
65
|
voter.vote(voteable, vote) # Adds either a +1 or -1 vote: vote => true (+1), vote => false (-1)
|
|
66
66
|
|
|
67
67
|
voter.vote_exclusively_for(voteable) # Removes any previous votes by that particular voter, and votes for.
|
|
68
|
-
voter.
|
|
68
|
+
voter.vote_exclusively_against(voteable) # Removes any previous votes by that particular voter, and votes against.
|
|
69
69
|
|
|
70
70
|
### Querying votes
|
|
71
71
|
|
|
@@ -98,6 +98,21 @@ 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
|
|
102
|
+
|
|
103
|
+
Similar to tallying votes, but this will actually return voteable object collections based on a rating
|
|
104
|
+
system where up votes and down votes get equal rating. For Instance, a voteable with 3 upvotes and 2
|
|
105
|
+
downvotes will have a rating in this instance of 1.
|
|
106
|
+
|
|
107
|
+
##### Rank_Tally Options:
|
|
108
|
+
:start_at - Restrict the votes to those created after a certain time
|
|
109
|
+
:end_at - Restrict the votes to those created before a certain time
|
|
110
|
+
:conditions - A piece of SQL conditions to add to the query
|
|
111
|
+
:limit - The maximum number of voteables to return
|
|
112
|
+
:ascending - Boolean Default false. If specified true, results will be returned in ascending order (from bottom up)
|
|
113
|
+
:at_least - Item must have at least X votes
|
|
114
|
+
:at_most - Item may not have more than X votes
|
|
115
|
+
|
|
101
116
|
#### Lower level queries
|
|
102
117
|
|
|
103
118
|
positiveVoteCount = voteable.votes_for
|
|
@@ -123,6 +138,9 @@ ThumbsUp by default only allows one vote per user. This can be changed by removi
|
|
|
123
138
|
|
|
124
139
|
add_index :votes, ["voter_id", "voter_type", "voteable_id", "voteable_type"], :unique => true, :name => "uniq_one_vote_only"
|
|
125
140
|
|
|
141
|
+
You can also use `--unique-voting false` when running the generator command:
|
|
142
|
+
|
|
143
|
+
rails generate thumbs_up --unique-voting false
|
|
126
144
|
|
|
127
145
|
Credits
|
|
128
146
|
=======
|
data/Rakefile
CHANGED
|
@@ -1,19 +1,50 @@
|
|
|
1
1
|
# encoding: UTF-8
|
|
2
2
|
require 'rubygems'
|
|
3
|
-
require '
|
|
3
|
+
require 'bundler'
|
|
4
4
|
|
|
5
5
|
begin
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
Bundler.setup(:default, :development)
|
|
7
|
+
rescue Bundler::BundlerError => e
|
|
8
|
+
$stderr.puts e.message
|
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
|
10
|
+
exit e.status_code
|
|
11
|
+
end
|
|
12
|
+
require 'rake'
|
|
13
|
+
|
|
14
|
+
require 'jeweler'
|
|
15
|
+
Jeweler::Tasks.new do |gem|
|
|
16
|
+
gem.name = "thumbs_up"
|
|
17
|
+
gem.summary = "Voting for ActiveRecord with multiple vote sources and karma calculation."
|
|
18
|
+
gem.description = "ThumbsUp provides dead-simple voting capabilities to ActiveRecord models with karma calculation, a la stackoverflow.com."
|
|
19
|
+
gem.email = "brady@ldawn.com"
|
|
20
|
+
gem.homepage = "http://github.com/brady8/thumbs_up"
|
|
21
|
+
gem.authors = ["Brady Bouchard", "Peter Jackson", "Cosmin Radoi", "Bence Nagy", "Rob Maddox", "Wojciech Wnętrzak"]
|
|
22
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
|
23
|
+
end
|
|
24
|
+
Jeweler::RubygemsDotOrgTasks.new
|
|
25
|
+
|
|
26
|
+
require 'rake/testtask'
|
|
27
|
+
Rake::TestTask.new(:test) do |test|
|
|
28
|
+
test.libs << 'lib' << 'test'
|
|
29
|
+
test.pattern = 'test/**/test_*.rb'
|
|
30
|
+
test.verbose = true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
require 'rcov/rcovtask'
|
|
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'
|
|
41
|
+
Rake::RDocTask.new do |rdoc|
|
|
42
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
|
43
|
+
|
|
44
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
45
|
+
rdoc.title = "leaderboard #{version}"
|
|
46
|
+
rdoc.rdoc_files.include('README*')
|
|
47
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
task :default => :test
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.4
|
data/lib/acts_as_voteable.rb
CHANGED
|
@@ -15,6 +15,60 @@ module ThumbsUp
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
module SingletonMethods
|
|
18
|
+
|
|
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.
|
|
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
|
|
23
|
+
# ranking of the items within that table based on the difference between up and down votes.
|
|
24
|
+
# Options:
|
|
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
|
+
# :conditions - A piece of SQL conditions to add to the query
|
|
28
|
+
# :limit - The maximum number of voteables to return
|
|
29
|
+
# :ascending - Default false - normal order DESC (i.e. highest rank to lowest)
|
|
30
|
+
# :at_least - Item must have at least X votes
|
|
31
|
+
# :at_most - Item may not have more than X votes
|
|
32
|
+
def rank_tally(*args)
|
|
33
|
+
options = args.extract_options!
|
|
34
|
+
|
|
35
|
+
tsub0 = Vote
|
|
36
|
+
tsub0 = tsub0.where("vote = ?", false)
|
|
37
|
+
tsub0 = tsub0.where("voteable_type = ?", self.name)
|
|
38
|
+
tsub0 = tsub0.group("voteable_id")
|
|
39
|
+
tsub0 = tsub0.select("DISTINCT voteable_id, COUNT(vote) as Votes_Against")
|
|
40
|
+
|
|
41
|
+
tsub1 = Vote
|
|
42
|
+
tsub1 = tsub1.where("vote = ?", true)
|
|
43
|
+
tsub1 = tsub1.where("voteable_type = ?", self.name)
|
|
44
|
+
tsub1 = tsub1.group("voteable_id")
|
|
45
|
+
tsub1 = tsub1.select("DISTINCT voteable_id, COUNT(vote) as Votes_For")
|
|
46
|
+
|
|
47
|
+
t = self.joins("LEFT OUTER JOIN (SELECT DISTINCT #{Vote.table_name}.*,
|
|
48
|
+
(COALESCE(vfor.Votes_For, 0)-COALESCE(against.Votes_Against, 0)) AS Vote_Total
|
|
49
|
+
FROM (#{Vote.table_name} LEFT JOIN
|
|
50
|
+
(#{tsub0.to_sql}) AS against ON #{Vote.table_name}.voteable_id = against.voteable_id)
|
|
51
|
+
LEFT JOIN
|
|
52
|
+
(#{tsub1.to_sql}) as vfor ON #{Vote.table_name}.voteable_id = vfor.voteable_id)
|
|
53
|
+
AS joined_#{Vote.table_name} ON #{self.table_name}.#{self.primary_key} =
|
|
54
|
+
joined_#{Vote.table_name}.voteable_id")
|
|
55
|
+
|
|
56
|
+
t = t.where("joined_#{Vote.table_name}.voteable_type = '#{self.name}'")
|
|
57
|
+
t = t.group("joined_#{Vote.table_name}.voteable_id, joined_#{Vote.table_name}.Vote_Total, #{column_names_for_tally}")
|
|
58
|
+
t = t.limit(options[:limit]) if options[:limit]
|
|
59
|
+
t = t.where("joined_#{Vote.table_name}.created_at >= ?", options[:start_at]) if options[:start_at]
|
|
60
|
+
t = t.where("joined_#{Vote.table_name}.created_at <= ?", options[:end_at]) if options[:end_at]
|
|
61
|
+
t = t.where(options[:conditions]) if options[:conditions]
|
|
62
|
+
t = options[:ascending] ? t.order("joined_#{Vote.table_name}.Vote_Total")
|
|
63
|
+
: t.order("joined_#{Vote.table_name}.Vote_Total DESC")
|
|
64
|
+
|
|
65
|
+
t = t.having(["COUNT(joined_#{Vote.table_name}.voteable_id) > 0",
|
|
66
|
+
(options[:at_least] ? "joined_votes.Vote_Total >= #{sanitize(options[:at_least])}" : nil),
|
|
67
|
+
(options[:at_most] ? "joined_votes.Vote_Total <= #{sanitize(options[:at_most])}" : nil)
|
|
68
|
+
].compact.join(' AND '))
|
|
69
|
+
|
|
70
|
+
t.select("#{self.table_name}.*, joined_#{Vote.table_name}.Vote_Total")
|
|
71
|
+
end
|
|
18
72
|
|
|
19
73
|
# Calculate the vote counts for all voteables of my type.
|
|
20
74
|
# This method returns all voteables with at least one vote.
|
data/lib/acts_as_voter.rb
CHANGED
|
@@ -75,7 +75,7 @@ module ThumbsUp #:nodoc:
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def vote(voteable, options = {})
|
|
78
|
-
raise ArgumentError "you must specify :up or :down in order to vote" unless options[:direction] && [:up, :down].include?(options[:direction].to_sym)
|
|
78
|
+
raise ArgumentError, "you must specify :up or :down in order to vote" unless options[:direction] && [:up, :down].include?(options[:direction].to_sym)
|
|
79
79
|
if options[:exclusive]
|
|
80
80
|
self.clear_votes(voteable)
|
|
81
81
|
end
|
|
@@ -12,8 +12,10 @@ class ThumbsUpMigration < ActiveRecord::Migration
|
|
|
12
12
|
add_index :votes, [:voter_id, :voter_type]
|
|
13
13
|
add_index :votes, [:voteable_id, :voteable_type]
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
<% if options[:unique_voting] == true %>
|
|
16
|
+
# Comment out the line below to allow multiple votes per voter on a single entity.
|
|
16
17
|
add_index :votes, [:voter_id, :voter_type, :voteable_id, :voteable_type], :unique => true, :name => 'fk_one_vote_per_user_per_entity'
|
|
18
|
+
<% end %>
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def self.down
|
|
@@ -10,7 +10,8 @@ class Vote < ActiveRecord::Base
|
|
|
10
10
|
|
|
11
11
|
attr_accessible :vote, :voter, :voteable
|
|
12
12
|
|
|
13
|
+
<% if options[:unique_voting] == true %>
|
|
13
14
|
# Comment out the line below to allow multiple votes per user.
|
|
14
15
|
validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
|
|
15
|
-
|
|
16
|
+
<% end %>
|
|
16
17
|
end
|
|
@@ -5,6 +5,8 @@ class ThumbsUpGenerator < Rails::Generators::Base
|
|
|
5
5
|
include Rails::Generators::Migration
|
|
6
6
|
|
|
7
7
|
source_root File.expand_path('../templates', __FILE__)
|
|
8
|
+
|
|
9
|
+
class_option :unique_voting, :type => :boolean, :default => true, :desc => 'Do you want only one vote allowed per voter? (default: true)'
|
|
8
10
|
|
|
9
11
|
# Implement the required interface for Rails::Generators::Migration.
|
|
10
12
|
def self.next_migration_number(dirname) #:nodoc:
|
data/rails/init.rb
CHANGED
|
@@ -3,8 +3,8 @@ RAILS_DEFAULT_LOGGER.info "** thumbs_up: setting up load paths **"
|
|
|
3
3
|
%w{ models controllers helpers }.each do |dir|
|
|
4
4
|
path = File.join(File.dirname(__FILE__) , 'lib', dir)
|
|
5
5
|
$LOAD_PATH << path
|
|
6
|
-
ActiveSupport::Dependencies.
|
|
7
|
-
ActiveSupport::Dependencies.
|
|
6
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
|
7
|
+
ActiveSupport::Dependencies.autoload_once_paths.delete(path)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
require 'thumbs_up'
|
|
10
|
+
require 'thumbs_up'
|
metadata
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: thumbs_up
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
prerelease:
|
|
5
|
-
|
|
6
|
-
- 0
|
|
7
|
-
- 3
|
|
8
|
-
- 2
|
|
9
|
-
version: 0.3.2
|
|
4
|
+
prerelease:
|
|
5
|
+
version: "0.4"
|
|
10
6
|
platform: ruby
|
|
11
7
|
authors:
|
|
12
8
|
- Brady Bouchard
|
|
@@ -19,7 +15,7 @@ autorequire:
|
|
|
19
15
|
bindir: bin
|
|
20
16
|
cert_chain: []
|
|
21
17
|
|
|
22
|
-
date:
|
|
18
|
+
date: 2011-04-01 00:00:00 +10:00
|
|
23
19
|
default_executable:
|
|
24
20
|
dependencies: []
|
|
25
21
|
|
|
@@ -61,21 +57,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
61
57
|
requirements:
|
|
62
58
|
- - ">="
|
|
63
59
|
- !ruby/object:Gem::Version
|
|
64
|
-
segments:
|
|
65
|
-
- 0
|
|
66
60
|
version: "0"
|
|
67
61
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
62
|
none: false
|
|
69
63
|
requirements:
|
|
70
64
|
- - ">="
|
|
71
65
|
- !ruby/object:Gem::Version
|
|
72
|
-
segments:
|
|
73
|
-
- 0
|
|
74
66
|
version: "0"
|
|
75
67
|
requirements: []
|
|
76
68
|
|
|
77
69
|
rubyforge_project:
|
|
78
|
-
rubygems_version: 1.3
|
|
70
|
+
rubygems_version: 1.5.3
|
|
79
71
|
signing_key:
|
|
80
72
|
specification_version: 3
|
|
81
73
|
summary: Voting for ActiveRecord with multiple vote sources and karma calculation.
|