journalduhacker/app/models/story_repository.rb

132 lines
3.5 KiB
Ruby

class StoryRepository
# how many days old a story can be to get on the bottom half of /recent
RECENT_DAYS_OLD = 3
# how many points a story has to have to probably get on the front page
HOT_STORY_POINTS = 5
def initialize(user, params = {})
@user = user
@params = params
end
def hottest
hottest = positive_ranked base_scope
hottest = filter_downvoted_and_tags hottest
hottest.order('hotness')
end
def hidden
hidden = base_scope
hidden = hidden.where(Story.arel_table[:id].in(hidden_arel)) if @user
hidden = filter_tags hidden, @params[:exclude_tags] if @params[:exclude_tags].try(:any?)
hidden.order('hotness')
end
def newest
newest = filter_downvoted_and_tags base_scope
newest.order('stories.created_at DESC')
end
def by_user(user)
base_scope.where(user_id: user.id)
end
def recent
stories = newest
# try to help recently-submitted stories that didn't gain traction
story_ids = []
10.times do |x|
# grab the list of stories from the past n days, shifting out popular
# stories that did gain traction
story_ids = stories.select(:id, :upvotes, :downvotes, :user_id).
where(Story.arel_table[:created_at].gt((RECENT_DAYS_OLD + x).days.ago)).
order("stories.created_at DESC").
reject{|s| s.score > HOT_STORY_POINTS }
if story_ids.length > StoriesPaginator::STORIES_PER_PAGE + 1
# keep the top half (newest stories)
keep_ids = story_ids[0 .. ((StoriesPaginator::STORIES_PER_PAGE + 1) * 0.5)]
story_ids = story_ids[keep_ids.length - 1 ... story_ids.length]
# make the bottom half a random selection of older stories
while keep_ids.length <= StoriesPaginator::STORIES_PER_PAGE + 1
story_ids.shuffle!
keep_ids.push story_ids.shift
end
stories = Story.where(:id => keep_ids)
break
end
end
stories.order('stories.created_at DESC')
end
def tagged(tag)
tagged = positive_ranked base_scope
tagged = tagged.where(
Story.arel_table[:id].in(
Tagging.arel_table.where(
Tagging.arel_table[:tag_id].eq(tag.id)
).project(
Tagging.arel_table[:story_id]
)
)
)
tagged.order('stories.created_at DESC')
end
def top(length)
top = base_scope.where("created_at >= (NOW() - INTERVAL #{length[:dur]} #{length[:intv].upcase})")
top.order '(CAST(upvotes AS integer) - CAST(downvotes AS integer)) DESC'
end
private
def base_scope
Story.unmerged.where(is_expired: false)
end
def filter_downvoted_and_tags(scope)
scope = filter_downvoted scope if @user
scope = filter_tags scope, @params[:exclude_tags] if @params[:exclude_tags].try(:any?)
scope
end
def filter_downvoted(scope)
scope.where(Story.arel_table[:id].not_in(hidden_arel))
end
def hidden_arel
if @user
hidden_arel = Vote.arel_table.where(
Vote.arel_table[:user_id].eq(@user.id)
).where(
Vote.arel_table[:vote].lteq(0)
).where(
Vote.arel_table[:comment_id].eq(nil)
).project(
Vote.arel_table[:story_id]
)
end
end
def positive_ranked(scope)
scope.where("(CAST(upvotes AS integer) - CAST(downvotes AS integer)) >= -2")
end
def filter_tags(scope, tags)
scope.where(
Story.arel_table[:id].not_in(
Tagging.arel_table.where(
Tagging.arel_table[:tag_id].in(tags)
).project(
Tagging.arel_table[:story_id]
)
)
)
end
end