Skip to content
This repository has been archived by the owner on Nov 13, 2021. It is now read-only.

Build Hacker News Like Reputation System

kn edited this page Oct 8, 2012 · 2 revisions

Let's define Hacker News like Reputation System using ActiveRecord Reputation System gem.

At Hacker News, user can post news and make comments about them. Let's define user, post and comment classes:

class User < ActiveRecord::Base
  has_many :posts
  has_many :comments
end

class Post < ActiveRecord::Base
  belong_to :user
end

class Comment < ActiveRecord::Base
  belong_to :user
end

Users can give points to comments and points. Let's define a reputation for posts and comments:

class Post < ActiveRecord::Base
  belong_to :user

  has_reputation :post_points,
    :source => :user
end

class Comment < ActiveRecord::Base
  belong_to :user

  has_reputation :comment_points,
    :source => :user
end

Here I ommitted :aggregated_by option because it's :sum by default and that's what we want here. Now you can give points to posts and comments as follow:

@post.add_evaluation(:post_points, 1, @user)
@comment.add_evaluation(:comment_points, 1, @user)

For the top news page, you can order posts by points:

@posts = Post.find_with_reputation(:post_points, :all, { :order => 'post_points DESC' })

In a view, you can do something like:

<% @posts.each do |post| %>
  <%= post.title %> has <%= post.post_points %> points.
<% end %>

Hacker News also keep track of user karma. Let's define user karma as a sum of the user's post points and comment points.

class User < ActiveRecord::Base
  has_many :posts
  has_many :comments

  has_reputation :karma
    :source => [
      { :reputation => :total_post_points },
      { :reputation => :total_comment_points }
    ]

  has_reputation :total_post_points,
    :source => { :reputation => :post_points, :of => :posts }

  has_reputation :total_comment_points
    :source => { :reputation => :comment_points, :of => :comments }
end

Here I omitted :source_of for :total_post_point and :total_comment_points because the gem can detect it if it's within the same class. But you need to define it for :post_points and :comment_points like below because they are now source of :total_post_points and :total_comment_points.

class Post < ActiveRecord::Base
  belong_to :user

  has_reputation :post_points,
    :source => :user,
    :source_of => { :reputation => :total_post_points, :of => :user }
end

class Comment < ActiveRecord::Base
  belong_to :user

  has_reputation :comment_points,
    :source => :user,
    :source_of => { :reputation => :total_comment_points, :of => :user }
end

Now you can access user karma as follow:

  @user.reputation_for(:karma)

You might want to get all posts for which the user gave a point. You can do this by:

@evaluated_posts = Post.evaluated_by(:post_points, @user)

Also, you can get all users who gave a point for a given post:

@post.evaluators_for(:post_points)

Now we have Hacker News like a Reputation System!

As you can see, the relationship among reputations is defined in very readable way. You can easily understand how each reputations related to each other by just looking at the has_reputation definition in models. Also all reputation value propagations are handled by gem which prevents you from writing a code related to the reputation system in various places.

Ready to build your own reputation system? For more infomation, please explore the ActiveRecord Reputation System Wiki.