views:

427

answers:

3

How would you design a content voting mechanism that could be applied polymorphically to multiple models / classes. (in a ruby on rails context preferably, but others are fine)

Given that instances of these classes can be voted on: - Article - Question - Product

Voters should not be required to register.

Best effort should be made to limit voters to one vote per object. (1 vote for a particular article and one vote for a particular question, etc.). I.e. use ip detection, cookies, etc.

+2  A: 

I'd suggest starting with Single Table Inheritance for a 'votable' interface and derive any votable classes from there. Starting details for STI: http://wiki.rubyonrails.org/rails/pages/singletableinheritance

workmad3
Not the best idea.
Ian Terrell
+1  A: 

You said polymorphically -- that's the key word.

Look at how ActsAsTaggable works. You can create an ActsAsVotable "plugin" that will provide voting behavior to specific models. Votes will be stored in another model (Vote?) that will have a polymorphic belongs_to.

create_table :orders do |t|
  t.string :votable_type
  t.integer :votable_id
  t.integer :vote
  t.timestamps
end

Your acts_as_votable call will behind the scenes associate it with its votes:

has_many :votes, :as => "votable"

Again, see ActsAsTaggable. :)

Ian Terrell
A: 

Thanks for the answers guys. Yup, creating a polymorphic votable model is the way to go, however I was attempting to extract a more complete answer.

For example here's what I'm thinking so far. A voteable class which can be tied to multiple classes (Article, Question, Product) in this scenario.

Votes table: id:integer, vote:boolean, voteable_type:string (name of the class being voted on), voteable_id:integer (id of the class being voted on), voter_id (identifer for the user voting).

Now we still need to try and limit votes to one per user without requiring some sort of registration. So the voter_id could be a composite containing the user's IP address and user_agent. This is where I'm open to ideas. The IP address captured is not necessarily unique if they're coming from a corp proxy etc. so combining the user agent shoud help somewhat. However you still have cases where two user's can have the same IP and user agent string. Also, you may not always be able to obtain this information if their browser doesn't send it in the request header, meaning that some user's simply won't get to vote... Also, IPs can be spoofed.

Another possiblity would be using cookies, but I think this invites abuse from user's who want to spike the votes for a particular piece of content by clearing their cookies or scripting hits. Checking the user-agent here should help too, but you can fake a user-agent via script easily enough.

There probably is no perfect solution, but I'm curious how other's have approached this. I.e. hacker news and even stackoverflow.

Dave Rapin
How about using cookies, with a CAPTCHA (to at least stop scripted voting) plus a time limit for multiple votes on that IP (rather than 1 vote per IP). This means people could still vote multiple times (by removing their cookies), but they would need to this manually (i.e. not scripted) and could only make a vote every X minutes. This should deter most people (I think)
JonoW