RedisVoteable

RedisVoteable is an extension for building a simple voting system for Rails applications. It does not need to be used with any ORM in particular, however classes in which it is used must define an id method. Also, only the ActiveSupport library from Rails is used.

Installation

Add RedisVoteable to your Gemfile

gem 'redis_voteable'

afterwards execute

bundle install

and you're done.

Configuration

By default, RedisVoteable's settings are

redis_voteable_settings = {
  :host       => 'localhost',
  :port       => '6379',
  :db         => 0,
  :key_prefix => 'vote:'
}

If you'd like to override any of the settings, just add a line like the following one to a config file or initializer.

RedisVoteable::redis_voteable_settings = { 
  :db         => 4,
  :key_prefix => 'voterecord:'
}

Usage

Note that in this example an ActiveRecord model is used, however, any other ORM will do, as long as the object defines an id method. Also, for a couple of methods, a find or get method is required. I'll point those out below.

# Specify a voteable model.
class Option < ActiveRecord::Base
  include RedisVoteable
  acts_as_voteable
end

# Specify a voter model.
class User < ActiveRecord::Base
  include RedisVoteable
  acts_as_voter
end

# Votes up the question by the user.
# If the user already voted the question up then an AlreadyVotedError is raised.
# If the same user already voted the question down then the vote is changed to an up vote.
user.up_vote(question)

# Votes the question up, but without raising an AlreadyVotedError when the user
# already voted the question up (it just ignores the vote and returns false).
user.up_vote!(question)

user.down_vote(question)
user.down_vote!(question)

# Clears a already done vote by an user.
# If the user didn't vote for the question then a NotVotedError is raised.
user.clear_vote(question)

# Does not raise a NotVotedError if the user didn't vote for the question
# (it just ignores the unvote and returns false).
user.clear_vote!(question)

# If you'd prefer, unvote is an alias for clear_vote.
user.unvote(question)
user.unvote!(question)

# The number of up votes for this question.
question.up_votes

# The number of down votes for this question.
question.down_votes

# The total number of votes for this question.
question.total_votes

# The number of up votes the user has cast.
user.up_votes

# The number of down votes the user has cat.
user.down_votes

# The total number of votes the user has cast.
user.total_votes

# up votes - down votes (may also be negative if there are more down votes than up votes)
question.tally

# The lower bound of the Wilson confidence interval. The default value for
# z is 1.4395314800662002, which estimates bounds with 85% confidence.
# The value can be modified in lib/voteable.rb.
# See http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval
# and: http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
question.confidence

# The upper bound of the Wilson confidence interval.
question.confidence(:upper)

# Returns true if the question was voted by the user
user.voted?(question)

# Returns true if the question was up voted by the user, false otherwise
user.up_voted?(question)

# Returns true if the question was down voted by the user, false otherwise
user.down_voted?(question)

# Returns :up, :down, or nil depending on how the user voted
user.vote_value?(question)

# Access voted voteables through voter (slow)
voteable = user.voteables.first
voteable.up_voted?(user) # true if up voted by user, false otherwise
voteable.vote_value?(user) # returns :up, :down, or nil if user didn't vote on voteable

# Access votings through voteable (slow)
voter = question.voters.first
voter.up_voted?(question) # true if up voted question, false otherwise
voter.vote_value?(question) # returns :up, :down, or nil if voter didn't vote on question

TO DO:

  • Add support for getting raw voteable and voter arrays, which would save time by not instantiating every object.

  • (Related to the next point.) Automatic ranking of voteables. Consider using a sorted set in redis.

  • Add some sort of namespacing/grouping support. That way, voteables could be grouped and ranked within their group. An example use case would be users voting on many options to multiple questions. The options would be grouped by the question to which they belonged and could be easily ranked for a question. Could use sorted set to store grouping and rankings within groupings, but that may not be practical. Could also store the keys of all of the voteables in a grouping in a set on Redis and rank them in Ruby, which makes it possible to rank by Wilson confidence score.

Copyright © 2011 Chris Brauchli, released under the MIT license