Q:

How to define reputation system values in model

How can I set up the default_scope in my blogging application so that the index orders the entries by an algorithm defined in the model?
If I were to use a HackerNews-like formula for the ranking algorithm as shown below, how can I define it in my model?
total_score = (votes_gained - 1) / (age_in_hours + 2)^1.5

The votes_gained variable relies on the Active_Record_Reputation_System, and is written as the following in my views:
votes_gained = @post.reputation_value_for(:votes).to_i

Finally, age_in_hours is pretty straight forward
age_in_hours = (Time.now - @post.created_at)/1.hour

How can I use these figures to order my blog posts index? I've been trying to figure out how to define total_score correctly in the model so that I can add it into the default scope as default_scope order("total_score DESC") or something similar. Direct substitution has not worked, and I'm not sure of how to "rephrase" each part of the formula.
How exactly should I define total_score? Thanks much for your insight!

A:

Seeing as how you can't rely on active record to translate the formula into SQL, you have to write it yourself. The only potential concern here is that this is not a database-independent solution.
Since you are using Postgres, you can define your scope as (I haven't tested this yet, so let me know whether it works):
AGE_IN_HOURS = "(#{Time.now.tv_sec} - EXTRACT (EPOCH FROM posts.created_at))/3600"
TOTAL_SCORE = "(rs_reputations.value - 1)/((#{AGE_IN_HOURS}) + 2)^1.5"

default_scope joins("INNER JOIN rs_reputations ON rs_reputations.target_id = posts.id").where("rs_reputations.target_type = 'Post'").order(TOTAL_SCORE)

EDIT: Actually this won't work because, as it stands, Time.now is calculated one time (when the model loads), but it needs to be recalculated each time records are pulled. Use
default_scope lambda { order_by_score }

def self.order_by_score

    age_in_hours = "(#{Time.now.tv_sec} - EXTRACT (EPOCH FROM posts.created_at))/3600"
    total_score = "(rs_reputations.value - 1)/((#{age_in_hours}) + 2)^1.5"

    joins("INNER JOIN rs_reputations ON rs_reputations.target_id = posts.id").where("rs_reputations.target_type = 'Post'").order(TOTAL_SCORE)
end

