Story: alpha sort methods, no functional changes
This commit is contained in:
parent
9918660ab4
commit
1d5b004a36
|
@ -47,10 +47,6 @@ class Story < ActiveRecord::Base
|
|||
check_tags
|
||||
end
|
||||
|
||||
def to_param
|
||||
self.short_id
|
||||
end
|
||||
|
||||
def self.find_similar_by_url(url)
|
||||
urls = [ url.to_s ]
|
||||
urls2 = [ url.to_s ]
|
||||
|
@ -119,6 +115,143 @@ class Story < ActiveRecord::Base
|
|||
self.short_id = ShortId.new(self.class).generate
|
||||
end
|
||||
|
||||
def calculated_hotness
|
||||
# don't immediately kill stories at 0 by bumping up score by one
|
||||
order = Math.log([ (score + 1).abs, 1 ].max, 10)
|
||||
if score > 0
|
||||
sign = 1
|
||||
elsif score < 0
|
||||
sign = -1
|
||||
else
|
||||
sign = 0
|
||||
end
|
||||
|
||||
# TODO: as the site grows, shrink this down to 12 or so.
|
||||
window = 60 * 60 * 36
|
||||
|
||||
return -((order * sign) + (self.created_at.to_f / window)).round(7)
|
||||
end
|
||||
|
||||
def can_be_seen_by_user?(user)
|
||||
if is_gone? && !(user && (user.is_moderator? || user.id == self.user_id))
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# this has to happen just before save rather than in tags_a= because we need
|
||||
# to have a valid user_id
|
||||
def check_tags
|
||||
self.taggings.each do |t|
|
||||
if !t.tag.valid_for?(self.user)
|
||||
raise "#{self.user.username} does not have permission to use " <<
|
||||
"privileged tag #{t.tag.tag}"
|
||||
end
|
||||
end
|
||||
|
||||
if !self.taggings.reject{|t| t.marked_for_destruction? || t.tag.is_media?
|
||||
}.any?
|
||||
errors.add(:base, "Must have at least one non-media (PDF, video) " <<
|
||||
"tag. If no tags apply to your content, it probably doesn't " <<
|
||||
"belong here.")
|
||||
end
|
||||
end
|
||||
|
||||
def comments_url
|
||||
"#{short_id_url}/#{self.title_as_url}"
|
||||
end
|
||||
|
||||
def description=(desc)
|
||||
self[:description] = desc.to_s.rstrip
|
||||
self.markeddown_description = self.generated_markeddown_description
|
||||
end
|
||||
|
||||
def domain
|
||||
if self.url.blank?
|
||||
nil
|
||||
else
|
||||
pu = URI.parse(self.url)
|
||||
pu.host.gsub(/^www\d*\./, "")
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_story_cache!
|
||||
if self.url.present?
|
||||
self.story_cache = StoryCacher.get_story_text(self.url)
|
||||
end
|
||||
end
|
||||
|
||||
def fetched_content(for_remote_ip = nil)
|
||||
return @fetched_content if @fetched_content
|
||||
|
||||
begin
|
||||
s = Sponge.new
|
||||
s.timeout = 3
|
||||
@fetched_content = s.fetch(self.url, :get, nil, nil,
|
||||
{ "User-agent" => "#{Rails.application.domain} for #{for_remote_ip}" },
|
||||
3)
|
||||
rescue
|
||||
end
|
||||
|
||||
@fetched_content
|
||||
end
|
||||
|
||||
def fetched_title(for_remote_ip = nil)
|
||||
doc = Nokogiri::HTML(fetched_content(for_remote_ip).to_s)
|
||||
if doc
|
||||
return doc.at_css("title").try(:text)
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
def generated_markeddown_description
|
||||
Markdowner.to_html(self.description, { :allow_images => true })
|
||||
end
|
||||
|
||||
def give_upvote_or_downvote_and_recalculate_hotness!(upvote, downvote)
|
||||
self.upvotes += upvote.to_i
|
||||
self.downvotes += downvote.to_i
|
||||
|
||||
Story.connection.execute("UPDATE #{Story.table_name} SET " <<
|
||||
"upvotes = COALESCE(upvotes, 0) + #{upvote.to_i}, " <<
|
||||
"downvotes = COALESCE(downvotes, 0) + #{downvote.to_i}, " <<
|
||||
"hotness = '#{self.calculated_hotness}' WHERE id = #{self.id.to_i}")
|
||||
end
|
||||
|
||||
def is_editable_by_user?(user)
|
||||
if user && user.is_moderator?
|
||||
return true
|
||||
elsif user && user.id == self.user_id
|
||||
if self.is_moderated?
|
||||
return false
|
||||
else
|
||||
return (Time.now.to_i - self.created_at.to_i < (60 * MAX_EDIT_MINS))
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def is_gone?
|
||||
is_expired?
|
||||
end
|
||||
|
||||
def is_recent?
|
||||
self.created_at >= RECENT_DAYS.days.ago
|
||||
end
|
||||
|
||||
def is_undeletable_by_user?(user)
|
||||
if user && user.is_moderator?
|
||||
return true
|
||||
elsif user && user.id == self.user_id && !self.is_moderated?
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def log_moderation
|
||||
if self.new_record? || !self.editor_user_id ||
|
||||
self.editor_user_id == self.user_id
|
||||
|
@ -146,129 +279,37 @@ class Story < ActiveRecord::Base
|
|||
self.is_moderated = true
|
||||
end
|
||||
|
||||
def give_upvote_or_downvote_and_recalculate_hotness!(upvote, downvote)
|
||||
self.upvotes += upvote.to_i
|
||||
self.downvotes += downvote.to_i
|
||||
|
||||
Story.connection.execute("UPDATE #{Story.table_name} SET " <<
|
||||
"upvotes = COALESCE(upvotes, 0) + #{upvote.to_i}, " <<
|
||||
"downvotes = COALESCE(downvotes, 0) + #{downvote.to_i}, " <<
|
||||
"hotness = '#{self.calculated_hotness}' WHERE id = #{self.id.to_i}")
|
||||
def mailing_list_message_id
|
||||
"story.#{short_id}.#{created_at.to_i}@#{Rails.application.domain}"
|
||||
end
|
||||
|
||||
def mark_submitter
|
||||
Keystore.increment_value_for("user:#{self.user_id}:stories_submitted")
|
||||
end
|
||||
|
||||
# this has to happen just before save rather than in tags_a= because we need
|
||||
# to have a valid user_id
|
||||
def check_tags
|
||||
self.taggings.each do |t|
|
||||
if !t.tag.valid_for?(self.user)
|
||||
raise "#{self.user.username} does not have permission to use " <<
|
||||
"privileged tag #{t.tag.tag}"
|
||||
end
|
||||
end
|
||||
|
||||
if !self.taggings.reject{|t| t.marked_for_destruction? || t.tag.is_media?
|
||||
}.any?
|
||||
errors.add(:base, "Must have at least one non-media (PDF, video) " <<
|
||||
"tag. If no tags apply to your content, it probably doesn't " <<
|
||||
"belong here.")
|
||||
end
|
||||
end
|
||||
|
||||
def comments_url
|
||||
"#{short_id_url}/#{self.title_as_url}"
|
||||
def recalculate_hotness!
|
||||
update_column :hotness, calculated_hotness
|
||||
end
|
||||
|
||||
def short_id_url
|
||||
Rails.application.routes.url_helpers.root_url + "s/#{self.short_id}"
|
||||
end
|
||||
|
||||
def domain
|
||||
if self.url.blank?
|
||||
nil
|
||||
else
|
||||
pu = URI.parse(self.url)
|
||||
pu.host.gsub(/^www\d*\./, "")
|
||||
end
|
||||
end
|
||||
|
||||
def fetched_title(for_remote_ip = nil)
|
||||
doc = Nokogiri::HTML(fetched_content(for_remote_ip).to_s)
|
||||
if doc
|
||||
return doc.at_css("title").try(:text)
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
def fetched_content(for_remote_ip = nil)
|
||||
return @fetched_content if @fetched_content
|
||||
|
||||
begin
|
||||
s = Sponge.new
|
||||
s.timeout = 3
|
||||
@fetched_content = s.fetch(self.url, :get, nil, nil,
|
||||
{ "User-agent" => "#{Rails.application.domain} for #{for_remote_ip}" },
|
||||
3)
|
||||
rescue
|
||||
end
|
||||
|
||||
@fetched_content
|
||||
end
|
||||
|
||||
def fetch_story_cache!
|
||||
if self.url.present?
|
||||
self.story_cache = StoryCacher.get_story_text(self.url)
|
||||
end
|
||||
end
|
||||
|
||||
def calculated_hotness
|
||||
# don't immediately kill stories at 0 by bumping up score by one
|
||||
order = Math.log([ (score + 1).abs, 1 ].max, 10)
|
||||
if score > 0
|
||||
sign = 1
|
||||
elsif score < 0
|
||||
sign = -1
|
||||
else
|
||||
sign = 0
|
||||
end
|
||||
|
||||
# TODO: as the site grows, shrink this down to 12 or so.
|
||||
window = 60 * 60 * 36
|
||||
|
||||
return -((order * sign) + (self.created_at.to_f / window)).round(7)
|
||||
end
|
||||
|
||||
def score
|
||||
upvotes - downvotes
|
||||
end
|
||||
|
||||
def vote_summary
|
||||
r_counts = {}
|
||||
Vote.where(:story_id => self.id, :comment_id => nil).each do |v|
|
||||
r_counts[v.reason.to_s] ||= 0
|
||||
r_counts[v.reason.to_s] += v.vote
|
||||
def tagging_changes
|
||||
old_tags_a = self.taggings.reject{|tg| tg.new_record? }.map{|tg|
|
||||
tg.tag.tag }.join(" ")
|
||||
new_tags_a = self.taggings.reject{|tg| tg.marked_for_destruction?
|
||||
}.map{|tg| tg.tag.tag }.join(" ")
|
||||
|
||||
if old_tags_a == new_tags_a
|
||||
{}
|
||||
else
|
||||
{ "tags" => [ old_tags_a, new_tags_a ] }
|
||||
end
|
||||
|
||||
r_counts.keys.sort.map{|k|
|
||||
k == "" ? "+#{r_counts[k]}" : "#{r_counts[k]} #{Vote::STORY_REASONS[k]}"
|
||||
}.join(", ")
|
||||
end
|
||||
|
||||
def generated_markeddown_description
|
||||
Markdowner.to_html(self.description, { :allow_images => true })
|
||||
end
|
||||
|
||||
def description=(desc)
|
||||
self[:description] = desc.to_s.rstrip
|
||||
self.markeddown_description = self.generated_markeddown_description
|
||||
end
|
||||
|
||||
def mailing_list_message_id
|
||||
"story.#{short_id}.#{created_at.to_i}@#{Rails.application.domain}"
|
||||
end
|
||||
|
||||
@_tags_a = []
|
||||
|
@ -296,19 +337,6 @@ class Story < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def tagging_changes
|
||||
old_tags_a = self.taggings.reject{|tg| tg.new_record? }.map{|tg|
|
||||
tg.tag.tag }.join(" ")
|
||||
new_tags_a = self.taggings.reject{|tg| tg.marked_for_destruction?
|
||||
}.map{|tg| tg.tag.tag }.join(" ")
|
||||
|
||||
if old_tags_a == new_tags_a
|
||||
{}
|
||||
else
|
||||
{ "tags" => [ old_tags_a, new_tags_a ] }
|
||||
end
|
||||
end
|
||||
|
||||
def title=(t)
|
||||
# change unicode whitespace characters into real spaces
|
||||
self[:title] = t.strip
|
||||
|
@ -322,6 +350,17 @@ class Story < ActiveRecord::Base
|
|||
u.gsub(/^_+/, "").gsub(/_+$/, "")
|
||||
end
|
||||
|
||||
def to_param
|
||||
self.short_id
|
||||
end
|
||||
|
||||
def update_comments_count!
|
||||
comments = self.comments.arrange_for_user(nil)
|
||||
|
||||
# calculate count after removing deleted comments and threads
|
||||
self.update_column :comments_count, comments.count{|c| !c.is_gone? }
|
||||
end
|
||||
|
||||
def url=(u)
|
||||
# strip out stupid google analytics parameters
|
||||
if u && (m = u.match(/\A([^\?]+)\?(.+)\z/))
|
||||
|
@ -335,10 +374,6 @@ class Story < ActiveRecord::Base
|
|||
self[:url] = u.to_s.strip
|
||||
end
|
||||
|
||||
def url_or_comments_url
|
||||
self.url.blank? ? self.comments_url : self.url
|
||||
end
|
||||
|
||||
def url_is_editable_by_user?(user)
|
||||
if self.new_record?
|
||||
true
|
||||
|
@ -349,54 +384,19 @@ class Story < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def is_editable_by_user?(user)
|
||||
if user && user.is_moderator?
|
||||
return true
|
||||
elsif user && user.id == self.user_id
|
||||
if self.is_moderated?
|
||||
return false
|
||||
else
|
||||
return (Time.now.to_i - self.created_at.to_i < (60 * MAX_EDIT_MINS))
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
def url_or_comments_url
|
||||
self.url.blank? ? self.comments_url : self.url
|
||||
end
|
||||
|
||||
def is_undeletable_by_user?(user)
|
||||
if user && user.is_moderator?
|
||||
return true
|
||||
elsif user && user.id == self.user_id && !self.is_moderated?
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def can_be_seen_by_user?(user)
|
||||
if is_gone? && !(user && (user.is_moderator? || user.id == self.user_id))
|
||||
return false
|
||||
def vote_summary
|
||||
r_counts = {}
|
||||
Vote.where(:story_id => self.id, :comment_id => nil).each do |v|
|
||||
r_counts[v.reason.to_s] ||= 0
|
||||
r_counts[v.reason.to_s] += v.vote
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def is_gone?
|
||||
is_expired?
|
||||
end
|
||||
|
||||
def is_recent?
|
||||
self.created_at >= RECENT_DAYS.days.ago
|
||||
end
|
||||
|
||||
def recalculate_hotness!
|
||||
update_column :hotness, calculated_hotness
|
||||
end
|
||||
|
||||
def update_comments_count!
|
||||
comments = self.comments.arrange_for_user(nil)
|
||||
|
||||
# calculate count after removing deleted comments and threads
|
||||
self.update_column :comments_count, comments.count{|c| !c.is_gone? }
|
||||
r_counts.keys.sort.map{|k|
|
||||
k == "" ? "+#{r_counts[k]}" : "#{r_counts[k]} #{Vote::STORY_REASONS[k]}"
|
||||
}.join(", ")
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue