Rankeable allows you to easily add rankings to your Rails applications
Ruby JavaScript
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



Build Status

Rankeable is a ranking system for Rails applications

Rankeable allows you to easily add rankings to your models in a Rails application. It's geared towards rankings that are too expensive to calculate on the request cycle, and must be calculated periodically in the background.

Any object can be ranked, and any number of different rankings can be created, with different ranking rules.


You can view the Survey documentation in RDoc format here:


Main Features:

  • A flexible model allowing any object to be ranked
  • Multiple rankings and ranking rules
  • Easy integration with delayed_job, sidekiq and resque for background calculation


Add Rankeable to your Gemfile:

gem 'rankeable', :git => 'git://github.com/runtimerevolution/rankeable.git'

Then run bundle to install the Gem:

bundle install

Now generate and run migrations:

rails generate survey:install
bundle exec rake db:migrate

After installation Rankeable generates a RankingsRules class inside of ranking_concerns folder (you can change the location of RankingRules class. The only constraint is the existence of RankingRules in your application).

Getting Started

For example, using the context of a Game that have many players and referees.

class Game < ActiveRecord::Base
    has_many :players
    has_many :referees

    # ...

class Player < ActiveRecord::Base

    # ...

class Referee < ActiveRecord::Base
    # ...

How Ranking works

Ranking has some important components:

  • rankeable: the owner of the rankings (in our example it's Game). Contextualizes the ranking
  • ranked_type: The ranked model
  • ranked_call: The calculation function that actually ranks objects

Creating rankings

Add a ranking and ranking rule that ranks players by number of goals scored:

# creating ranking with rule "number_of_goals"
@game.rankings.create(:name => "goals_by_player",
    :ranked_type => "Player", :ranked_call => "number_of_goals")

# in file app/ranking_concerns/ranking_rules.rb

# Rule Number of Goals
def number_of_goals(ranking, *args)
    ranking.rankeable.players.order("goals DESC").map do |player|
        OpenStruct.new(:value => player.goals,
            :ranked_object => player, :label => "#{player.name} - #{player.number}")

Add a ranking and ranking rule that ranks players by the number of faults:

# creating ranking with rule "number_of_faults"
@game.rankings.create(:name => "faults_by_refeere",
    :ranked_type => "Refeere", :ranked_call => "number_of_faults")

# in file app/ranking_concerns/ranking_rules.rb

# Rule Number of Faults
def number_of_faults(ranking, *args)
    ranking.rankeable.refeeres.map { |refeere|
        total_faults = refeere.red_cards + refeere.yellow_cards
        OpenStruct.new(:value => total_faults,
            :ranked_object => player, :label => refeere.name)
    }.sort {|a,b| b.value <=> a.value }

Ranking Rules Protocol

Every Ranking Rule must return a collection with objects that respond to:

  • label => a label describing the object ranked
  • ranked_object => The target object of the ranking
  • value => The value calculated on Ranking rule (something that can be converted to a float)

See Rankeable Working

# find our number_of_goals strategy for this game
ranking = Game.first.rankings.find_by_name("goals_by_player")

#calculate our rankings

# show the results
=> [#<RankingValue position=1, value=2, ranked_object=#<Player id: 13, name: "Chuck Norris", goals: 2, created_at: "2013-01-31 14:48:54", updated_at: "2013-01-31 14:48:54">>,
#<RankingValue position=2, value=1, ranked_object=#<User id: 8, name: "Bob", goals: 1, created_at: "2013-01-31 14:48:54", updated_at: "2013-01-31 14:48:54">>]

# What's the position of a particular player in the ranking ?
ranking.ranked_item_position(Player.find_by_name('Chuck Norris'))
=> 25

Help your migrations

Rankeable help us with migrations.

To create a model with rankings you can use has_rankings option, but if you want a model who is the target of your ranking use is_rankeable option instead. This will create the model (if one doesn't exist) and configure it with default Rankeable Modules.

# creating Game Model
rails g has_rankings game name:string
# creating Player Model
rails g is_rankeable player name:string number:integer goals:integer
# creating Referee Model
rails g is_rankeable referee name:string number_of_faults:integer yellow_cards:integer red_cards:integer

This is the scafold generated by the Rankeable migration helper. Next, you must run the migrate command in order to the changes take the effect.

Integration with delayed_job and others

Usually rankings need to be calculated in the background, using something like delayed_job, sidekiq or resque.

An example using delayed_job:

rank_scores_for_refeeres = @game.rankings.where(:ranked_call => "number_of_goals",
    :ranked_type => "Refeere").first


Copyright © 2013 Runtime Revolution, released under the MIT license.

githalytics.com alpha