Go to file
Washington Botelho 038a0d20cb
spec: fix missing metadata attribute
2018-01-26 19:32:45 -02:00
lib feat: add ability to persist metadata 2018-01-26 19:16:56 -02:00
spec spec: fix missing metadata attribute 2018-01-26 19:32:45 -02:00
.gitignore first commit 2017-10-30 22:30:09 -02:00
.rubocop.yml first commit 2017-10-30 22:30:09 -02:00
.rubocop_todo.yml first commit 2017-10-30 22:30:09 -02:00
.ruby-gemset first commit 2017-10-30 22:30:09 -02:00
.ruby-version bump ruby to 2.4.3 2018-01-14 22:28:34 -02:00
.travis.yml first commit 2017-10-30 22:30:09 -02:00
CHANGELOG.md v0.2.0 2017-11-03 00:08:39 -02:00
Gemfile first commit 2017-10-30 22:30:09 -02:00
Gemfile.lock gem: update 2018-01-26 19:20:42 -02:00
LICENSE first commit 2017-10-30 22:30:09 -02:00
README.md doc: add example of metadata feature 2018-01-26 17:46:25 -02:00
Rakefile first commit 2017-10-30 22:30:09 -02:00
rating.gemspec up: precise dep declaration of activerecord 2018-01-22 02:31:22 -02:00

README.md

Rating

Build Status Gem Version Maintainability Support

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

Metadata

Maybe you want include a comment together your rating or even a fingerprint field to make your rating more secure. So, first you will need to add more fields to the Rating::Rate table:

class AddCommentAndFingerprintOnRatingRates < ActiveRecord::Migration
  def change
    add_column :rating_rates, :comment, :text

    add_reference :rating_rates, :fingerprint, foreign_key: true, index: true, null: false
  end
end

add_column :table_name, :column_name, :decimal, default: 0, precision: 15, scale: 10

As you can seed, we can add any kind of field we want. Now we just provide this values when we make the rate:

author      = Author.last
resource    = Article.last
comment     = 'This is a very nice rating. s2'
fingerprint = Fingerprint.new(ip: '127.0.0.1')

author.rate resource, 3, metadata: { comment: comment, fingerprint: fingerprint }

Now you can have this data into your model normally:

author = Author.last
rate   = author.rates.last

rate.comment     # 'This is a very nice rating. s2'
rate.fingerprint # <Fingerprint id:...>
rate.value       # 3

Love it!

Via PayPal or Support. Thanks! (: