zooniverse/Talk-Api

View on GitHub
app/models/comment.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
class Comment < ActiveRecord::Base
  include Moderatable
  include HashChanges
  include HstoreUpdate
  include Searchable
  include Sectioned
  include Notifiable
  include BooleanCoercion

  include Comment::Tagging
  include Comment::Mentioning
  include Comment::Subscribing
  include Comment::Publishing

  has_many :mentions, dependent: :destroy
  has_many :group_mentions, dependent: :destroy
  has_many :tags, dependent: :destroy
  has_many :taggables, through: :tags

  belongs_to :reply, class_name: 'Comment'
  has_many :replies, class_name: 'Comment', foreign_key: :reply_id

  belongs_to :user, required: true
  belongs_to :discussion, counter_cache: true, touch: true, required: true
  belongs_to :focus, polymorphic: true
  belongs_to :board

  validates :body, presence: true
  validates :section, presence: true
  validates :focus_type, inclusion: {
    in: ['Subject'],
    if: ->{ focus_id.present? },
    message: 'must be "Subject"'
  }

  before_validation :set_section
  before_create :denormalize_attributes
  after_create :update_discussion
  before_update :update_board_id, if: ->{ discussion_id_changed? }
  after_update :update_moved_discussion
  after_destroy :update_discussion, :clear_replies

  moderatable_with :destroy, by: [:moderator, :admin]
  moderatable_with :ignore, by: [:moderator, :admin]
  moderatable_with :report, by: [:all]

  def upvote!(voter)
    hstore_concat 'upvotes', voter.login => Time.now.to_i
  end

  def remove_upvote!(voter)
    hstore_delete_key 'upvotes', voter.login
  end

  def soft_destroy
    update_attributes is_deleted: true, body: 'This comment has been deleted'
    mentions.destroy_all
    group_mentions.destroy_all
    tags.destroy_all
    close_moderation
    # prevent empty discussions
    discussion.destroy unless discussion.comments.where(is_deleted: false).any?
    self
  end

  def searchable?
    discussion.searchable? && !is_deleted?
  end

  def searchable_update
    <<-SQL
      update searchable_comments
      set content =
        setweight(to_tsvector(comments.body), 'B') ||
        setweight(to_tsvector(users.login), 'B') ||
        setweight(to_tsvector(users.display_name), 'B') ||
        setweight(to_tsvector(tag_list.names), 'A')
        #{ searchable_focus }
      from comments, users, (
        select coalesce(string_agg(name, ' '), '') as names
        from tags
        where comment_id = #{ id }
      ) as tag_list
      where searchable_comments.searchable_id = #{ id } and
      users.id = comments.user_id and
      comments.id = #{ id }
    SQL
  end

  def searchable_focus
    return '' unless focus
    <<-SQL
      || setweight(to_tsvector(focus_type), 'C')
      || setweight(to_tsvector(focus_id::text), 'A')
      || setweight(to_tsvector(substring(focus_type, 1, 1) || focus_id::text), 'A')
    SQL
  end

  protected

  def set_section
    self.section = discussion.section
  end

  def denormalize_attributes
    self.focus ||= discussion.focus if discussion.focus
    self.user_login = user.login
    self.board_id = discussion.board_id
  end

  def update_discussion
    discussion.last_comment_created_at = discussion.comments.order(created_at: :desc).first.try :created_at
    discussion.last_comment_created_at ||= discussion.created_at
    board.last_comment_created_at = discussion.last_comment_created_at
    board.save if board.changed?
    discussion.update_counters!
  end

  def update_board_id
    denormalize_attributes if board_id != discussion.board_id
  end

  def update_moved_discussion
    changes.fetch(:discussion_id, []).compact.each do |id|
      Discussion.find_by_id(id).try :update_counters!
    end
  end

  def clear_replies
    replies.update_all reply_id: nil
  end

  def url
    prefix = if project_section_match
      FrontEnd.project_talk(project)
    else
      FrontEnd.zooniverse_talk
    end
    "#{prefix}/#{board_id}/#{discussion_id}?comment=#{id}"
  end
end