rating/README.md

6.0 KiB
Raw Blame History

Rating

Build Status Gem Version Maintainability

A true Bayesian rating system with scope and cache enabled.

JS Rating?

This is Raty: https://github.com/wbotelhos/raty 🌟

Description

Rating uses the know as "True Bayesian Estimate" inspired on IMDb rating with the following formula:

(WR) = (v ÷ (v + m)) × R + (m ÷ (v + m)) × C

IMDb Implementation:

WR: weighted rating

R: average for the movie (mean) = (Rating)

v: number of votes for the movie = (votes)

m: minimum votes required to be listed in the Top 250

C: the mean vote across the whole report

Rating Implementation:

WR: weighted rating

R: average for the resource

v: number of votes for the resource

m: average of the number of votes

C: the average rating based on all resources

Install

Add the following code on your Gemfile and run bundle install:

gem 'rating'

Run the following task to create a Rating migration:

rails g rating:install

Then execute the migrations to create the to create tables rating_rates and rating_ratings:

rake db:migrate

Usage

Just add the callback rating to your model:

class Author < ApplicationRecord
  rating
end

Now this model can vote or receive votes.

rate

You can vote on some resource:

author   = Author.last
resource = Article.last

author.rate(resource, 3)

rating

A voted resource exposes a cached data about it state:

resource = Article.last

resource.rating

It will return a Rating object that keeps:

average: the normal mean of votes;

estimate: the true Bayesian estimate mean value (you should use this over average);

sum: the sum of votes for this resource;

total: the total of votes for this resource.

rate_for

You can retrieve the rate of some author gave to some resource:

author   = Author.last
resource = Article.last

author.rate_for resource

It will return a Rate object that keeps:

author: the author of vote;

resource: the resource that received the vote;

value: the value of the vote.

rated?

Maybe you want just to know if some author already rated some resource and receive true or false:

author   = Author.last
resource = Article.last

author.rated? resource

rates

You can retrieve all rates received by some resource:

resource = Article.last

resource.rates

It will return a collection of Rate object.

rated

In the same way you can retrieve all rates that some author received:

author = Author.last

author.rated

It will return a collection of Rate object.

order_by_rating

You can list resource ordered by rating data:

Article.order_by_rating

It will return a collection of resource ordered by estimate desc as default. The order column and direction can be changed:

Article.order_by_rating :average, :asc

It will return a collection of resource ordered by Rating table data.

Scope

All methods support scope query, since you may want to vote on items of a resource instead the resource itself. Let's say an article belongs to one or more categories and you want to vote on some categories of this article.

category_1 = Category.first
category_2 = Category.second
author     = Author.last
resource   = Article.last

In this situation you should scope the vote of article with some category:

rate

author.rate resource, 3, scopeable: category_1
author.rate resource, 5, scopeable: category_2

Now resource has a rating for category_1 and another one for category_2.

rating

Recovering the rating values for resource, we have:

resource.rating
# nil

But using the scope to make the right query:

resource.rating scope: category_1
# { average: 3, estimate: 3, sum: 3, total: 1 }

resource.rating scope: category_2
# { average: 5, estimate: 5, sum: 5, total: 1 }

rated

On the same way you can find your rates with a scoped query:

author.rated scope: category_1
# { value: 3, scopeable: category_1 }

rates

The resource still have the power to consult its rates:

article.rates scope: category_1
# { value: 3, scopeable: category_1 }

article.rates scope: category_2
# { value: 3, scopeable: category_2 }

order_by_rating

To order the rating you do the same thing:

Article.order_by_rating scope: category_1

Records

Maybe you want to recover all records with or without scope, so you can add the suffix _records on relations:

category_1 = Category.first
category_2 = Category.second
author     = Author.last
resource   = Article.last

author.rate resource, 1
author.rate resource, 3, scopeable: category_1
author.rate resource, 5, scopeable: category_2

author.rating_records
# { average: 1, estimate: 1, scopeable: nil       , sum: 1, total: 1 },
# { average: 3, estimate: 3, scopeable: category_1, sum: 3, total: 1 },
# { average: 5, estimate: 5, scopeable: category_2, sum: 5, total: 1 }

author.rated_records
# { value: 1 }, { value: 3, scopeable: category_1 }, { value: 5, scopeable: category_2 }

article.rates_records
# { value: 1 }, { value: 3, scopeable: category_1 }, { value: 5, scopeable: category_2 }

As

If you have a model that will only be able to rate but not to receive a rate, configure it as author. An author model still can be rated, but won't genarate a Rating record with all values as zero to warm up the cache.

rating as: :author

Love it!

Via PayPal or Gratipay. Thanks! (: