0
votes

I'm trying to add a user karma feature to my app and I'm almost done, just that the karma is being awarded to a different user.

NB, My like system is from scratch and not acts_as_votable.

What I want:

  1. When a user upvotes a book, I want a +1 karma be awarded to the book.user
  2. If a user's books are downvoted more then they upvoted, I want such user have negative karma.

What I'm getting:

  1. When a book is upvoted, the user who upvoted the book gets the +1 karma instead of the book.user.
  2. When a user with 0 karma gets his/her book downvoted, the karma incrment by 1 instead of decrementing.

    class AddKarmaToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :karma, :integer, default: 0 end end

My code:

vote.rb

class Vote < ApplicationRecord
  belongs_to :user
  belongs_to :book

  validates_uniqueness_of :user_id, scope: :book_id

  after_create :increment_vote, :add_karma
  after_destroy :decrement_vote, :remove_karma

  private

  def increment_vote
    field = self.upvote ? :upvotes : :downvotes
    Book.find(self.book_id).increment(field).save
  end

  def decrement_vote
    field = self.upvote ? :upvotes : :downvotes
    Book.find(self.book_id).decrement(field).save
  end

  def add_karma
    user = User.find(self.user_id)
    user.increment(:karma, 1).save
  end

  def remove_karma
    user = User.find(self.user_id)
    user.decrement(:karma, 1).save
  end
end

votes_controller.rb

class VotesController < ApplicationController
    def create
        book_id = params[:book_id]

        vote = Vote.new
        vote.book_id = params[:book_id]
        vote.upvote = params[:upvote]
        vote.user_id = current_user.id

        #check if vote by this user exists
        existing_vote = Vote.where(user_id: current_user.id, book_id: book_id)
        @new_vote = existing_vote.size < 1

        respond_to do |format|
            format.js {
                if existing_vote.size > 0
                    #destroy existing vote
                    existing_vote.first.destroy
                else
                    #save new vote
                    if vote.save
                        @success = true
                    else
                        @success = false
                    end

                    # @total_upvotes = @book.upvotes
                    # @total_downvotes = @book.downvotes
                end
                @book = Book.find(book_id)
                @is_upvote = params[:upvote]

                render "votes/create"
            }
        end
    end

    private

    def vote_params
        params.require(:vote).permit(:upvote, :book_id)
    end
end
1

1 Answers

1
votes

First of all when using active record relations you don't need to call Model.find in the class, just call the relation with it's name:

  def increment_vote
    field = self.upvote ? :upvotes : :downvotes
    book.increment(field).save
  end

  def add_karma
    user.increment(:karma, 1).save
  end

In add_karma and remove_karma you are referencing the user that the vote belongs to, and not the user that owns the book. To achieve your goal you should also increment and decrement karma on the book's owner:

  def add_karma
    user.increment(:karma, 1).save
    book.user.increment(:karma, self.upvote ? 1 : -1).save
  end

  def remove_karma
    user.increment(:karma, 1).save
    book.user.decrement(:karma, 1).save
  end

You could rewrite your controller to simplify the code:

class VotesController < ApplicationController
  def create
    @vote = current_user.votes.find_or_initialize_by vote_params[:book_id]
    @vote.assign_attributes vote_params
    @success = @vote.save
    # instead of @book = @vote.book just use @vote.book in your view
    @book = @vote.book
    # instead of @is_upvote you can use @vote.upvote in your view
    @is_upvote = @vote.upvote
    respond_to do |format|
      format.js { render 'votes/create'}
    end
  end

  private

  def vote_params
    params.require(:vote).permit(:upvote, :book_id)
  end
end