Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modern tools for Ruby/Rails for building an achievement system

Tags:

I am interested in building a robust achievement system for my Ruby on Rails application.

The achievements can happen in a myriad of ways. Normally, we'd have some criteria that can happen to several models, some required, some optional (mix and match) that triggers an achievement.

I would say this is similar to the achievement system on here or Foursquare badges. Ideally if there is a modern tool out there that can help abstract a lot of the work in some DSL, that would be fantastic.

I'm interested in seeing how others solved this issue using Ruby or Ruby on Rails. Building this from scratch sounds unreasonable as I'm sure it's been solved many times repeatedly.

like image 912
randombits Avatar asked Mar 07 '12 19:03

randombits


2 Answers

An achievement system seems simple at first glance but can quickly become quite complex.

First, you have to identify what kind of achievements you want to provide. You can award :

  1. Badges
  2. Points
  3. Ranks

Of course, you'll also want to make various combinations of those. Non-obvious but frequently asked features are :

  • the ability to know its progress towards a specific rank or badge.
  • the ability to hide some badges

In RoR world, I have found 3 third-party libraries freely available. As often, there's no magic bullet and you have to choose one according to your needs.

Badgeable

Badgeable is a simple DSL which implements only a badge system. It's dynamic and simple to understand. This example is from the official documentation :

badge "Fancy Pants" do   thing Meal   subject :person   count     Meal.where(:price_cents.gte => 10000).count >= 12   end   conditions do |meal|     meal.restaurant.city != meal.eater.city   end end 

It would award the Fancy Pants badge to the diner who has eaten 12 expensive meals where the awarding meal was out of town. It includes interesting features like unseen badge, but cannot award the same badge multiples times. By default, Badgeable add hooks after creation of the observed record. In the above example, badge condition is executed after each Meal creation.

It supports both ActiveRecord and Mongoid.

Paths of Glory

Paths of Glory is quite different of Badgeable. This gem is more towards Points and Ranks. It separates logic to compute badges (observer) from logic to describe badges (Achivement class). Maybe it would be more natural for you if you already use Observer pattern. Take note that it's pure Ruby, there's no DSL in Paths of Glory.

In the Achievement class, you describe your levels, what you count and how to award achievements :

level 1, :quota => 2 level 2, :quota => 4 level 3, :quota => 6  set_thing_to_check { |user| user.posts.count }  def self.award_achievements_for(user)   return unless user   return if user.has_achievement?(self)   user.award_achievement(self) end 

Observer part is very classic :

  observe :post    def after_save(post)     Teacher.award_achievements_for(post.user) unless post.new_record?   end 

It's not well documented but you can find a sample application using it here.

It includes helpers in order to follow progress to the next rank. Since it uses classic rails features, it should be compatible with every rails ORM available.

Merit

Merit seems to be the more complete gem about this subject, ATM. It allows to define badges, points and rules with a DSL.

For badges, it looks like :

grant_on ['users#create', 'users#update'], :badge => 'autobiographer', :temporary => true do |user|   user.name.present? && user.address.present? end 

It will check on both creation and update if user has put its address. It will remove the badge if user removes its address.

For points, it's capable to count a score based on multiple models :

score 20, :on => [ 'comments#create', 'photos#create' ] 

For ranks, it's quite similar to badges. Difference is mainly in the level :

set_rank :stars, :level => 2, :to => Commiter.active do |commiter|   commiter.branches > 1 && commiter.followers >= 10 end  set_rank :stars, :level => 3, :to => Commiter.active do |commiter|   commiter.branches > 2 && commiter.followers >= 20 end 

This gem also provides means to compute badges or ranks in cron jobs and not after each writes on objects :

task :cron => :environment do   MeritRankRules.new.check_rank_rules end 

Under the hood, Merit uses Ambry to store badges information. It should help to both reduces it's noise on your datastore and make it a little faster. There's an experimental support available for MongoMapper. I haven't found any means to have unseen badges or to follow progress towards a badge.

like image 181
Coren Avatar answered Oct 01 '22 05:10

Coren


activerecord-reputation-system by twitter folks if you need a comprehensive solution.

like image 35
ecleel Avatar answered Oct 01 '22 04:10

ecleel