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.
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 :
Of course, you'll also want to make various combinations of those. Non-obvious but frequently asked features are :
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 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 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 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.
activerecord-reputation-system by twitter folks if you need a comprehensive solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With