Rails plugin for Voting
Highly customizable Voting system for Rails.
Install the gem by adding the following to your Gemfile:
gem 'votable'
Install the plugin to your app. This will create a new initializer. This is where you can configure default options for votable.
rails generate votable:install
Next you'll need to generate the Vote model and migration. Currently only ActiveRecord is supported.
# usage: rails generate votable [NAME]
rails g votable Vote
Next, in order to use votable, you need to define one or more voter. Voters
use the votes_on
method to define what models they can vote on. The
votes_on
method also accepts local overrides for any of Votables options
(ie. :default_vote_class
and :allow_recast
). Here is an example:
class User < ActiveRecord::Base
votes_on :posts, :questions
end
Models which get voted on, are called Votables. These models use the
votable_by
method to define what Voters can vote on them.
class Post < ActiveRecord::Base
votable_by :users
end
class Question < ActiveRecord::Base
votable_by :users
end
Under the hood, the votes_on
and votable_by
methods setup the
necessary relationships and associations between your models.
In this example, User will now be related to Vote records under the
post_votes
and question_votes
association. Since the relationship is
polymorphic, the class types will also be included.
Coming Soon Eventually, there will a rich enough API so one can relate a given voter and votable model more than once. The key here is naming the uniquely association (and generated methods)
Once you've defined Voters and Votables, you can now cast votes. Votable automatically generates vote casting methods on the Voter class for all objects they can vote on.
Continuing with the above example, the following would be available to the User class:
# cast an up Vote on a Post instance
current_user.cast_post_vote(post_instance, 1)
# cast a down Vote on a Question instance
current_user.cast_question_vote(question_instance, -1)
Votable gives you complete control over what value of the vote actually is. This provides a lot of flexibile
Votable will automatically store a running total of Vote values if the correct attributes are defined. Vote counter caches should exist under the Votable class.
Step one in adding a counter cache is to create a migration for it. The name of the counter cache column should match whatever Voter type you are keeping count of. For example, suppose you wanted to create a total value cache for User votes on Posts:
rails g migration AddVotableCacheToPosts user_votes_total:integer
Now, whenever a Vote is created or updated by a User for a Post, the
user_votes_count
cache will be updated accordingly.
It's recommend that you also add :default => 0
to your migration in
order to ensure sorting and ordering works as expected for this
attribute.
Note: The attribute name ends with _total
rather than _count
.
This is done on purpose to avoid clashing with ActiveRecord's own
:counter_cache
option which is designed to count how many instances
have been created. Votable will eventually add an option to enable
the :counter_cache
in order to track how many votes have been
cast altogether for a given Voter and Votable.
Coming Soon An API for customizable counter caches with different conditions tied to them (ie. positive and negative values).
In addition to the vote casting method, the following methods are
also generated under the _votes
association for the Voter class.
voted_on?(object, direction = nil)
The voted_on?
method returns either true
or false
. You must call
this method with either an instance of the Votable class or an ID.
The method also takes an optional second argument. This can either
be: :up
, :down
, :positive
, :negative
or nil
(the default)
For example:
# check if the current_user voted at all on a given Post
current_user.post_votes.voted_on?(post_instance)
# check if user voted up (positive) on a given Question
current_user.question_votes.voted_on?(question_instance, :up)
Here is a list of upcoming features and ideas. Feel free to submit pull requests!
API options for customizing association names. This will allow for mulitple votable relationships between the same models.
Counter cache for pure vote total. Presently, we just track the
running vote total. Can likely just leverage the existing
:counter_cache
options in ActiveRecord.
Conditional counter caches to allow for custom counts with arbitrary logic tied to it (ie. positive and negative counts).
Add options for allowing multiple votes for a given Voter and Votable.
Vote value validation which will likely exist at the controller level rather than the model layer. Maintaining flexibility is key (ie. some votes for a given Voter and Votable may require totally different values than another Voter\Votable pair).
Voter reputations. Support decreasing reputation on down\negative votes.
Options and support to destroy Votes. Presently, deleting a vote will throw off the total count caches.