Acts As Votable gem for Rails Apps

Posted by PRbsas on February 23, 2018

Acts As Votable is a Ruby Gem written for Rails ActiveRecord. This gem adds methods to your model that allow it to vote or be voted on while providing an easy syntax to read and write. This makes it easy to enable user interaction with the content of your application.

Getting started

Setup

First you’ll need to add the gem to your Gemfile: gem 'acts_as_votable', '~> 0.10.0'.

And run bundle install in your terminal.

Migrations

Acts As Votable uses a votes table to store all the voting information, so you’ll need to generate a migration and migrate the database as well:

rails generate acts_as_votable:migration
rake db:migrate

The migration generated:

class ActsAsVotableMigration < ActiveRecord::Migration[4.2]
  def self.up
    create_table :votes do |t|

      t.references :votable, :polymorphic => true
      t.references :voter, :polymorphic => true

      t.boolean :vote_flag
      t.string :vote_scope
      t.integer :vote_weight

      t.timestamps
    end

    add_index :votes, [:voter_id, :voter_type, :vote_scope]
    add_index :votes, [:votable_id, :votable_type, :vote_scope]
  end

  def self.down
    drop_table :votes
  end
end

Models

To make a model votable you will need to add to it the acts_as_votable helper:

class Post < ApplicationRecord
  acts_as_votable
end

You can have models act_as_voters, this will provide them with more helper methods.

class Post < ApplicationRecord
  acts_as_voter
end

Routes

In app/config/routes.rb you’ll need to add a nested block to the votable model routes, with upvote and downvote actions:

resources :posts do
  member do
    put 'like', to: 'posts#upvote'
    put 'dislike', to: 'posts#downvote'
  end
end

This will generate two new routes for you:

Prefix        Verb   URI Pattern      			    Controller#Action
like_post     PUT    /posts/:id/like(.:format)      posts#upvote
dislike_post  PUT    /posts/:id/dislike(.:format)   posts#downvote

Controllers

In the controller we will need to define the #upvote and #downvote actions:

def upvote
	@post = Post.find(params[:id])
    @post.upvote_by current_user
    redirect_back fallback_location: root_path
  end
def downvote
	@post = Post.find(params[:id])
    @post.downvote_by current_user
    redirect_back fallback_location: root_path
  end

The current_user helper method is provided by Devise to check for the current signed-in user. (Devise is a flexible authentication solution for Rails applications).

The user votes and gets redirected to the current page with redirect_back fallback_location: root_path.

Views

Lastly, making this functionality visible in the browser:

<%= link_to like_post_path(@post), method: :put do %>
   <div class="engagement">
     <%= image_tag("up") %>
     <p><%= @post.get_upvotes.size %> </p>
   </div>
<% end %>
<%= link_to dislike_post_path(@post), method: :put do %>
   <div class="engagement">
     <%= image_tag("down") %>
     <p><%= @post.get_downvotes.size %> </p>
   </div>
<% end %>

Next

This gem provides lots of helper methods, for example: you can count the upvotes and downvotes a model has.

@post.votes_for.size # => 10

@post.get_likes.size # => 7
@post.get_upvotes.size # => 7

@post.get_dislikes.size # => 3
@post.get_downvotes.size # => 3

You can find more useful methods in the Acts As Votable GitHub Repo.