Kevin
Kevin

Reputation: 635

How to implement one vote per user per comment?

I currently have a comment controller that has the method vote_up and vote_down this is how my vote_up currently works.

My Comment model has description and a count field.

  def vote_up
    @comment = Comment.find(params[:comment_id])
    @comment.count += 1
    if @comment.save
      flash[:notice] = "Thank you for voting"
      respond_to do |format|
        format.html { redirect_to show_question_path(@comment.question) }
        format.js
      end
    else
      flash[:notice] = "Error Voting Please Try Again"
      redirect_to show_question_path(@comment.question)
    end
  end

This allows for multiple vote up and downs. How would I design it so that a user can only vote once per comment but somehow keep track if they voted up or down, so they have the ability to change their vote if they want too.

Upvotes: 5

Views: 2190

Answers (3)

bassneck
bassneck

Reputation: 4043

You could do something like this. It prohibits identical votes but allows changing the vote to the opposite (it's a thumbs up/thumbs down system).

def vote(value, user) # this goes to your model

  #find vote for this instance by the given user OR create a new one
  vote = votes.where(:user_id => user).first || votes.build(:user_id => user)

  if value == :for
    vote_value = 1
  elsif value == :against
    vote_value = -1
  end

  if vote.value != vote_value
    vote.value = vote_value
    vote.save
  end
end

migration:

def self.up
    create_table :votes do |t|
    t.references :comment, :null => false
    t.references :user, :null => false
    t.integer :value, :null => false
  end
  add_index :votes, :post_id
  add_index :votes, :user_id
  add_index :votes, [:post_id, :user_id], :unique => true
end

Alternatively, you could use a gem called thumbs_up or any other.

Upvotes: 3

Anatoly
Anatoly

Reputation: 15530

class AnswersController < ApplicationsController
  def vote
    #params[:answer_id][:vote]
    #it can be "1" or "-1"
    @answer = Answer.find(params[:answer_id])
    @answer.vote!(params[:answer_id][:vote])
  end

  def show
    @answer = Answer.find(params[:answer_id])
    @answer.votes.total_sum
  end

end

class Answer < ActiveRecord::Base
  has_many :votes do
    def total_sum
      votes.sum(:vote)
    end
  end


  def vote!(t)
    self.votes.create(:vote => t.to_i)
  end

end

class Vote < ActiveRecord::Base
  belongs_to :answer
  belongs_to :user

  validates_uniqueness_of :user_id, :scope => :answer_id
end

Upvotes: 2

stephenmurdoch
stephenmurdoch

Reputation: 34613

you could perhaps add a validation in your model to make sure that count is numerically equal to or less than 1

validates :count, :numericality => { :less_than_or_equal_to => 1 }

Upvotes: 1

Related Questions