thumbs_up 0.2.1
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/CHANGELOG.markdown +7 -0
- data/MIT-LICENSE +66 -0
- data/README.markdown +126 -0
- data/Rakefile +19 -0
- data/VERSION +1 -0
- data/lib/acts_as_voteable.rb +85 -0
- data/lib/acts_as_voter.rb +84 -0
- data/lib/generators/thumbs_up/templates/migration.rb +21 -0
- data/lib/generators/thumbs_up/templates/vote.rb +16 -0
- data/lib/generators/thumbs_up/thumbs_up_generator.rb +27 -0
- data/lib/has_karma.rb +42 -0
- data/lib/thumbs_up.rb +7 -0
- data/rails/init.rb +10 -0
- data/thumbs_up.gemspec +50 -0
- metadata +85 -0
data/CHANGELOG.markdown
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
2010-08-03
|
|
2
|
+
==========
|
|
3
|
+
* Renamed to ThumbsUp from vote\_fu.
|
|
4
|
+
* Updated for Rails 3, using ActiveRecord/Arel.
|
|
5
|
+
* Cleaned up some dead code, some shitty code, and made a few methods take up quite a lot less memory and time (voters\_who\_voted).
|
|
6
|
+
* Removed some shitty example code - this gem is self-explanatory and straight-forward as-is.
|
|
7
|
+
* Fixed karma.
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Copyright (c) 2010 Brady Bouchard (ldawn.com)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
21
|
+
|
|
22
|
+
Major portions of this package were adapted from VoteFu, which is subject to the same license. Here is the original copyright notice for VoteFu:
|
|
23
|
+
|
|
24
|
+
Copyright (c) 2008 Peter Jackson (peteonrails.com)
|
|
25
|
+
|
|
26
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
27
|
+
a copy of this software and associated documentation files (the
|
|
28
|
+
"Software"), to deal in the Software without restriction, including
|
|
29
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
30
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
31
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
32
|
+
the following conditions:
|
|
33
|
+
|
|
34
|
+
The above copyright notice and this permission notice shall be
|
|
35
|
+
included in all copies or substantial portions of the Software.
|
|
36
|
+
|
|
37
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
38
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
39
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
40
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
41
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
42
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
43
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
44
|
+
|
|
45
|
+
Major portions of this package were adapted from ActsAsVoteable, which is subject to the same license. Here is the original copyright notice for ActsAsVoteable:
|
|
46
|
+
|
|
47
|
+
Copyright (c) 2006 Cosmin Radoi
|
|
48
|
+
|
|
49
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
50
|
+
a copy of this software and associated documentation files (the
|
|
51
|
+
"Software"), to deal in the Software without restriction, including
|
|
52
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
53
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
54
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
55
|
+
the following conditions:
|
|
56
|
+
|
|
57
|
+
The above copyright notice and this permission notice shall be
|
|
58
|
+
included in all copies or substantial portions of the Software.
|
|
59
|
+
|
|
60
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
61
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
62
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
63
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
64
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
65
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
66
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
ThumbsUp
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
A ridiculously straightforward and simple package 'o' code to enable voting in your application, a la stackoverflow.com, etc.
|
|
5
|
+
Allows an arbitrary number of entities (users, etc.) to vote on models.
|
|
6
|
+
|
|
7
|
+
### Mixins
|
|
8
|
+
This plugin introduces two mixins to your recipe book:
|
|
9
|
+
|
|
10
|
+
1. **acts\_as\_voteable** : Intended for content objects like Posts, Comments, etc.
|
|
11
|
+
2. **acts\_as\_voter** : Intended for voting entities, like Users.
|
|
12
|
+
3. **has\_karma** : Adds some helpers to acts\_as\_voter models for calculating karma.
|
|
13
|
+
|
|
14
|
+
### Inspiration
|
|
15
|
+
|
|
16
|
+
This plugin started as an adaptation / update of vote\_fu for use with Rails 3. It adds some speed, removes some cruft, and is adapted for use with ActiveRecord / Arel in Rails 3. It maintains the awesomeness of the original vote\_fu.
|
|
17
|
+
|
|
18
|
+
Installation
|
|
19
|
+
============
|
|
20
|
+
|
|
21
|
+
### Require the gem:
|
|
22
|
+
|
|
23
|
+
gem 'thumbs_up'
|
|
24
|
+
|
|
25
|
+
### Create and run the ThumbsUp migration:
|
|
26
|
+
|
|
27
|
+
rails generate thumbs_up
|
|
28
|
+
rake db:migrate
|
|
29
|
+
|
|
30
|
+
Usage
|
|
31
|
+
=====
|
|
32
|
+
|
|
33
|
+
## Getting Started
|
|
34
|
+
|
|
35
|
+
### Turn your AR models into something that can be voted upon.
|
|
36
|
+
|
|
37
|
+
class SomeModel < ActiveRecord::Base
|
|
38
|
+
acts_as_voteable
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class Question < ActiveRecord::Base
|
|
42
|
+
acts_as_voteable
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
### Turn your Users (or any other model) into voters.
|
|
46
|
+
|
|
47
|
+
class User < ActiveRecord::Base
|
|
48
|
+
acts_as_voter
|
|
49
|
+
# The following line is optional, and tracks karma (up votes) for questions this user has submitted.
|
|
50
|
+
# Each question has a submitter_id column that tracks the user who submitted it.
|
|
51
|
+
# You can track any voteable model.
|
|
52
|
+
has_karma(:questions, :as => :submitter)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class Robot < ActiveRecord::Base
|
|
56
|
+
acts_as_voter
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
### To cast a vote for a Model you can do the following:
|
|
60
|
+
|
|
61
|
+
#### Shorthand syntax
|
|
62
|
+
voter.vote_for(voteable) # Adds a +1 vote
|
|
63
|
+
voter.vote_against(voteable) # Adds a -1 vote
|
|
64
|
+
voter.vote(voteable, vote) # Adds either a +1 or -1 vote: vote => true (+1), vote => false (-1)
|
|
65
|
+
|
|
66
|
+
### Querying votes
|
|
67
|
+
|
|
68
|
+
Did the first user vote for the Car with id = 2 already?
|
|
69
|
+
|
|
70
|
+
u = User.first
|
|
71
|
+
u.voted_on?(Car.find(2))
|
|
72
|
+
|
|
73
|
+
#### Tallying Votes
|
|
74
|
+
|
|
75
|
+
You can easily retrieve voteable object collections based on the properties of their votes:
|
|
76
|
+
|
|
77
|
+
@items = Item.tally(
|
|
78
|
+
{ :at_least => 1,
|
|
79
|
+
:at_most => 10000,
|
|
80
|
+
:start_at => 2.weeks.ago,
|
|
81
|
+
:end_at => 1.day.ago,
|
|
82
|
+
:limit => 10,
|
|
83
|
+
:order => "items.name DESC"
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
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.
|
|
87
|
+
|
|
88
|
+
##### Tally Options:
|
|
89
|
+
:start_at - Restrict the votes to those created after a certain time
|
|
90
|
+
:end_at - Restrict the votes to those created before a certain time
|
|
91
|
+
:conditions - A piece of SQL conditions to add to the query
|
|
92
|
+
:limit - The maximum number of voteables to return
|
|
93
|
+
:order - A piece of SQL to order by. Eg 'votes.count desc' or 'voteable.created_at desc'
|
|
94
|
+
:at_least - Item must have at least X votes
|
|
95
|
+
:at_most - Item may not have more than X votes
|
|
96
|
+
|
|
97
|
+
#### Lower level queries
|
|
98
|
+
|
|
99
|
+
positiveVoteCount = voteable.votes_for
|
|
100
|
+
negativeVoteCount = voteable.votes_against
|
|
101
|
+
plusminus = voteable.plusminus # Votes for minus votes against.
|
|
102
|
+
|
|
103
|
+
voter.voted_for?(voteable) # True if the voter voted for this object.
|
|
104
|
+
voter.vote_count(:up | :down | :all) # returns the count of +1, -1, or all votes
|
|
105
|
+
|
|
106
|
+
voteable.voted_by?(voter) # True if the voter voted for this object.
|
|
107
|
+
@voters = voteable.voters_who_voted
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### One vote per user!
|
|
111
|
+
|
|
112
|
+
ThumbsUp by default only allows one vote per user. This can be changed by removing:
|
|
113
|
+
|
|
114
|
+
#### In vote.rb:
|
|
115
|
+
|
|
116
|
+
validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
|
|
117
|
+
|
|
118
|
+
#### In the migration:
|
|
119
|
+
|
|
120
|
+
add_index :votes, ["voter_id", "voter_type", "voteable_id", "voteable_type"], :unique => true, :name => "uniq_one_vote_only"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
Credits
|
|
124
|
+
=======
|
|
125
|
+
|
|
126
|
+
Basic scaffold is from Peter Jackson's work on VoteFu / ActsAsVoteable. All code updated for Rails 3, cleaned up for speed and clarity, karma calculation fixed, and (hopefully) zero introduced bugs.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
require 'rake'
|
|
4
|
+
|
|
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
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.1
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module ThumbsUp
|
|
2
|
+
module ActsAsVoteable #:nodoc:
|
|
3
|
+
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.extend ClassMethods
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
def acts_as_voteable
|
|
10
|
+
has_many :votes, :as => :voteable, :dependent => :nullify
|
|
11
|
+
|
|
12
|
+
include ThumbsUp::ActsAsVoteable::InstanceMethods
|
|
13
|
+
extend ThumbsUp::ActsAsVoteable::SingletonMethods
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module SingletonMethods
|
|
18
|
+
|
|
19
|
+
# Calculate the vote counts for all voteables of my type.
|
|
20
|
+
# This method returns all voteables with at least one vote.
|
|
21
|
+
# The vote count for each voteable is available as #vote_count.
|
|
22
|
+
#
|
|
23
|
+
# Options:
|
|
24
|
+
# :start_at - Restrict the votes to those created after a certain time
|
|
25
|
+
# :end_at - Restrict the votes to those created before a certain time
|
|
26
|
+
# :conditions - A piece of SQL conditions to add to the query
|
|
27
|
+
# :limit - The maximum number of voteables to return
|
|
28
|
+
# :order - A piece of SQL to order by. Eg 'vote_count DESC' or 'voteable.created_at DESC'
|
|
29
|
+
# :at_least - Item must have at least X votes
|
|
30
|
+
# :at_most - Item may not have more than X votes
|
|
31
|
+
def tally(*args)
|
|
32
|
+
options = args.extract_options!
|
|
33
|
+
t = self.where("#{Vote.table_name}.voteable_type = '#{self.name}'")
|
|
34
|
+
# We join so that you can order by columns on the voteable model.
|
|
35
|
+
t = t.joins("LEFT OUTER JOIN #{Vote.table_name} ON #{self.table_name}.#{self.primary_key} = #{Vote.table_name}.voteable_id")
|
|
36
|
+
t = t.having("vote_count > 0")
|
|
37
|
+
t = t.group("#{Vote.table_name}.voteable_id")
|
|
38
|
+
t = t.limit(options[:limit]) if options[:limit]
|
|
39
|
+
t = t.where("#{Vote.table_name}.created_at >= ?", options[:start_at]) if options[:start_at]
|
|
40
|
+
t = t.where("#{Vote.table_name}.created_at <= ?", options[:end_at]) if options[:end_at]
|
|
41
|
+
t = t.where(options[:conditions]) if options[:conditions]
|
|
42
|
+
t = options[:order] ? t.order(options[:order]) : t.order("vote_count DESC")
|
|
43
|
+
t = t.having(["vote_count >= ?", options[:at_least]]) if options[:at_least]
|
|
44
|
+
t = t.having(["vote_count <= ?", options[:at_most]]) if options[:at_most]
|
|
45
|
+
t.select("#{self.table_name}.*, COUNT(#{Vote.table_name}.voteable_id) AS vote_count")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module InstanceMethods
|
|
51
|
+
|
|
52
|
+
def votes_for
|
|
53
|
+
Vote.where(:voteable_id => id, :voteable_type => self.class.name, :vote => true).count
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def votes_against
|
|
57
|
+
Vote.where(:voteable_id => id, :voteable_type => self.class.name, :vote => false).count
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# You'll probably want to use this method to display how 'good' a particular voteable
|
|
61
|
+
# is, and/or sort based on it.
|
|
62
|
+
def plusminus
|
|
63
|
+
votes_for - votes_against
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def votes_count
|
|
67
|
+
self.votes.size
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def voters_who_voted
|
|
71
|
+
self.votes.map(&:voter).uniq
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def voted_by?(voter)
|
|
75
|
+
0 < Vote.where(
|
|
76
|
+
:voteable_id => self.id,
|
|
77
|
+
:voteable_type => self.class.name,
|
|
78
|
+
:voter_type => voter.class.name,
|
|
79
|
+
:voter_id => voter.id
|
|
80
|
+
).count
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module ThumbsUp #:nodoc:
|
|
2
|
+
module ActsAsVoter #:nodoc:
|
|
3
|
+
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.extend ClassMethods
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
def acts_as_voter
|
|
10
|
+
|
|
11
|
+
# If a voting entity is deleted, keep the votes.
|
|
12
|
+
# has_many :votes, :as => :voter, :dependent => :nullify
|
|
13
|
+
# Destroy votes when a user is deleted.
|
|
14
|
+
has_many :votes, :as => :voter, :dependent => :destroy
|
|
15
|
+
|
|
16
|
+
include ThumbsUp::ActsAsVoter::InstanceMethods
|
|
17
|
+
extend ThumbsUp::ActsAsVoter::SingletonMethods
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# This module contains class methods
|
|
22
|
+
module SingletonMethods
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# This module contains instance methods
|
|
26
|
+
module InstanceMethods
|
|
27
|
+
|
|
28
|
+
# Usage user.vote_count(:up) # All +1 votes
|
|
29
|
+
# user.vote_count(:down) # All -1 votes
|
|
30
|
+
# user.vote_count() # All votes
|
|
31
|
+
|
|
32
|
+
def vote_count(for_or_against = :all)
|
|
33
|
+
v = Vote.where(:voter_id => id).where(:voter_type => self.class.name)
|
|
34
|
+
v = case for_or_against
|
|
35
|
+
when :all then v
|
|
36
|
+
when :up then v.where(:vote => true)
|
|
37
|
+
when :down then v.where(:vote => false)
|
|
38
|
+
end
|
|
39
|
+
v.count
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def voted_for?(voteable)
|
|
43
|
+
voted_which_way?(voteable, :up)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def voted_against?(voteable)
|
|
47
|
+
voted_which_way?(voteable, :down)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def voted_on?(voteable)
|
|
51
|
+
0 < Vote.where(
|
|
52
|
+
:voter_id => self.id,
|
|
53
|
+
:voter_type => self.class.name,
|
|
54
|
+
:voteable_id => voteable.id,
|
|
55
|
+
:voteable_type => voteable.class.name
|
|
56
|
+
).count
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def vote_for(voteable)
|
|
60
|
+
self.vote(voteable, true)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def vote_against(voteable)
|
|
64
|
+
self.vote(voteable, false)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def vote(voteable, vote)
|
|
68
|
+
Vote.create!(:vote => vote, :voteable => voteable, :voter => self)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def voted_which_way?(voteable, direction)
|
|
72
|
+
raise ArgumentError, "expected :up or :down" unless [:up, :down].include?(direction)
|
|
73
|
+
0 < Vote.where(
|
|
74
|
+
:voter_id => self.id,
|
|
75
|
+
:voter_type => self.class.name,
|
|
76
|
+
:vote => direction == :up ? true : false,
|
|
77
|
+
:voteable_id => voteable.id,
|
|
78
|
+
:voteable_type => voteable.class.name
|
|
79
|
+
).count
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class ThumbsUpMigration < ActiveRecord::Migration
|
|
2
|
+
def self.up
|
|
3
|
+
create_table :votes, :force => true do |t|
|
|
4
|
+
t.boolean :vote, :default => false
|
|
5
|
+
t.references :voteable, :polymorphic => true, :null => false
|
|
6
|
+
t.references :voter, :polymorphic => true
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :votes, ["voter_id", "voter_type"], :name => "fk_voters"
|
|
11
|
+
add_index :votes, ["voteable_id", "voteable_type"], :name => "fk_voteables"
|
|
12
|
+
|
|
13
|
+
# If you don't want to enforce "One Person, One Vote" rules in the database, comment out the index below.
|
|
14
|
+
add_index :votes, ["voter_id", "voter_type", "voteable_id", "voteable_type"], :unique => true, :name => "uniq_one_vote_only"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.down
|
|
18
|
+
drop_table :votes
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class Vote < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
scope :for_voter, lambda { |*args| where(["voter_id = ? AND voter_type = ?", args.first.id, args.first.class.name]) }
|
|
4
|
+
scope :for_voteable, lambda { |*args| where(["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.class.name]) }
|
|
5
|
+
scope :recent, lambda { |*args| where(["created_at > ?", (args.first || 2.weeks.ago)]) }
|
|
6
|
+
scope :descending, order("created_at DESC")
|
|
7
|
+
|
|
8
|
+
belongs_to :voteable, :polymorphic => true
|
|
9
|
+
belongs_to :voter, :polymorphic => true
|
|
10
|
+
|
|
11
|
+
attr_accessible :vote, :voter, :voteable
|
|
12
|
+
|
|
13
|
+
# Comment out the line below to allow multiple votes per user.
|
|
14
|
+
validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'rails/generators/active_record'
|
|
2
|
+
|
|
3
|
+
class ThumbsUpGenerator < Rails::Generators::Base
|
|
4
|
+
|
|
5
|
+
include Rails::Generators::Migration
|
|
6
|
+
|
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
8
|
+
|
|
9
|
+
# Implement the required interface for Rails::Generators::Migration.
|
|
10
|
+
def self.next_migration_number(dirname) #:nodoc:
|
|
11
|
+
next_migration_number = current_migration_number(dirname) + 1
|
|
12
|
+
if ActiveRecord::Base.timestamped_migrations
|
|
13
|
+
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
|
|
14
|
+
else
|
|
15
|
+
"%.3d" % next_migration_number
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_migration
|
|
20
|
+
migration_template 'migration.rb', File.join('db', 'migrate', 'thumbs_up_migration.rb')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def move_vote_model
|
|
24
|
+
template 'vote.rb', File.join('app', 'models', 'vote.rb')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
data/lib/has_karma.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module ThumbsUp #:nodoc:
|
|
2
|
+
module Karma #:nodoc:
|
|
3
|
+
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.extend ClassMethods
|
|
6
|
+
class << base
|
|
7
|
+
attr_accessor :karmic_objects
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def has_karma(voteable_type, options = {})
|
|
13
|
+
include ThumbsUp::Karma::InstanceMethods
|
|
14
|
+
extend ThumbsUp::Karma::SingletonMethods
|
|
15
|
+
self.karmic_objects ||= {}
|
|
16
|
+
self.karmic_objects[voteable_type.to_s.classify.constantize] = (options[:as] ? options[:as].to_s.foreign_key : self.class.name.foreign_key)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module SingletonMethods
|
|
21
|
+
|
|
22
|
+
## Not yet implemented. Don't use it!
|
|
23
|
+
# Find the most popular users
|
|
24
|
+
def find_most_karmic
|
|
25
|
+
find(:all)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module InstanceMethods
|
|
31
|
+
def karma(options = {})
|
|
32
|
+
self.class.karmic_objects.collect do |object, fk|
|
|
33
|
+
v = object.where(["#{Vote.table_name}.vote = ?", true]).where(["#{self.class.table_name}.#{self.class.primary_key} = ?", self.id])
|
|
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
|
+
v = v.joins("INNER JOIN #{self.class.table_name} ON #{self.class.table_name}.#{self.class.primary_key} = #{object.table_name}.#{fk}")
|
|
36
|
+
v.count
|
|
37
|
+
end.sum
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/thumbs_up.rb
ADDED
data/rails/init.rb
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
RAILS_DEFAULT_LOGGER.info "** thumbs_up: setting up load paths **"
|
|
2
|
+
|
|
3
|
+
%w{ models controllers helpers }.each do |dir|
|
|
4
|
+
path = File.join(File.dirname(__FILE__) , 'lib', dir)
|
|
5
|
+
$LOAD_PATH << path
|
|
6
|
+
ActiveSupport::Dependencies.load_paths << path
|
|
7
|
+
ActiveSupport::Dependencies.load_once_paths.delete(path)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require 'thumbs_up'
|
data/thumbs_up.gemspec
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{thumbs_up}
|
|
8
|
+
s.version = "0.2.1"
|
|
9
|
+
|
|
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 Wn\304\231trzak"]
|
|
12
|
+
s.date = %q{2010-08-04}
|
|
13
|
+
s.description = %q{ThumbsUp provides dead-simple voting capabilities to ActiveRecord models with karma calculation, a la stackoverflow.com.}
|
|
14
|
+
s.email = %q{brady@ldawn.com}
|
|
15
|
+
s.extra_rdoc_files = [
|
|
16
|
+
"README.markdown"
|
|
17
|
+
]
|
|
18
|
+
s.files = [
|
|
19
|
+
"CHANGELOG.markdown",
|
|
20
|
+
"MIT-LICENSE",
|
|
21
|
+
"README.markdown",
|
|
22
|
+
"Rakefile",
|
|
23
|
+
"VERSION",
|
|
24
|
+
"lib/acts_as_voteable.rb",
|
|
25
|
+
"lib/acts_as_voter.rb",
|
|
26
|
+
"lib/generators/thumbs_up/templates/migration.rb",
|
|
27
|
+
"lib/generators/thumbs_up/templates/vote.rb",
|
|
28
|
+
"lib/generators/thumbs_up/thumbs_up_generator.rb",
|
|
29
|
+
"lib/has_karma.rb",
|
|
30
|
+
"lib/thumbs_up.rb",
|
|
31
|
+
"rails/init.rb",
|
|
32
|
+
"thumbs_up.gemspec"
|
|
33
|
+
]
|
|
34
|
+
s.homepage = %q{http://github.com/brady8/thumbs_up}
|
|
35
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
|
36
|
+
s.require_paths = ["lib"]
|
|
37
|
+
s.rubygems_version = %q{1.3.7}
|
|
38
|
+
s.summary = %q{Voting for ActiveRecord with multiple vote sources and karma calculation.}
|
|
39
|
+
|
|
40
|
+
if s.respond_to? :specification_version then
|
|
41
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
42
|
+
s.specification_version = 3
|
|
43
|
+
|
|
44
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
45
|
+
else
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
metadata
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: thumbs_up
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 21
|
|
5
|
+
prerelease: false
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 2
|
|
9
|
+
- 1
|
|
10
|
+
version: 0.2.1
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- Brady Bouchard
|
|
14
|
+
- Peter Jackson
|
|
15
|
+
- Cosmin Radoi
|
|
16
|
+
- Bence Nagy
|
|
17
|
+
- Rob Maddox
|
|
18
|
+
- "Wojciech Wn\xC4\x99trzak"
|
|
19
|
+
autorequire:
|
|
20
|
+
bindir: bin
|
|
21
|
+
cert_chain: []
|
|
22
|
+
|
|
23
|
+
date: 2010-08-04 00:00:00 +10:00
|
|
24
|
+
default_executable:
|
|
25
|
+
dependencies: []
|
|
26
|
+
|
|
27
|
+
description: ThumbsUp provides dead-simple voting capabilities to ActiveRecord models with karma calculation, a la stackoverflow.com.
|
|
28
|
+
email: brady@ldawn.com
|
|
29
|
+
executables: []
|
|
30
|
+
|
|
31
|
+
extensions: []
|
|
32
|
+
|
|
33
|
+
extra_rdoc_files:
|
|
34
|
+
- README.markdown
|
|
35
|
+
files:
|
|
36
|
+
- CHANGELOG.markdown
|
|
37
|
+
- MIT-LICENSE
|
|
38
|
+
- README.markdown
|
|
39
|
+
- Rakefile
|
|
40
|
+
- VERSION
|
|
41
|
+
- lib/acts_as_voteable.rb
|
|
42
|
+
- lib/acts_as_voter.rb
|
|
43
|
+
- lib/generators/thumbs_up/templates/migration.rb
|
|
44
|
+
- lib/generators/thumbs_up/templates/vote.rb
|
|
45
|
+
- lib/generators/thumbs_up/thumbs_up_generator.rb
|
|
46
|
+
- lib/has_karma.rb
|
|
47
|
+
- lib/thumbs_up.rb
|
|
48
|
+
- rails/init.rb
|
|
49
|
+
- thumbs_up.gemspec
|
|
50
|
+
has_rdoc: true
|
|
51
|
+
homepage: http://github.com/brady8/thumbs_up
|
|
52
|
+
licenses: []
|
|
53
|
+
|
|
54
|
+
post_install_message:
|
|
55
|
+
rdoc_options:
|
|
56
|
+
- --charset=UTF-8
|
|
57
|
+
require_paths:
|
|
58
|
+
- lib
|
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
|
+
none: false
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
hash: 3
|
|
65
|
+
segments:
|
|
66
|
+
- 0
|
|
67
|
+
version: "0"
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
none: false
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
hash: 3
|
|
74
|
+
segments:
|
|
75
|
+
- 0
|
|
76
|
+
version: "0"
|
|
77
|
+
requirements: []
|
|
78
|
+
|
|
79
|
+
rubyforge_project:
|
|
80
|
+
rubygems_version: 1.3.7
|
|
81
|
+
signing_key:
|
|
82
|
+
specification_version: 3
|
|
83
|
+
summary: Voting for ActiveRecord with multiple vote sources and karma calculation.
|
|
84
|
+
test_files: []
|
|
85
|
+
|