A Binomial proportion confidence interval voting system with scope and cache enabled.
Voting uses Binomial proportion confidence interval to calculate the voting. Inspired on Evan Miller Article and used by Reddit, Yelp, Digg and probably other, Voting gives you cache and option to work with scopes over a binary voting system.
If you are looking for 5 stars voting system, check Rating 🌟
Add the following code on your Gemfile
and run bundle install
:
gem 'voting'
Run the following task to create a Voting migration:
rails g voting:install
Then execute the migrations to create the tables voting_votes
and voting_votings
:
rake db:migrate
Just add the callback voting
to your model:
class Author < ApplicationRecord
voting
end
Now this model can vote or receive votes.
You can vote on some resource using integer or string as value:
author_1 = Author.first
author_2 = Author.last
resource = Comment.last
author_1.vote(resource, 1) # +1 vote
author_2.vote(resource, -1) # -1 vote
This mehod will return the status of a Voting::Vote
object as positive
, negative
or none
.
author = Author.first
resource = Comment.last
author.vote(resource, 1).status # 'positive'
author.vote(resource, -1) # 'negative'
author.vote(resource, -1) # 'none'
A voted resource exposes a cached data about it state:
resource = Comment.last
resource.voting
It will return a Voting::Voting
object that keeps:
author
: the author of this vote;
estimate
: the Binomial proportion confidence interval value;
negative
: the sum of negative votes for this resource;
positive
: the sum of positive votes for this resource;
resource
: the self object that received this vote;
scopeable
: the object used as scope;
You can retrieve the vote an author gave to a specific resource:
author = Author.last
resource = Comment.last
author.vote_for resource
It will return a Voting::Vote
object that keeps:
author
: the author of vote;
resource
: the resource that received the vote;
negative
: the -1 value when vote was negative;
positive
: the 1 value when vote was positive;
scopeable
: the object used as scope;
Maybe you want just to know if some author already voted some resource and receive true
or false
:
author = Author.last
resource = Comment.last
author.voted? resource
If you want to know if the vote was positive
or negative
, just pass a symbol about it:
author.voted? resource, :negative
author.voted? resource, :positive
You can retrieve all votes received by some resource:
resource = Article.last
resource.votes
It will return a collection of Voting::Vote
object.
In the same way you can retrieve all votes that some author made:
author = Author.last
author.voted
It will return a collection of Voting::Vote
object.
You can list resource ordered by voting data:
Comment.order_by_voting
It will return a collection of resource ordered by estimate desc
as default.
The order column and direction can be changed:
Comment.order_by_voting :negative, :asc
It will return a collection of resource ordered by Voting::Voting
table data.
Maybe you want to recover all records, so you can add the suffix _records
on relations:
author = Author.last
resource = Comment.last
author.vote resource, 1
author.voting_records
author.voted_records
comment.voting_records
If you have a model that will only be able to vote but not to receive a vote, configure it as author
.
An author model still can be voted, but won't generate a Voting record with all values as zero to warm up the cache.
voting as: :author
You can to use alias to directly call vote
with positive or negative data.
author = Author.last
resource = Comment.last
author.up resource # +1
author.down resource # -1
down
: makes a negative vote;
up
: makes a positive vote;
The toggle functions works out of box, so if you vote up twice or vote twice down, the vote will be canceled.
When you do that, the vote record is not destroyed instead, it receives zero on negative
and positive
column.
All methods accepts a scope
param to be persisted with the vote or to be searched:
category = Category.last
author.down resource, scope: category
author.up resource, scope: category
author.vote resource, 1, scope: category
author.vote_for resource, scope: category
author.voted resource, scope: category
author.voted? resource, :negative, scope: category
author.voted? resource, :positive, scope: category
resource.votes scope: category
author .voted scope: category
resource.votign scope: category
If you need to warm up a record with scope, you need to setup the scoping
relation.
class Resource < ApplicationRecord
voting scoping: :categories
end
Now, when a resource is created, the cache will be generated for each related category
as scopeable
.