aflatter-vote_fu 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.markdown +31 -0
- data/MIT-LICENSE +43 -0
- data/README.markdown +220 -0
- data/examples/routes.rb +7 -0
- data/examples/users_controller.rb +76 -0
- data/examples/voteable.html.erb +8 -0
- data/examples/voteable.rb +10 -0
- data/examples/voteables_controller.rb +117 -0
- data/examples/votes/_voteable_vote.html.erb +23 -0
- data/examples/votes/create.rjs +1 -0
- data/examples/votes_controller.rb +110 -0
- data/generators/vote_fu/templates/migration.rb +21 -0
- data/generators/vote_fu/vote_fu_generator.rb +8 -0
- data/init.rb +1 -0
- data/lib/acts_as_voteable.rb +114 -0
- data/lib/acts_as_voter.rb +75 -0
- data/lib/controllers/votes_controller.rb +96 -0
- data/lib/has_karma.rb +68 -0
- data/lib/models/vote.rb +17 -0
- data/lib/vote_fu.rb +9 -0
- data/rails/init.rb +10 -0
- data/test/vote_fu_test.rb +8 -0
- metadata +79 -0
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
2009-02-11
|
2
|
+
==========
|
3
|
+
* Merge in xlash's bugfix for PostgreSQL and his has\_karma patch for multi-model support.
|
4
|
+
|
5
|
+
2008-12-02
|
6
|
+
==========
|
7
|
+
* Merge in maddox's README typo fix and his ActiveSupport.Dependency patch
|
8
|
+
* Merge in nagybence's updates that make the code usable as a Gem in addition to being a Rails plugin.
|
9
|
+
* Thanks for the bugfixes and proofreading, nagybence and maddox!
|
10
|
+
* Updated the gemplugin support to be compatible with maddox and nagybence's changes.
|
11
|
+
* Added details on the MyQuotable reference application.
|
12
|
+
|
13
|
+
2008-07-20
|
14
|
+
==========
|
15
|
+
* Protect against mass assignment misvotes using attr\_accessible
|
16
|
+
* Update acts\_as mixins to use self.class.name instead of the deprecated self.type.name
|
17
|
+
|
18
|
+
2008-07-15
|
19
|
+
==========
|
20
|
+
* Added examples directory
|
21
|
+
* Changed this file to markdown format for GitHub goodness
|
22
|
+
* Added a commented out unique index in the migration generator for "one person, one vote"
|
23
|
+
* Removed votes\_controller.rb from lib/ and moved to examples
|
24
|
+
|
25
|
+
2008-07-10
|
26
|
+
==========
|
27
|
+
|
28
|
+
* Added a generator class for the migration.
|
29
|
+
* Implemented rails/init.rb
|
30
|
+
* Implemented capability to use any model as the initiator of votes.
|
31
|
+
* Implemented acts\_as\_voter methods.
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
Copyright (c) 2008 Peter Jackson (peteonrails.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 ActsAsVoteable, which is subject to the same license. Here is the original copyright notice for ActsAsVoteable:
|
23
|
+
|
24
|
+
Copyright (c) 2006 Cosmin Radoi
|
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.
|
data/README.markdown
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
vote_fu
|
2
|
+
=======
|
3
|
+
|
4
|
+
Allows an arbitrary number of entites (including Users) to vote on models.
|
5
|
+
|
6
|
+
### Mixins
|
7
|
+
This plugin introduces three mixins to your recipe book:
|
8
|
+
|
9
|
+
1. **acts\_as\_voteable** : Intended for content objects like Posts, Comments, etc.
|
10
|
+
2. **acts\_as\_voter** : Intended for voting entities, like Users.
|
11
|
+
3. **has\_karma** : Intended for voting entities, or other objects that own the things you're voting on.
|
12
|
+
|
13
|
+
### Inspiration
|
14
|
+
|
15
|
+
This plugin started as an adaptation / update of act\_as\_voteable. It has grown different from that plugin in several ways:
|
16
|
+
|
17
|
+
1. You can specify the model name that initiates votes.
|
18
|
+
2. You can, with a little tuning, have more than one entity type vote on more than one model type.
|
19
|
+
3. Adds "acts\_as\_voter" behavior to the initiator of votes.
|
20
|
+
4. Introduces some newer Rails features like named\_scope and :polymorphic keywords
|
21
|
+
5. Adds "has\_karma" mixin for identifying key content contributors
|
22
|
+
|
23
|
+
Installation
|
24
|
+
============
|
25
|
+
Use either the plugin or the gem installation method depending on your preference. If you're not sure, the plugin method is simpler. Whichever you choose, create the migration afterward and run it to create the required model.
|
26
|
+
|
27
|
+
### Via plugin
|
28
|
+
./script/plugin install git://github.com/peteonrails/vote_fu.git
|
29
|
+
|
30
|
+
### Via gem
|
31
|
+
Add the following to your application's environment.rb:
|
32
|
+
config.gem "peteonrails-vote_fu", :lib => 'vote_fu', :source => 'http://gems.github.com'
|
33
|
+
|
34
|
+
Install the gem:
|
35
|
+
rake gems:install
|
36
|
+
|
37
|
+
### Create vote_fu migration
|
38
|
+
Create a new rails migration using your new vote_fu generator (Note: "VoteableModel" is the name of the model on which you would like votes to be cast, e.g. Comment):
|
39
|
+
./script/generate vote_fu VoteableModel
|
40
|
+
|
41
|
+
Run the migration:
|
42
|
+
rake db:migrate
|
43
|
+
|
44
|
+
Usage
|
45
|
+
=====
|
46
|
+
|
47
|
+
## Getting Started
|
48
|
+
|
49
|
+
### Make your ActiveRecord model act as voteable.
|
50
|
+
|
51
|
+
|
52
|
+
class Model < ActiveRecord::Base
|
53
|
+
acts_as_voteable
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
### Make your ActiveRecord model(s) that vote act as voter.
|
58
|
+
|
59
|
+
class User < ActiveRecord::Base
|
60
|
+
acts_as_voter
|
61
|
+
end
|
62
|
+
|
63
|
+
class Robot < ActiveRecord::Base
|
64
|
+
acts_as_voter
|
65
|
+
end
|
66
|
+
|
67
|
+
### To cast a vote for a Model you can do the following:
|
68
|
+
|
69
|
+
#### Shorthand syntax
|
70
|
+
voter.vote_for(voteable) # Adds a +1 vote
|
71
|
+
voter.vote_against(voteable) # Adds a -1 vote
|
72
|
+
voter.vote(voteable, t_or_f) # Adds either +1 or -1 vote true => +1, false => -1
|
73
|
+
|
74
|
+
#### ActsAsVoteable syntax
|
75
|
+
The old acts\_as\_voteable syntax is still supported:
|
76
|
+
|
77
|
+
vote = Vote.new(:vote => true)
|
78
|
+
m = Model.find(params[:id])
|
79
|
+
m.votes << vote
|
80
|
+
user.votes << vote
|
81
|
+
|
82
|
+
### Querying votes
|
83
|
+
|
84
|
+
#### Tallying Votes
|
85
|
+
|
86
|
+
You can easily retrieve voteable object collections based on the properties of their votes:
|
87
|
+
|
88
|
+
@items = Item.tally(
|
89
|
+
{ :at_least => 1,
|
90
|
+
:at_most => 10000,
|
91
|
+
:start_at => 2.weeks.ago,
|
92
|
+
:end_at => 1.day.ago,
|
93
|
+
:limit => 10,
|
94
|
+
:order => "items.name desc"
|
95
|
+
})
|
96
|
+
|
97
|
+
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.
|
98
|
+
|
99
|
+
##### Tally Options:
|
100
|
+
:start_at - Restrict the votes to those created after a certain time
|
101
|
+
:end_at - Restrict the votes to those created before a certain time
|
102
|
+
:conditions - A piece of SQL conditions to add to the query
|
103
|
+
:limit - The maximum number of voteables to return
|
104
|
+
:order - A piece of SQL to order by. Eg 'votes.count desc' or 'voteable.created_at desc'
|
105
|
+
:at_least - Item must have at least X votes
|
106
|
+
:at_most - Item may not have more than X votes
|
107
|
+
|
108
|
+
#### Lower level queries
|
109
|
+
ActiveRecord models that act as voteable can be queried for the positive votes, negative votes, and a total vote count by using the votes\_for, votes\_against, and votes\_count methods respectively. Here is an example:
|
110
|
+
|
111
|
+
positiveVoteCount = m.votes_for
|
112
|
+
negativeVoteCount = m.votes_against
|
113
|
+
totalVoteCount = m.votes_count
|
114
|
+
|
115
|
+
And because the Vote Fu plugin will add the has_many votes relationship to your model you can always get all the votes by using the votes property:
|
116
|
+
|
117
|
+
allVotes = m.votes
|
118
|
+
|
119
|
+
The mixin also provides these methods:
|
120
|
+
|
121
|
+
voter.voted_for?(voteable) # True if the voter voted for this object.
|
122
|
+
voter.vote_count([true|false|"all"]) # returns the count of +1, -1, or all votes
|
123
|
+
|
124
|
+
voteable.voted_by?(voter) # True if the voter voted for this object.
|
125
|
+
@voters = voteable.voters_who_voted
|
126
|
+
|
127
|
+
|
128
|
+
#### Named Scopes
|
129
|
+
|
130
|
+
The Vote model has several named scopes you can use to find vote details:
|
131
|
+
|
132
|
+
@pete_votes = Vote.for_voter(pete)
|
133
|
+
@post_votes = Vote.for_voteable(post)
|
134
|
+
@recent_votes = Vote.recent(1.day.ago)
|
135
|
+
@descending_votes = Vote.descending
|
136
|
+
|
137
|
+
You can chain these together to make interesting queries:
|
138
|
+
|
139
|
+
# Show all of Pete's recent votes for a certain Post, in descending order (newest first)
|
140
|
+
@pete_recent_votes_on_post = Vote.for_voter(pete).for_voteable(post).recent(7.days.ago).descending
|
141
|
+
|
142
|
+
### Experimental: Voteable Object Owner Karma
|
143
|
+
I have just introduced the "has\_karma" mixin to this package. It aims to assign a karma score to the owners of voteable objects. This is designed to allow you to see which users are submitting the most highly voted content. Currently, karma is only "positive". That is, +1 votes add to karma, but -1 votes do not detract from it.
|
144
|
+
|
145
|
+
class User
|
146
|
+
has_many :posts
|
147
|
+
has_karma :posts
|
148
|
+
end
|
149
|
+
|
150
|
+
class Post
|
151
|
+
acts_as_voteable
|
152
|
+
end
|
153
|
+
|
154
|
+
# in your view, you can then do this:
|
155
|
+
Karma: <%= @user.karma %>
|
156
|
+
|
157
|
+
This feature is in alpha, but useful enough that I'm releasing it.
|
158
|
+
|
159
|
+
### One vote per user!
|
160
|
+
If you want to limit your users to a single vote on each item, take a look in lib/vote.rb.
|
161
|
+
|
162
|
+
# Uncomment this to limit users to a single vote on each item.
|
163
|
+
# validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
|
164
|
+
|
165
|
+
And if you want that enforced at the database level, look in the generated migration for your voteable:
|
166
|
+
|
167
|
+
# If you want to enfore "One Person, One Vote" rules in the database, uncomment the index below
|
168
|
+
# add_index :votes, ["voter_id", "voter_type", "voteable_id", "voteable_type"], :unique => true, :name => "uniq_one_vote_only"
|
169
|
+
|
170
|
+
### Example Application
|
171
|
+
|
172
|
+
There is now a reference application available. Due to overwhelming demand for example
|
173
|
+
code and kickstart guides, I have open-sourced MyQuotable.com in order to provide an
|
174
|
+
easy-to-follow example of how to use VoteFu with RESTful Authentication, JRails, and
|
175
|
+
other popular plugins. To get the example code:
|
176
|
+
|
177
|
+
git clone git://github.com/peteonrails/myquotable.git
|
178
|
+
|
179
|
+
There will be a screencast coming soon too. Contact me if you want to help.
|
180
|
+
|
181
|
+
Consideration
|
182
|
+
=============
|
183
|
+
If you like this software and use it, please consider recommending me on Working With Rails.
|
184
|
+
|
185
|
+
I don't want donations: a simple up-vote would make my day. My profile is: [http://www.workingwithrails.com/person/12521-peter-jackson][4]
|
186
|
+
|
187
|
+
To go directly to the "Recommend Me" screen: [http://www.workingwithrails.com/recommendation/new/person/12521-peter-jackson][5]
|
188
|
+
|
189
|
+
|
190
|
+
Credits
|
191
|
+
=======
|
192
|
+
|
193
|
+
#### Contributors
|
194
|
+
|
195
|
+
* Bence Nagy, Budapest, Hungary
|
196
|
+
* Jon Maddox, Richmond, Virginia, USA
|
197
|
+
|
198
|
+
#### Other works
|
199
|
+
|
200
|
+
[Juixe - The original ActsAsVoteable plugin inspired this code.][1]
|
201
|
+
|
202
|
+
[Xelipe - This plugin is heavily influenced by Acts As Commentable.][2]
|
203
|
+
|
204
|
+
[1]: http://www.juixe.com/techknow/index.php/2006/06/24/acts-as-voteable-rails-plugin/
|
205
|
+
[2]: http://github.com/jackdempsey/acts_as_commentable/tree/master
|
206
|
+
|
207
|
+
More
|
208
|
+
====
|
209
|
+
|
210
|
+
Support: [Use my blog for support.][6]
|
211
|
+
|
212
|
+
|
213
|
+
[Documentation from the original acts\_as\_voteable plugin][3]
|
214
|
+
|
215
|
+
[3]: http://www.juixe.com/techknow/index.php/2006/06/24/acts-as-voteable-rails-plugin/
|
216
|
+
[4]: http://www.workingwithrails.com/person/12521-peter-jackson
|
217
|
+
[5]: http://www.workingwithrails.com/recommendation/new/person/12521-peter-jackson
|
218
|
+
[6]: http://blog.peteonrails.com
|
219
|
+
|
220
|
+
Copyright (c) 2008 Peter Jackson, released under the MIT license
|
data/examples/routes.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# I usually use the user class from restful_authentication as my principle voter class
|
2
|
+
# There are generally no changes required to support voting in this controller.
|
3
|
+
|
4
|
+
class UsersController < ApplicationController
|
5
|
+
# Be sure to include AuthenticationSystem in Application Controller instead
|
6
|
+
include AuthenticatedSystem
|
7
|
+
|
8
|
+
# Protect these actions behind an admin login
|
9
|
+
before_filter :admin_required, :only => [:suspend, :unsuspend, :destroy, :purge]
|
10
|
+
before_filter :find_user, :only => [:suspend, :unsuspend, :destroy, :purge, :show]
|
11
|
+
|
12
|
+
before_filter :login_required, :only => [:index]
|
13
|
+
|
14
|
+
# render new.html.erb
|
15
|
+
def new
|
16
|
+
end
|
17
|
+
|
18
|
+
# GET /users/:id
|
19
|
+
def show
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def create
|
24
|
+
cookies.delete :auth_token
|
25
|
+
@user = User.new(params[:user])
|
26
|
+
@user.register! if @user.valid?
|
27
|
+
if @user.errors.empty?
|
28
|
+
self.current_user.forget_me if logged_in?
|
29
|
+
cookies.delete :auth_token
|
30
|
+
reset_session
|
31
|
+
flash[:notice] = "Thanks for signing up!"
|
32
|
+
else
|
33
|
+
render :action => 'new'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def activate
|
38
|
+
unless params[:activation_code].blank?
|
39
|
+
self.current_user = User.find_by_activation_code(params[:activation_code])
|
40
|
+
if logged_in? && !current_user.active?
|
41
|
+
current_user.activate!
|
42
|
+
flash[:notice] = "Signup complete!"
|
43
|
+
redirect_back_or_default('/')
|
44
|
+
else
|
45
|
+
flash[:error] = "Sorry, we couldn't find that activation code. Please cut and paste your activation code into the space at left."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
# render activate.html.erb
|
49
|
+
end
|
50
|
+
|
51
|
+
def suspend
|
52
|
+
@user.suspend!
|
53
|
+
redirect_to users_path
|
54
|
+
end
|
55
|
+
|
56
|
+
def unsuspend
|
57
|
+
@user.unsuspend!
|
58
|
+
redirect_to users_path
|
59
|
+
end
|
60
|
+
|
61
|
+
def destroy
|
62
|
+
@user.delete!
|
63
|
+
redirect_to users_path
|
64
|
+
end
|
65
|
+
|
66
|
+
def purge
|
67
|
+
@user.destroy
|
68
|
+
redirect_to users_path
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
def find_user
|
73
|
+
@user = User.find(params[:id])
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# This example controller assumes you are using the User class from restful_authentication
|
2
|
+
# and a nested voteable resource. See routes.rb
|
3
|
+
|
4
|
+
|
5
|
+
class VoteablesController < ApplicationController
|
6
|
+
|
7
|
+
before_filter :find_user
|
8
|
+
before_filter :login_required, :only => [:new, :edit, :destroy, :create, :update]
|
9
|
+
before_filter :must_own_voteable, :only => [:edit, :destroy, :update]
|
10
|
+
|
11
|
+
# GET /users/:id/voteables
|
12
|
+
# GET /users/:id/voteables.xml
|
13
|
+
def index
|
14
|
+
@voteable = Voteable.descending
|
15
|
+
|
16
|
+
respond_to do |format|
|
17
|
+
format.html # index.html.erb
|
18
|
+
format.xml { render :xml => @voteables }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# GET /users/:id/voteables/1
|
23
|
+
# GET /users/:id/voteables/1.xml
|
24
|
+
def show
|
25
|
+
@voteable = Voteable.find(params[:id])
|
26
|
+
|
27
|
+
respond_to do |format|
|
28
|
+
format.html # show.html.erb
|
29
|
+
format.xml { render :xml => @voteable }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# GET /users/:id/voteables/new
|
34
|
+
# GET /users/:id/voteables/new.xml
|
35
|
+
def new
|
36
|
+
@voteable = Voteable.new
|
37
|
+
|
38
|
+
respond_to do |format|
|
39
|
+
format.html # new.html.erb
|
40
|
+
format.xml { render :xml => @voteable }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# GET /users/:id/voteables/1/edit
|
45
|
+
def edit
|
46
|
+
@voteable ||= Voteable.find(params[:id])
|
47
|
+
end
|
48
|
+
|
49
|
+
# POST /users/:id/voteables
|
50
|
+
# POST /users/:id/voteables.xml
|
51
|
+
def create
|
52
|
+
@voteable = Voteable.new(params[:voteable])
|
53
|
+
@voteable.user = current_user
|
54
|
+
|
55
|
+
respond_to do |format|
|
56
|
+
if @voteable.save
|
57
|
+
flash[:notice] = 'Voteable was successfully saved.'
|
58
|
+
format.html { redirect_to([@user, @voteable]) }
|
59
|
+
format.xml { render :xml => @voteable, :status => :created, :location => @voteable }
|
60
|
+
else
|
61
|
+
format.html { render :action => "new" }
|
62
|
+
format.xml { render :xml => @voteable.errors, :status => :unprocessable_entity }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# PUT /users/:id/voteable/1
|
68
|
+
# PUT /users/:id/voteable/1.xml
|
69
|
+
def update
|
70
|
+
@voteable = Voteable.find(params[:id])
|
71
|
+
|
72
|
+
respond_to do |format|
|
73
|
+
if @quote.update_attributes(params[:voteable])
|
74
|
+
flash[:notice] = 'Voteable was successfully updated.'
|
75
|
+
format.html { redirect_to([@user, @voteable]) }
|
76
|
+
format.xml { head :ok }
|
77
|
+
else
|
78
|
+
format.html { render :action => "edit" }
|
79
|
+
format.xml { render :xml => @voteable.errors, :status => :unprocessable_entity }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# DELETE /users/:id/voteable/1
|
85
|
+
# DELETE /users/:id/voteable/1.xml
|
86
|
+
def destroy
|
87
|
+
@voteable = Voteable.find(params[:id])
|
88
|
+
@voteable.destroy
|
89
|
+
|
90
|
+
respond_to do |format|
|
91
|
+
format.html { redirect_to(user_voteables_url) }
|
92
|
+
format.xml { head :ok }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def find_user
|
98
|
+
@user = User.find(params[:user_id])
|
99
|
+
end
|
100
|
+
|
101
|
+
def must_own_voteable
|
102
|
+
@voteable ||= Voteable.find(params[:id])
|
103
|
+
@voteable.user == current_user || ownership_violation
|
104
|
+
end
|
105
|
+
|
106
|
+
def ownership_violation
|
107
|
+
respond_to do |format|
|
108
|
+
flash[:notice] = 'You cannot edit or delete voteable that you do not own!'
|
109
|
+
format.html do
|
110
|
+
redirect_to user_path(current_user)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<%
|
2
|
+
# You can't vote if it is your quote,
|
3
|
+
# you are not logged in,
|
4
|
+
# or you have already voted on this item
|
5
|
+
|
6
|
+
unless quote.user == current_user ||
|
7
|
+
!logged_in? ||
|
8
|
+
current_user.voted_on?(@voteable)
|
9
|
+
%>
|
10
|
+
|
11
|
+
<%= link_to_remote "Up",
|
12
|
+
:url => user_voteable_votes_path(voteable.user, voteable, :vote => :true, :format => :rjs),
|
13
|
+
:method => :post
|
14
|
+
%>
|
15
|
+
/
|
16
|
+
<%= link_to_remote "Down",
|
17
|
+
:url => user_voteable_votes_path(voteable.user, voteable, :vote => :false, :format => :rjs),
|
18
|
+
:method => :post
|
19
|
+
%>
|
20
|
+
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
Votes: <%= voteable.votes_for - voteable.votes_against %>
|
@@ -0,0 +1 @@
|
|
1
|
+
page.replace_html "votes_#{@voteable.id}", :partial => "voteable_vote", :locals => {:voteable => @voteable}
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# An example controller for "votes" that are nested resources under users. See examples/routes.rb
|
2
|
+
|
3
|
+
class VotesController < ApplicationController
|
4
|
+
|
5
|
+
# First, figure out our nested scope. User or Voteable?
|
6
|
+
before_filter :find_votes_for_my_scope, :only => [:index]
|
7
|
+
|
8
|
+
before_filter :login_required, :only => [:new, :edit, :destroy, :create, :update]
|
9
|
+
before_filter :must_own_vote, :only => [:edit, :destroy, :update]
|
10
|
+
before_filter :not_allowed, :only => [:edit, :update, :new]
|
11
|
+
|
12
|
+
# GET /users/:user_id/votes/
|
13
|
+
# GET /users/:user_id/votes.xml
|
14
|
+
# GET /users/:user_id/voteables/:voteable_id/votes/
|
15
|
+
# GET /users/:user_id/voteables/:voteable_id/votes.xml
|
16
|
+
def index
|
17
|
+
respond_to do |format|
|
18
|
+
format.html # index.html.erb
|
19
|
+
format.xml { render :xml => @votes }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# GET /users/:user_id/votes/1
|
24
|
+
# GET /users/:user_id/votes/1.xml
|
25
|
+
# GET /users/:user_id/voteables/:voteable_id/votes/1
|
26
|
+
# GET /users/:user_id/voteables/:voteable_id/1.xml
|
27
|
+
def show
|
28
|
+
@voteable = Vote.find(params[:id])
|
29
|
+
|
30
|
+
respond_to do |format|
|
31
|
+
format.html # show.html.erb
|
32
|
+
format.xml { render :xml => @vote }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# GET /users/:id/votes/new
|
37
|
+
# GET /users/:id/votes/new.xml
|
38
|
+
# GET /users/:id/votes/new
|
39
|
+
# GET /users/:id/votes/new.xml
|
40
|
+
def new
|
41
|
+
# Not generally used. Most people want to vote via AJAX calls.
|
42
|
+
end
|
43
|
+
|
44
|
+
# GET /users/:id/votes/1/edit
|
45
|
+
def edit
|
46
|
+
# Not generally used. Most people don't want to allow editing of votes.
|
47
|
+
end
|
48
|
+
|
49
|
+
# POST /users/:user_id/voteables/:voteable_id/votes
|
50
|
+
# POST /users/:user_id/voteables/:voteable_id/votes.xml
|
51
|
+
def create
|
52
|
+
@voteable = Voteable.find(params[:quote_id])
|
53
|
+
|
54
|
+
respond_to do |format|
|
55
|
+
if current_user.vote(@voteable, params[:vote])
|
56
|
+
format.rjs { render :action => "create", :vote => @vote }
|
57
|
+
format.html { redirect_to([@voteable.user, @voteable]) }
|
58
|
+
format.xml { render :xml => @voteable, :status => :created, :location => @voteable }
|
59
|
+
else
|
60
|
+
format.rjs { render :action => "error" }
|
61
|
+
format.html { render :action => "new" }
|
62
|
+
format.xml { render :xml => @vote.errors, :status => :unprocessable_entity }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# PUT /users/:id/votes/1
|
68
|
+
# PUT /users/:id/votes/1.xml
|
69
|
+
def update
|
70
|
+
# Not generally used
|
71
|
+
end
|
72
|
+
|
73
|
+
# DELETE /users/:id/votes/1
|
74
|
+
# DELETE /users/:id/votes/1.xml
|
75
|
+
def destroy
|
76
|
+
@vote = Vote.find(params[:id])
|
77
|
+
@vote.destroy
|
78
|
+
|
79
|
+
respond_to do |format|
|
80
|
+
format.html { redirect_to(user_votes_url) }
|
81
|
+
format.xml { head :ok }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
def find_votes_for_my_scope
|
87
|
+
if params[:voteable_id]
|
88
|
+
@votes = Vote.for_voteable(Voteable.find(params[:voteable_id])).descending
|
89
|
+
elsif params[:user_id]
|
90
|
+
@votes = Vote.for_voter(User.find(params[:user_id])).descending
|
91
|
+
else
|
92
|
+
@votes = []
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def must_own_vote
|
97
|
+
@vote ||= Vote.find(params[:id])
|
98
|
+
@vote.user == current_user || ownership_violation
|
99
|
+
end
|
100
|
+
|
101
|
+
def ownership_violation
|
102
|
+
respond_to do |format|
|
103
|
+
flash[:notice] = 'You cannot edit or delete votes that you do not own!'
|
104
|
+
format.html do
|
105
|
+
redirect_to user_path(current_user)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class VoteFuMigration < 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 want to enfore "One Person, One Vote" rules in the database, uncomment 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
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'vote_fu'
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# ActsAsVoteable
|
2
|
+
module Juixe
|
3
|
+
module Acts #:nodoc:
|
4
|
+
module Voteable #:nodoc:
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def acts_as_voteable
|
12
|
+
has_many :votes, :as => :voteable, :dependent => :nullify
|
13
|
+
|
14
|
+
include Juixe::Acts::Voteable::InstanceMethods
|
15
|
+
extend Juixe::Acts::Voteable::SingletonMethods
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# This module contains class methods
|
20
|
+
module SingletonMethods
|
21
|
+
|
22
|
+
# Calculate the vote counts for all voteables of my type.
|
23
|
+
def tally(options = {})
|
24
|
+
find(:all, options_for_tally(options.merge({:order =>"count DESC" })))
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Options:
|
29
|
+
# :start_at - Restrict the votes to those created after a certain time
|
30
|
+
# :end_at - Restrict the votes to those created before a certain time
|
31
|
+
# :conditions - A piece of SQL conditions to add to the query
|
32
|
+
# :limit - The maximum number of voteables to return
|
33
|
+
# :order - A piece of SQL to order by. Eg 'votes.count desc' or 'voteable.created_at desc'
|
34
|
+
# :at_least - Item must have at least X votes
|
35
|
+
# :at_most - Item may not have more than X votes
|
36
|
+
def options_for_tally (options = {})
|
37
|
+
options.assert_valid_keys :start_at, :end_at, :conditions, :at_least, :at_most, :order, :limit
|
38
|
+
|
39
|
+
scope = scope(:find)
|
40
|
+
start_at = sanitize_sql(["#{Vote.table_name}.created_at >= ?", options.delete(:start_at)]) if options[:start_at]
|
41
|
+
end_at = sanitize_sql(["#{Vote.table_name}.created_at <= ?", options.delete(:end_at)]) if options[:end_at]
|
42
|
+
|
43
|
+
type_and_context = "#{Vote.table_name}.voteable_type = #{quote_value(base_class.name)}"
|
44
|
+
|
45
|
+
conditions = [
|
46
|
+
type_and_context,
|
47
|
+
options[:conditions],
|
48
|
+
start_at,
|
49
|
+
end_at
|
50
|
+
]
|
51
|
+
|
52
|
+
conditions = conditions.compact.join(' AND ')
|
53
|
+
conditions = merge_conditions(conditions, scope[:conditions]) if scope
|
54
|
+
|
55
|
+
joins = ["LEFT OUTER JOIN #{Vote.table_name} ON #{table_name}.#{primary_key} = #{Vote.table_name}.voteable_id"]
|
56
|
+
joins << scope[:joins] if scope && scope[:joins]
|
57
|
+
at_least = sanitize_sql(["COUNT(#{Vote.table_name}.id) >= ?", options.delete(:at_least)]) if options[:at_least]
|
58
|
+
at_most = sanitize_sql(["COUNT(#{Vote.table_name}.id) <= ?", options.delete(:at_most)]) if options[:at_most]
|
59
|
+
having = [at_least, at_most].compact.join(' AND ')
|
60
|
+
group_by = "#{Vote.table_name}.voteable_id HAVING COUNT(#{Vote.table_name}.id) > 0"
|
61
|
+
group_by << " AND #{having}" unless having.blank?
|
62
|
+
|
63
|
+
{ :select => "#{table_name}.*, COUNT(#{Vote.table_name}.id) AS count",
|
64
|
+
:joins => joins.join(" "),
|
65
|
+
:conditions => conditions,
|
66
|
+
:group => group_by
|
67
|
+
}.update(options)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# This module contains instance methods
|
72
|
+
module InstanceMethods
|
73
|
+
def votes_for
|
74
|
+
Vote.count(:all, :conditions => [
|
75
|
+
"voteable_id = ? AND voteable_type = ? AND vote = ?",
|
76
|
+
id, self.class.name, true
|
77
|
+
])
|
78
|
+
end
|
79
|
+
|
80
|
+
def votes_against
|
81
|
+
Vote.count(:all, :conditions => [
|
82
|
+
"voteable_id = ? AND voteable_type = ? AND vote = ?",
|
83
|
+
id, self.class.name, false
|
84
|
+
])
|
85
|
+
end
|
86
|
+
|
87
|
+
# Same as voteable.votes.size
|
88
|
+
def votes_count
|
89
|
+
self.votes.size
|
90
|
+
end
|
91
|
+
|
92
|
+
def voters_who_voted
|
93
|
+
voters = []
|
94
|
+
self.votes.each { |v|
|
95
|
+
voters << v.voter
|
96
|
+
}
|
97
|
+
voters
|
98
|
+
end
|
99
|
+
|
100
|
+
def voted_by?(voter)
|
101
|
+
rtn = false
|
102
|
+
if voter
|
103
|
+
self.votes.each { |v|
|
104
|
+
rtn = true if (voter.id == v.voter_id && voter.class.name == v.voter_type)
|
105
|
+
}
|
106
|
+
end
|
107
|
+
rtn
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# ActsAsVoter
|
2
|
+
module PeteOnRails
|
3
|
+
module Acts #:nodoc:
|
4
|
+
module Voter #:nodoc:
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def acts_as_voter
|
12
|
+
has_many :votes, :as => :voter, :dependent => :nullify # If a voting entity is deleted, keep the votes.
|
13
|
+
include PeteOnRails::Acts::Voter::InstanceMethods
|
14
|
+
extend PeteOnRails::Acts::Voter::SingletonMethods
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# This module contains class methods
|
19
|
+
module SingletonMethods
|
20
|
+
end
|
21
|
+
|
22
|
+
# This module contains instance methods
|
23
|
+
module InstanceMethods
|
24
|
+
|
25
|
+
# Usage user.vote_count(true) # All +1 votes
|
26
|
+
# user.vote_count(false) # All -1 votes
|
27
|
+
# user.vote_count() # All votes
|
28
|
+
|
29
|
+
def vote_count(for_or_against = "all")
|
30
|
+
where = (for_or_against == "all") ?
|
31
|
+
["voter_id = ? AND voter_type = ?", id, self.class.name ] :
|
32
|
+
["voter_id = ? AND voter_type = ? AND vote = ?", id, self.class.name, for_or_against ]
|
33
|
+
|
34
|
+
Vote.count(:all, :conditions => where)
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def voted_for?(voteable)
|
39
|
+
0 < Vote.count(:all, :conditions => [
|
40
|
+
"voter_id = ? AND voter_type = ? AND vote = ? AND voteable_id = ? AND voteable_type = ?",
|
41
|
+
self.id, self.class.name, true, voteable.id, voteable.class.name
|
42
|
+
])
|
43
|
+
end
|
44
|
+
|
45
|
+
def voted_against?(voteable)
|
46
|
+
0 < Vote.count(:all, :conditions => [
|
47
|
+
"voter_id = ? AND voter_type = ? AND vote = ? AND voteable_id = ? AND voteable_type = ?",
|
48
|
+
self.id, self.class.name, false, voteable.id, voteable.class.name
|
49
|
+
])
|
50
|
+
end
|
51
|
+
|
52
|
+
def voted_on?(voteable)
|
53
|
+
0 < Vote.count(:all, :conditions => [
|
54
|
+
"voter_id = ? AND voter_type = ? AND voteable_id = ? AND voteable_type = ?",
|
55
|
+
self.id, self.class.name, voteable.id, voteable.class.name
|
56
|
+
])
|
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 = Vote.new(:vote => vote, :voteable => voteable, :voter => self)
|
69
|
+
vote.save
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
class VotesController < ApplicationController
|
2
|
+
# First, figure out our nested scope. User or vote?
|
3
|
+
# before_filter :find_my_scope
|
4
|
+
|
5
|
+
# before_filter :find_user
|
6
|
+
|
7
|
+
# before_filter :login_required, :only => [:new, :edit, :destroy, :create, :update]
|
8
|
+
# before_filter :must_own_vote, :only => [:edit, :destroy, :update]
|
9
|
+
|
10
|
+
|
11
|
+
# GET /votes/
|
12
|
+
# GET /votes.xml
|
13
|
+
def index
|
14
|
+
@votes = Vote.descending
|
15
|
+
|
16
|
+
respond_to do |format|
|
17
|
+
format.html # index.html.erb
|
18
|
+
format.xml { render :xml => @votes }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# GET /votes/1
|
23
|
+
# GET /votes/1.xml
|
24
|
+
def show
|
25
|
+
@vote = Vote.find(params[:id])
|
26
|
+
|
27
|
+
respond_to do |format|
|
28
|
+
format.html # show.html.erb
|
29
|
+
format.xml { render :xml => @vote }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# GET /votes/new
|
34
|
+
# GET /votes/new.xml
|
35
|
+
def new
|
36
|
+
@vote = Vote.new
|
37
|
+
|
38
|
+
respond_to do |format|
|
39
|
+
format.html # new.html.erb
|
40
|
+
format.xml { render :xml => @vote }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# GET /votes/1/edit
|
45
|
+
def edit
|
46
|
+
@vote ||= Vote.find(params[:id])
|
47
|
+
end
|
48
|
+
|
49
|
+
# POST /votes
|
50
|
+
# POST /votes.xml
|
51
|
+
def create
|
52
|
+
@vote = Vote.new(params[:vote])
|
53
|
+
@vote.user = current_user
|
54
|
+
|
55
|
+
respond_to do |format|
|
56
|
+
if @vote.save
|
57
|
+
flash[:notice] = 'Vote was successfully saved.'
|
58
|
+
format.html { redirect_to([@user, @vote]) }
|
59
|
+
format.xml { render :xml => @vote, :status => :created, :location => @vote }
|
60
|
+
else
|
61
|
+
format.html { render :action => "new" }
|
62
|
+
format.xml { render :xml => @vote.errors, :status => :unprocessable_entity }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# PUT /votes/1
|
68
|
+
# PUT /votes/1.xml
|
69
|
+
def update
|
70
|
+
@vote = Vote.find(params[:id])
|
71
|
+
|
72
|
+
respond_to do |format|
|
73
|
+
if @vote.update_attributes(params[:vote])
|
74
|
+
flash[:notice] = 'Vote was successfully updated.'
|
75
|
+
format.html { redirect_to([@user, @vote]) }
|
76
|
+
format.xml { head :ok }
|
77
|
+
else
|
78
|
+
format.html { render :action => "edit" }
|
79
|
+
format.xml { render :xml => @vote.errors, :status => :unprocessable_entity }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# DELETE /votes/1
|
85
|
+
# DELETE /votes/1.xml
|
86
|
+
def destroy
|
87
|
+
@vote = Vote.find(params[:id])
|
88
|
+
@vote.destroy
|
89
|
+
|
90
|
+
respond_to do |format|
|
91
|
+
format.html { redirect_to(user_votes_url) }
|
92
|
+
format.xml { head :ok }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
data/lib/has_karma.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Has Karma
|
2
|
+
|
3
|
+
module PeteOnRails
|
4
|
+
module VoteFu #:nodoc:
|
5
|
+
module Karma #:nodoc:
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
class << base
|
10
|
+
attr_accessor :karmatic_objects
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def has_karma(voteable_type)
|
16
|
+
self.class_eval <<-RUBY
|
17
|
+
def karma_voteable
|
18
|
+
#{voteable_type.to_s.classify}
|
19
|
+
end
|
20
|
+
RUBY
|
21
|
+
include PeteOnRails::VoteFu::Karma::InstanceMethods
|
22
|
+
extend PeteOnRails::VoteFu::Karma::SingletonMethods
|
23
|
+
if self.karmatic_objects.nil?
|
24
|
+
self.karmatic_objects = [eval(voteable_type.to_s.classify)]
|
25
|
+
else
|
26
|
+
self.karmatic_objects.push(eval(voteable_type.to_s.classify))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# This module contains class methods
|
32
|
+
module SingletonMethods
|
33
|
+
|
34
|
+
## Not yet implemented. Don't use it!
|
35
|
+
# Find the most popular users
|
36
|
+
def find_most_karmic
|
37
|
+
find(:all)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
# This module contains instance methods
|
43
|
+
module InstanceMethods
|
44
|
+
def karma(options = {})
|
45
|
+
#FIXME cannot have 2 models imapcting the karma simultaneously
|
46
|
+
# count the total number of votes on all of the voteable objects that are related to this object
|
47
|
+
#2009-01-30 GuillaumeNM The following line is not SQLite3 compatible, because boolean are stored as 'f' or 't', not '1', or '0'
|
48
|
+
#self.karma_voteable.sum(:vote, options_for_karma(options))
|
49
|
+
#self.karma_voteable.find(:all, options_for_karma(options)).length
|
50
|
+
karma_value = 0
|
51
|
+
self.class.karmatic_objects.each do |object|
|
52
|
+
karma_value += object.find(:all, options_for_karma(object, options)).length
|
53
|
+
end
|
54
|
+
return karma_value
|
55
|
+
end
|
56
|
+
|
57
|
+
def options_for_karma (object, options = {})
|
58
|
+
#GuillaumeNM : 2009-01-30 Adding condition for SQLite3
|
59
|
+
conditions = ["u.id = ? AND vote = ?" , self[:id] , true]
|
60
|
+
joins = ["inner join votes v on #{object.table_name}.id = v.voteable_id", "inner join #{self.class.table_name} u on u.id = #{object.name.tableize}.#{self.class.name.foreign_key}"]
|
61
|
+
{ :joins => joins.join(" "), :conditions => conditions }.update(options)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/models/vote.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class Vote < ActiveRecord::Base
|
2
|
+
|
3
|
+
named_scope :for_voter, lambda { |*args| {:conditions => ["voter_id = ? AND voter_type = ?", args.first.id, args.first.type.name]} }
|
4
|
+
named_scope :for_voteable, lambda { |*args| {:conditions => ["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.type.name]} }
|
5
|
+
named_scope :recent, lambda { |*args| {:conditions => ["created_at > ?", (args.first || 2.weeks.ago).to_s(:db)]} }
|
6
|
+
named_scope :descending, :order => "created_at DESC"
|
7
|
+
|
8
|
+
# NOTE: Votes belong to the "voteable" interface, and also to voters
|
9
|
+
belongs_to :voteable, :polymorphic => true
|
10
|
+
belongs_to :voter, :polymorphic => true
|
11
|
+
|
12
|
+
attr_accessible :vote, :voter, :voteable
|
13
|
+
|
14
|
+
# Uncomment this to limit users to a single vote on each item.
|
15
|
+
# validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
|
16
|
+
|
17
|
+
end
|
data/lib/vote_fu.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'acts_as_voteable'
|
2
|
+
require 'acts_as_voter'
|
3
|
+
require 'has_karma'
|
4
|
+
require 'models/vote.rb'
|
5
|
+
|
6
|
+
ActiveRecord::Base.send(:include, Juixe::Acts::Voteable)
|
7
|
+
ActiveRecord::Base.send(:include, PeteOnRails::Acts::Voter)
|
8
|
+
ActiveRecord::Base.send(:include, PeteOnRails::VoteFu::Karma)
|
9
|
+
RAILS_DEFAULT_LOGGER.info "** vote_fu: initialized properly."
|
data/rails/init.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
RAILS_DEFAULT_LOGGER.info "** vote_fu: 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 'vote_fu'
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aflatter-vote_fu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.11
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Peter Jackson
|
8
|
+
- Cosmin Radoi
|
9
|
+
- Bence Nagy
|
10
|
+
- Rob Maddox
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
|
15
|
+
date: 2009-02-11 00:00:00 +01:00
|
16
|
+
default_executable:
|
17
|
+
dependencies: []
|
18
|
+
|
19
|
+
description: VoteFu provides the ability to have multiple voting entities on an arbitrary number of models in ActiveRecord.
|
20
|
+
email: pete@peteonrails.com
|
21
|
+
executables: []
|
22
|
+
|
23
|
+
extensions: []
|
24
|
+
|
25
|
+
extra_rdoc_files: []
|
26
|
+
|
27
|
+
files:
|
28
|
+
- CHANGELOG.markdown
|
29
|
+
- MIT-LICENSE
|
30
|
+
- README.markdown
|
31
|
+
- generators/vote_fu/vote_fu_generator.rb
|
32
|
+
- generators/vote_fu/templates/migration.rb
|
33
|
+
- init.rb
|
34
|
+
- lib/vote_fu.rb
|
35
|
+
- lib/acts_as_voteable.rb
|
36
|
+
- lib/acts_as_voter.rb
|
37
|
+
- lib/has_karma.rb
|
38
|
+
- lib/models/vote.rb
|
39
|
+
- lib/controllers/votes_controller.rb
|
40
|
+
- test/vote_fu_test.rb
|
41
|
+
- examples/votes_controller.rb
|
42
|
+
- examples/users_controller.rb
|
43
|
+
- examples/voteables_controller.rb
|
44
|
+
- examples/voteable.rb
|
45
|
+
- examples/voteable.html.erb
|
46
|
+
- examples/votes/_voteable_vote.html.erb
|
47
|
+
- examples/votes/create.rjs
|
48
|
+
- examples/routes.rb
|
49
|
+
- rails/init.rb
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: http://blog.peteonrails.com/vote-fu
|
52
|
+
licenses: []
|
53
|
+
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.3.5
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Voting for ActiveRecord with multiple vote sources and advanced features.
|
78
|
+
test_files: []
|
79
|
+
|