Ruby
Article

Increase Engagement with Merit and Gamification

By Manu Ajith

Gamification or reputation systems have become quite popular with web and mobile apps. Keeping score of users’ activity helps websites find out who should be rewarded and encourages users to contribute. Websites like StackOverflow and Foursquare are two of the common applications that owe much of their success to gamification. These reputation systems have been in use on forums and communities for a long time and work well in building a quality user engagements.

There are a couple of gems that can help seamlessly build a gamified experience for users. One of the gems that I feel is quite useful, customizable, and well maintained is merit.

The simple rule behind gamification is that when a user performs an action or achieves a quantifiable goal, there is a reward. For example:
* When a user comments 10 they are given a Junior Critic Badge
* When the user posts 5 articles they are given the Junior Contributor Badge

Setup Merit

To get started with Merit, add the following code to your gem file.

gem 'merit'

run

bundle install

Once the bundle is complete, add the initializer and migration. The initializer mentions the name of the badges we want use. We can also define which datastore we plan to use (PostgreSQL or mongoDB). In the migration, the model to be “gamified” is specified. For example, we are going to track user activity, so we need to add the migration to the User model:

bundle exec rails g merit:install
=> Creates the initializer in /config/initializer/merit.rb

bundle exec rails g merit model_name (eg: user)
=>  would add has_merit to the user model

Now migrate the database:

bundle exec rake db:migrate

Another folder to be aware of is the app/models/merit/*. This directory holds the definitions of the rules for the badges given to users. The ability to write the rules in a plain Ruby file is one the major advantages of using Merit. Using a file based approach makes it highly customizable.

Let’s create our first badge.

The badges we plan to use need to be written in the initializer file config/initializers/merit.rb. The method to create a badge is Merit::Badge.create!. It accepts the following parameters:

  • :id integer (required)
  • :name this is how you reference the badge (required)
  • :level (optional)
  • :description (optional)
  • :custom_fields hash of anything else you want associated with the badge (optional)

Example:

Merit::Badge.create!(
  id: 1,
  name: "Jr.Critic",
  description: "Over 10 comments"
)
Merit::Badge.create!(
  id: 2,
  name: "Sr.Critic",
  description: "Over 50 comments"
)

Once the badge is created, it’s time to define the rules to award this badge to the user. As mentioned before, the rules to award the badge are defined in the app/models/merit/* folder. We need to create a file callled badge_rules.rb. Note that we can provide a temporary or permanent badge. A temporary badge is revoked when its rules are no longer met.

The rules are defined by calling the grant_on method and providing a block. The parameters that for this method are:

  • 'controller#action' – The name of the controller and action that triggers the block
  • :badge – The :name of the badge
  • :level – The :level of the badge
  • :to – The name of object that holds the badge receiver. It needs a variable named @model in the associated controller action, like @post for posts_controller.rb or @comment for comments_controller.rb
    • Can be a method name, which called over the target object should retrieve the object to badge. If it’s :user for example, merit will internally call @model.user to find who to badge
    • Can be :itself, in which case it badges the target object itself (@model)
    • Is :action_user by default, which means current_user
  • :model_name – Defines the controller’s name if it’s different from the model’s (e.g. RegistrationsController for the User model).
  • :multiple – Whether or not the badge may be granted multiple times. false by default.
  • :temporary – Whether or not the badge should be revoked if the condition no longer holds. false -badges are kept for ever- by default.
  • &block – Can be one of the following:
    • Empty / not included: always grant the badge
    • A block which evaluates to boolean. It receives the target object as a parameter (e.g. @post if you’re working with a PostsController action).
    • A block with a hash composed of methods to run on the target object and expected method return values

Example:

# app/models/merit/badge_rules.rb
grant_on 'comments#create',  badge: 'Jr.Critic', to: :user do |comment|
  comment.user.comments.count >= 10 &&  comment.user.comments.count < 50
end
grant_on 'comments#create',  badge: 'Sr.Critic', to: :user do |comment|
  comment.user.comments.count >= 50
end

It’s clear from the above code that the Junior Critic badge will last only as long as the user’s comments count is greater than 10 and less than 50. When his comment count goes to more than 50, a new badge will be issued and The Junior Badge will be removed.

To access the badges of the current user (assuming that your logged in user is known as current_user):

current_user.badges

If you want add/remove badges manually, use:

current_user.add_badge(badge.id)
current_user.rm_badge(badge.id)

An example use case for the above situation is when you want to give a ‘premium user’ badge for paying customers. Instead of writing a rule and have it checked many times, just add it from the payment controller and action.

Point System

From here, we can build a cool reputation system, but t won’t be complete without displaying points. The calculation of points is similar to the badge system. Define the point rules in a file called (you guessed it) app/models/merit/point_rules.rb.

Points are configured using a method called score that accepts the same parameters as the badge system.

For example, if you require the user’s points to increase by one when he comments on an article:

score 1, on: 'comments#create', to: [:user]
 score 5, on: 'comments#vote_up', to: [:comment_owner]

:user is the the attribute of the model (comment) that points to the user. In the case of vote_up, the owner of the comment was referenced by an attribute in the Vote model as comment_owner.

You can manually add points, just like badges:

current_user.add_points(20, category: 'Optional category')
current_user.subtract_points(10, category: 'Optional category')

To find the points awarded since a given date say, for a leaderboard, use:

score_points = current_user.score_points(category: 'Optional category')
score_points.where("created_at > '#{1.month.ago}'").sum(:num_points)

Ranking system

Similar to both points and badges, ranking is defined in app/models/merit/rank_rules.rb.

Ranking rules are created with the set_rank method. The parameters are as follows:

  • :level– Ranking level (greater is better, Lexicographical order)
  • :to – Model or scope to check if new rankings apply
  • :level_name – Attribute name (default is empty and results in ‘level’ attribute, if set it’s appended like ‘level_#{level_name}’)

Example:

set_rank level: 2, to: User.active do |user|
  user.comments > 10 && user.followers >= 10
end

This is more functionality available with Merit. Using something like ActiveRecord Observer, it is simple to track the badges/points being awarded and create the required notification for users. Try out the gem and bring gamification and, thus, more engagement to your web application.

  • http://bottico.tumblr.com bottico

    very nice

  • http://www.francesyun.com Frances Yun

    thanks for explaining this so clearly!

  • http://ibelmopan2.blogspot.com/ iBelmopan Admin

    Great share!

  • http://ibelmopan2.blogspot.com/ iBelmopan Admin

    Nice example. When using the custom fields it gave me a little problem. Once you understand how hashes work in ruby it should be no problem. Thanks again!

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Ruby, once a week, for free.