How to Add Voting to Rails App

05 Aug 2014

Posted by Milos Dolobac

In this post I want to show to add upvotes and dowvotes using acts_as votable gem.

What do You Need

  • Basic Authentication with current_user helper(Devise or something else)
  • One model with name Link and controller links
  • acts_as_votable gem installed

How to setup acts_as_votable

Add gem acts_as_votable to your Gemfile.

Gemfile

'acts_as_votable'

And install it with bundle

bundle install

Now generate acts_as_votable migration

rails generate acts_as_votable:migration

And migrate to database

rake db:migrate

Now we’re done with preparation let’s add upvote action.

Adding upvotes

First thing we need to is to add acts_as_votable helper to our model.

app/models/link.rb

class Link < ActiveRecord::Base
  acts_as_votable
end

Helper will provide useful methods like this:

upvote_by ..... => Add upvote by current_user
get_dislikes.size => Count all downvotes
get_likes.size => Count all upvotes

You can checkout all methods at acts_as_votable documentation.

So now when we know how acts_as_votable can helps us, let’s add upvoting to our controller.

app/controllers/links_controller.rb

def upvote
  @link = Link.find(params[:id])
  @link.upvote_by current_user
  redirect_to links_path
end

config/routes.rb

resources :links do
  member do
    put "like", to: "links#upvote"
    put "dislike", to: "links#downvote"

Here we’ve added two routes, one for upvoting and another for downvoting that will update current like and dislikes size in votes table.

Now we need to add upvote route to our root path.

app/views/links/index.html.slim

- @links.each do |link|
  = link_to "upvote", like_link_path(link), method: :put

If you click on link, and checkout upvotes size like this in console:

@link = Link.find(1)
@link.get_upvotes.size => 0

You certainly noticed that nothing will happen. Of course because voting only counts when we’re signed in. Try to signin and now click on link. Now if you checkout in console, everything should work.

@link = Link.find(1)
@link.get_upvotes.size => 1

Adding downvoting

Adding downvoting to rails app is reversed process to upvoting, so instead of upvote_by you will add downvote_by

app/controllers/links_controller.rb

def downvote
  @link = Link.find(params[:id])
  @link.downvote_by current_user
  redirect_to links_path
end

Add path to links path.

app/views/links/index.html.slim

= link_to "downvote", dislike_link_path(link), method: :put

Next thing we need to is to add link’s score. Adding score is easy, thanks to methods get_upvotes.size and get_downvotes.size we can just add score method to our model.

app/models/link.rb

def score
  self.get_upvotes.size - self.get_downvotes.size

And this to your view file

app/views/links/index.html.slim

= link.score

Now if you click on link, you should see that our score is automatically updated by downvotes and upvotes.

That’s all for now, if you don’t understand anything, feel free to ask in discussion.

comments powered by Disqus