twitter / activerecord-reputation-system

An Active Record Reputation System for Rails
Apache License 2.0
1.33k stars 122 forks source link

STI pulls wrong target_id for non-primary reputation #63

Closed creativetim closed 11 years ago

creativetim commented 11 years ago

This is also a SO post: http://stackoverflow.com/questions/18246904/twitter-activerecord-reputation-counting-primary-reputations-as-non-primary-rep

I have two models: Voters and Polls.

A Voter can vote on a Poll in two ways: 1) up/down vote 2) power vote

I have up/down voting working, but I want to be able to know how many power votes a user has accumulated in aggregate as they can only have a certain amount.

poll.rb

class Poll < ActiveRecord::Base
  belongs_to :voter

  has_reputation :votes,
                 source: :voter

  has_reputation :powervotes,
             source: :voter,
             source_of: [{reputation: :voter_powervotes, of: :voter}]

  has_reputation :score,
                 source: [
                   { reputation: :votes },
                   { reputation: :powervotes, weight: 2 }]

  def upvote(voter)
    self.add_or_update_evaluation(:votes, 1, voter)
  end

  def downvote(voter)
    self.add_or_update_evaluation(:votes, -1, voter)
  end

  def powervote(voter)
    self.add_or_update_evaluation(:powervotes, 1, voter)
  end

  def score
    self.reputation_for(:score).to_i
  end
end

voter.rb

class Voter < ActiveRecord::Base
  has_many :polls

  has_reputation :voter_powervotes,
             source: [reputation: :powervotes, of: :polls]

  def powervotes
    self.reputation_for(:powervotes).to_i
  end
end

When I powervote() on a poll as a voter and then call Voter.powervotes it returns 0. I think this is because of STI.

Assuming Voter ID 1 and Poll ID 1, when I record a powervote as a user on a poll, using this setup, 3 reputations are created in my rs_reputations table.

powervotes voter_powervotes score

Everything seems fine, until you dig a little deeper: voter_powervotes' target_id seems to be pulling the wrong ID. In this case it's pulling 4 - it should be 1. I think it's doing this because Poll ID 1 has a column called voter_id and its value is 4. I don't care about who the Poll belongs to I just care about each user's powervote count.

Is there a better way to setup the associations between models or have I found a bug?

kn commented 11 years ago

With your reputation definition, right behavior is:

voter1 = Voter.create
voter2 = Voter.create
poll = voter1.polls.create
poll.powervote(voter2)

voter1.powervotes
=> 1
voter2.powervotes
=> 0

However, I think you are trying to make:

voter2.powervotes
=> 1

Reputation chain can be done only through model association.

One work around you can do is:

Poll.evaluated_by(:powervotes, voter2)
=> [<Poll ...>]
Poll.evaluated_by(:powervotes, voter2).count
=> 1
creativetim commented 11 years ago

That works.

So a :voter_powervotes reputation on the Voter model is unnecessary in this case since all I need to do is count how many powervotes have been cast on behalf of a user.

kn commented 11 years ago

Glad it works. Please close this issue :)