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 CHANGED
@@ -1,2 +1,3 @@
1
1
  *.gem
2
2
  pkg/*
3
+ Gemfile.lock
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.vote_exclusively_for(voteable) # Removes any previous votes by that particular voter, and votes against.
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 'rake'
3
+ require 'bundler'
4
4
 
5
5
  begin
6
- require 'jeweler'
7
- Jeweler::Tasks.new do |gem|
8
- gem.name = "thumbs_up"
9
- gem.summary = "Voting for ActiveRecord with multiple vote sources and karma calculation."
10
- gem.description = "ThumbsUp provides dead-simple voting capabilities to ActiveRecord models with karma calculation, a la stackoverflow.com."
11
- gem.email = "brady@ldawn.com"
12
- gem.homepage = "http://github.com/brady8/thumbs_up"
13
- gem.authors = ["Brady Bouchard", "Peter Jackson", "Cosmin Radoi", "Bence Nagy", "Rob Maddox", "Wojciech Wnętrzak"]
14
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
- end
16
- Jeweler::GemcutterTasks.new
17
- rescue LoadError
18
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
- end
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.3.2
1
+ 0.4
@@ -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
- # Comment out the line below to allow multiple votes per voter on a single entity.
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.load_paths << path
7
- ActiveSupport::Dependencies.load_once_paths.delete(path)
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: false
5
- segments:
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: 2010-12-16 00:00:00 -07:00
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.7
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.