I've created a voting system that's similar to Stack Exchange's in a Ruby on Rails web app. It's working great - as far as I can tell, all the edge cases are caught. This is the action to cast a vote.
There are some conditions I've had to satisfy:
- must not allow duplicate voting, even from console requests
- one vote per post (can't have an up and a down vote on the same post)
- must be logged in
At the moment, I'm updating the record if there's already a vote by this user on this post, but I wonder if that's the most efficient strategy. Would destroying the old row and creating a new record be faster?
I'd particularly appreciate efficiency comments - at the moment, this is a very server heavy action, which I'd like to minimise.
def create
post = (params[:post_type] == "a" ? Answer.find(params[:post_id]) : Question.find(params[:post_id]))
existing = Vote.where(:post => post, :user => current_user)
if existing.count > 0
if existing.first.vote_type == params[:vote_type].to_i
render :plain => "You have already voted.", :status => 409 and return
else
# There's already a vote by this user on this post, so we may as well update that instead of removing it.
vote = existing.first
vote.vote_type = params[:vote_type].to_i
vote.save!
state = { :status => "modified", :vote_id => vote.id }
render :json => state and return
end
end
vote = Vote.new
vote.user = current_user
vote.post = post
vote.vote_type = params[:vote_type]
vote.save!
state = { :status => "OK", :vote_id => vote.id }
render :json => state and return
end
(A quick note on vote types: the Vote.vote_type
column has type integer
, and the possible values are 0 (upvote) or 1 (downvote)).