journalduhacker/app/models/comment.rb

170 lines
4 KiB
Ruby
Raw Normal View History

class Comment < ActiveRecord::Base
2012-07-01 00:43:45 +02:00
belongs_to :user
belongs_to :story
has_many :votes,
:dependent => :delete_all
2012-07-03 18:59:50 +02:00
belongs_to :parent_comment,
:class_name => "Comment"
attr_accessible :comment
attr_accessor :parent_comment_short_id, :current_vote, :previewing,
2012-07-03 19:12:20 +02:00
:indent_level, :highlighted
before_create :assign_short_id_and_upvote
2012-07-03 18:59:50 +02:00
after_create :assign_votes, :mark_submitter, :email_reply
after_destroy :unassign_votes
2012-07-01 00:43:45 +02:00
MAX_EDIT_MINS = 45
2012-06-30 21:14:35 +02:00
validate do
2012-07-01 00:43:45 +02:00
self.comment.to_s.strip == "" &&
errors.add(:comment, "cannot be blank.")
2012-07-01 00:43:45 +02:00
self.user_id.blank? &&
errors.add(:user_id, "cannot be blank.")
2012-07-01 00:43:45 +02:00
self.story_id.blank? &&
errors.add(:story_id, "cannot be blank.")
2012-07-01 20:31:31 +02:00
(m = self.comment.to_s.strip.match(/\A(t)his([\.!])?$\z/i)) &&
2012-07-01 00:43:45 +02:00
errors.add(:base, (m[1] == "T" ? "N" : "n") + "ope" + m[2].to_s)
end
2012-07-01 00:43:45 +02:00
def assign_short_id_and_upvote
(1...10).each do |tries|
if tries == 10
raise "too many hash collisions"
end
2012-07-01 00:43:45 +02:00
if !Comment.find_by_short_id(self.short_id = Utils.random_str(6))
break
end
2012-07-01 00:43:45 +02:00
end
self.upvotes = 1
2012-07-01 00:43:45 +02:00
end
def assign_votes
2012-07-01 00:43:45 +02:00
Vote.vote_thusly_on_story_or_comment_for_user_because(1, self.story_id,
self.id, self.user.id, nil, false)
2012-07-01 00:43:45 +02:00
self.story.update_comment_count!
end
2012-07-01 20:38:01 +02:00
def mark_submitter
Keystore.increment_value_for("user:#{self.user_id}:comments_posted")
end
2012-07-03 18:59:50 +02:00
def email_reply
begin
if self.parent_comment_id && u = self.parent_comment.try(:user)
if u.email_replies?
EmailReply.reply(self, u).deliver
end
if u.pushover_replies?
Pushover.push(u.pushover_user_key, u.pushover_device, {
:title => "Lobsters reply from #{self.user.username} on " <<
"#{self.story.title}",
:message => self.plaintext_comment,
:url => self.url,
:url_title => "Reply to #{self.user.username}",
})
end
end
rescue
end
end
# http://evanmiller.org/how-not-to-sort-by-average-rating.html
# https://github.com/reddit/reddit/blob/master/r2/r2/lib/db/_sorts.pyx
def confidence
n = (upvotes + downvotes).to_f
if n == 0.0
return 0
end
z = 1.281551565545 # 80% confidence
p = upvotes.to_f / n
left = p + (1 / ((2.0 * n) * z * z))
right = z * Math.sqrt((p * ((1.0 - p) / n)) + (z * (z / (4.0 * n * n))))
under = 1.0 + ((1.0 / n) * z * z)
return (left - right) / under
end
2012-07-01 00:43:45 +02:00
def unassign_votes
self.story.update_comment_count!
end
2012-07-01 00:43:45 +02:00
def score
self.upvotes - self.downvotes
end
2012-07-01 00:43:45 +02:00
def linkified_comment
2012-07-02 03:09:22 +02:00
RDiscount.new(self.comment, :smart, :autolink, :safelink,
:filter_html).to_html
2012-07-01 00:43:45 +02:00
end
2012-07-03 18:59:50 +02:00
def plaintext_comment
# TODO: linkify then strip tags and convert entities back
comment
end
2012-07-01 00:43:45 +02:00
def flag!
Story.update_counters self.id, :flaggings => 1
end
2012-06-30 21:14:35 +02:00
2012-07-01 20:31:31 +02:00
def has_been_edited?
self.updated_at && (self.updated_at - self.created_at > 1.minute)
end
2012-07-01 00:43:45 +02:00
def self.ordered_for_story_or_thread_for_user(story_id, thread_id, user_id)
parents = {}
2012-06-30 21:14:35 +02:00
if thread_id
cs = [ "thread_id = ?", thread_id ]
else
cs = [ "story_id = ?", story_id ]
end
Comment.find(:all, :conditions => cs).sort_by{|c| c.confidence }.each do |c|
(parents[c.parent_comment_id.to_i] ||= []).push c
end
# top-down list of comments, regardless of indent level
ordered = []
recursor = lambda{|comment,level|
if comment
comment.indent_level = level
ordered.push comment
end
# for each comment that is a child of this one, recurse with it
(parents[comment ? comment.id : 0] || []).each do |child|
recursor.call(child, level + 1)
end
}
recursor.call(nil, 0)
2012-07-03 19:12:20 +02:00
# TODO: handle deleted comments, show for user_id
2012-06-30 21:14:35 +02:00
ordered
2012-07-01 00:43:45 +02:00
end
2012-06-30 21:14:35 +02:00
def is_editable_by_user?(user)
2012-07-01 00:43:45 +02:00
if !user || user.id != self.user_id
2012-06-30 21:14:35 +02:00
return false
end
2012-07-01 00:43:45 +02:00
(Time.now.to_i - self.created_at.to_i < (60 * MAX_EDIT_MINS))
end
2012-07-03 18:59:50 +02:00
def url
self.story.comments_url + "/comments/#{self.short_id}"
end
end