Search code examples
ruby-on-railsrubydryvoting-system

DRY voting methods


I have a prompt asking to write my up_vote and down_vote methods in only two lines using 'redirect_to' and the 'update_vote!' method as presented below. Implementing redirect_to is easy enough, but I'm not quite sure how to write my up/down_vote methods concisely using the existing 'update_vote!' method. Any help is appreciated.

 class VotesController < ApplicationController
      before_action :load_post_and_vote

      def up_vote

        if @vote
          @vote.update_attribute(:value, 1)
        else
          @vote = current_user.votes.create(value: 1, post: @post)
        end

        # http://apidoc.com/rails/ActionController/Base/redirect_to
        redirect_to :back
      end

      def down_vote

        if @vote
          @vote.update_attribute(:value, -1)
        else
          @vote = current_user.votes.create(value: -1, post: @post)
        end

        # http://apidoc.com/rails/ActionController/Base/redirect_to
        redirect_to :back
      end

      private

      def load_post_and_vote
        @post = Post.find(params[:post_id])

        @vote = @post.votes.where(user_id: current_user.id).first
      end

      def update_vote!(new_value)
        if @vote
          authorize @vote, :update?
          @vote.update_attribute(:value, new_value)
        else
          @vote = current_user.votes.build(value: new_value, post: @post)
          authorize @vote, :create
          @vote.save
        end
      end
    end

Solution

  • You should invoke the update_vote! method. How about:

    def up_vote        
      update_vote!(1)
      # http://apidoc.com/rails/ActionController/Base/redirect_to
      redirect_to :back
    end
    
    def down_vote
        update_vote!(-1)
        # http://apidoc.com/rails/ActionController/Base/redirect_to
        redirect_to :back
    end
    

    Also your method can be re-written as :

    def update_vote!(new_value)
      Vote.find_or_create_by post: @post do |v|
        authorize v, :update?
        v.user_id = current_user.id
        v.value = new_value
        v.save!
      end
    end
    

    Read find_or_create_by.