#364 Active Record Reputation System
Jul 09, 2012 | 10 minutes | Active Record, Plugins
If you need to calculate an average user's rating or sum up a number of votes, consider using the activerecord-reputation-system gem. Here I will cover the basics and also briefly present a from-scratch solution.
- Download:
- source codeProject Files in Zip (112 KB)
- mp4Full Size H.264 Video (24.5 MB)
- m4vSmaller H.264 Video (12 MB)
- webmFull Size VP8 Video (14.3 MB)
- ogvFull Size Theora Video (27.4 MB)
Resources
terminal
rails g reputation_system rake db:migrate
Gemfile
gem 'activerecord-reputation-system', require: 'reputation_system'
models/haiku.rb
has_reputation :votes, source: :user, aggregated_by: :sum
models/user.rb
has_many :evaluations, class_name: "RSEvaluation", as: :source has_reputation :votes, source: {reputation: :votes, of: :haikus}, aggregated_by: :sum def voted_for?(haiku) evaluations.where(target_type: haiku.class, target_id: haiku.id).present? end
config/routes.rb
resources :haikus do member { post :vote } end
haikus_controller.rb
def index @haikus = Haiku.find_with_reputation(:votes, :all, order: "votes desc") end def vote value = params[:type] == "up" ? 1 : -1 @haiku = Haiku.find(params[:id]) @haiku.add_or_update_evaluation(:votes, value, current_user) redirect_to :back, notice: "Thank you for voting" end
_haiku.html.erb
| <%= pluralize haiku.reputation_value_for(:votes).to_i, "vote" %> <% if current_user && !current_user.voted_for?(haiku) %> | <%= link_to "up", vote_haiku_path(haiku, type: "up"), method: "post" %> | <%= link_to "down", vote_haiku_path(haiku, type: "down"), method: "post" %> <% end %>
application.html.erb
<%= current_user.reputation_value_for(:votes).to_i %>
From-Scratch Solution
models/haiku_vote.rb
validates_uniqueness_of :haiku_id, scope: :user_id validates_inclusion_of :value, in: [1, -1] validate :ensure_not_author def ensure_not_author errors.add :user_id, "is the author of the haiku" if haiku.user_id == user_id end
models/haiku.rb
def self.by_votes select('haikus.*, coalesce(value, 0) as votes'). joins('left join haiku_votes on haiku_id=haikus.id'). order('votes desc') end def votes read_attribute(:votes) || haiku_votes.sum(:value) end
models/user.rb
def total_votes HaikuVote.joins(:haiku).where(haikus: {user_id: self.id}).sum('value') end def can_vote_for?(haiku) haiku_votes.build(value: 1, haiku: haiku).valid? end
haikus_controller.rb
def index @haikus = Haiku.by_votes end def vote vote = current_user.haiku_votes.new(value: params[:value], haiku_id: params[:id]) if vote.save redirect_to :back, notice: "Thank you for voting." else redirect_to :back, alert: "Unable to vote, perhaps you already did." end end
_haiku.html.erb
| <%= pluralize haiku.votes, "vote" %> <% if current_user && current_user.can_vote_for?(haiku) %> | <%= link_to "up", vote_haiku_path(haiku, value: 1), method: "post" %> | <%= link_to "down", vote_haiku_path(haiku, value: -1), method: "post" %> <% end %>
application.html.erb
<%= current_user.total_votes %>