consul/consul

View on GitHub
app/models/related_content.rb

Summary

Maintainability
A
0 mins
Test Coverage
class RelatedContent < ApplicationRecord
  RELATED_CONTENT_SCORE_THRESHOLD = Setting["related_content_score_threshold"].to_f
  RELATIONABLE_MODELS = %w[proposals debates budgets investments].freeze

  acts_as_paranoid column: :hidden_at
  include ActsAsParanoidAliases

  belongs_to :author, class_name: "User"
  belongs_to :parent_relationable, polymorphic: true, optional: false, touch: true
  belongs_to :child_relationable, polymorphic: true, optional: false, touch: true
  has_one :opposite_related_content, class_name: name, foreign_key: :related_content_id
  has_many :related_content_scores, dependent: :destroy

  validates :parent_relationable_id,
            uniqueness: {
              scope: [:parent_relationable_type, :child_relationable_id, :child_relationable_type]
            }
  validate :different_parent_and_child

  after_create :create_opposite_related_content, unless: proc { opposite_related_content.present? }
  after_create :create_author_score

  scope :not_hidden, -> { where(hidden_at: nil) }
  scope :from_users, -> { where(machine_learning: false) }
  scope :from_machine_learning, -> { where(machine_learning: true) }
  scope :for_proposals, -> do
    where(parent_relationable_type: "Proposal", child_relationable_type: "Proposal")
  end
  scope :for_investments, -> do
    where(parent_relationable_type: "Budget::Investment", child_relationable_type: "Budget::Investment")
  end

  def score_positive(user)
    score(RelatedContentScore::SCORES[:POSITIVE], user)
  end

  def score_negative(user)
    score(RelatedContentScore::SCORES[:NEGATIVE], user)
  end

  def scored_by_user?(user)
    related_content_scores.exists?(user: user)
  end

  def same_parent_and_child?
    parent_relationable == child_relationable
  end

  def duplicate?
    parent_relationable.relationed_contents.include?(child_relationable)
  end

  private

    def different_parent_and_child
      if same_parent_and_child?
        errors.add(:parent_relationable, :itself)
      end
    end

    def create_opposite_related_content
      related_content = RelatedContent.create!(opposite_related_content: self,
                                               parent_relationable: child_relationable,
                                               child_relationable: parent_relationable,
                                               machine_learning: machine_learning,
                                               author: author)
      self.opposite_related_content = related_content
    end

    def score(value, user)
      score_with_opposite(value, user)

      if (related_content_scores.sum(:value) / related_content_scores_count) < RELATED_CONTENT_SCORE_THRESHOLD
        hide_with_opposite
      end
    end

    def hide_with_opposite
      hide
      opposite_related_content.hide
    end

    def create_author_score
      score_positive(author)
    end

    def score_with_opposite(value, user)
      RelatedContentScore.create(user: user, related_content: self, value: value)
      RelatedContentScore.create(user: user, related_content: opposite_related_content, value: value)
    end
end