ryanto/acts_as_votable

View on GitHub
lib/acts_as_votable/cacheable.rb

Summary

Maintainability
C
1 day
Test Coverage
# frozen_string_literal: true

module ActsAsVotable
  module Cacheable
    def scope_cache_field(field, vote_scope)
      return field if vote_scope.nil?

      case field
      when :cached_votes_total=
        "cached_scoped_#{vote_scope}_votes_total="
      when :cached_votes_total
        "cached_scoped_#{vote_scope}_votes_total"
      when :cached_votes_up=
        "cached_scoped_#{vote_scope}_votes_up="
      when :cached_votes_up
        "cached_scoped_#{vote_scope}_votes_up"
      when :cached_votes_down=
        "cached_scoped_#{vote_scope}_votes_down="
      when :cached_votes_down
        "cached_scoped_#{vote_scope}_votes_down"
      when :cached_votes_score=
        "cached_scoped_#{vote_scope}_votes_score="
      when :cached_votes_score
        "cached_scoped_#{vote_scope}_votes_score"
      when :cached_weighted_total
        "cached_weighted_#{vote_scope}_total"
      when :cached_weighted_total=
        "cached_weighted_#{vote_scope}_total="
      when :cached_weighted_score
        "cached_weighted_#{vote_scope}_score"
      when :cached_weighted_score=
        "cached_weighted_#{vote_scope}_score="
      when :cached_weighted_average
        "cached_weighted_#{vote_scope}_average"
      when :cached_weighted_average=
        "cached_weighted_#{vote_scope}_average="
      end
    end

    def update_cached_votes(vote_scope = nil)
      updates = {}

      if self.respond_to?(:cached_votes_up=)
        updates[:cached_votes_up] = count_votes_up(true)
      end

      if self.respond_to?(:cached_votes_down=)
        updates[:cached_votes_down] = count_votes_down(true)
      end

      if self.respond_to?(:cached_votes_total=)
        updates[:cached_votes_total] = (
          (updates[:cached_votes_up] || count_votes_up(true)) +
          (updates[:cached_votes_down] || count_votes_down(true))
        )
      end

      if self.respond_to?(:cached_votes_score=)
        updates[:cached_votes_score] = (
          (updates[:cached_votes_up] || count_votes_up(true)) -
          (updates[:cached_votes_down] || count_votes_down(true))
        )
      end

      if self.respond_to?(:cached_weighted_total=)
        updates[:cached_weighted_total] = weighted_total(true)
      end

      if self.respond_to?(:cached_weighted_score=)
        updates[:cached_weighted_score] = weighted_score(true)
      end

      if self.respond_to?(:cached_weighted_average=)
        updates[:cached_weighted_average] = weighted_average(true)
      end

      if vote_scope
        if self.respond_to?(scope_cache_field :cached_votes_up=, vote_scope)
          updates[scope_cache_field :cached_votes_up, vote_scope] = count_votes_up(true, vote_scope)
        end

        if self.respond_to?(scope_cache_field :cached_votes_down=, vote_scope)
          updates[scope_cache_field :cached_votes_down, vote_scope] = count_votes_down(true, vote_scope)
        end

        if self.respond_to?(scope_cache_field :cached_votes_total=, vote_scope)
          updates[scope_cache_field :cached_votes_total, vote_scope] = (
            (updates[scope_cache_field :cached_votes_up, vote_scope] || count_votes_up(true, vote_scope)) +
            (updates[scope_cache_field :cached_votes_down, vote_scope] || count_votes_down(true, vote_scope))
          )
        end

        if self.respond_to?(scope_cache_field :cached_weighted_total=, vote_scope)
          updates[scope_cache_field :cached_weighted_total, vote_scope] = weighted_total(true, vote_scope)
        end

        if self.respond_to?(scope_cache_field :cached_weighted_score=, vote_scope)
          updates[scope_cache_field :cached_weighted_score, vote_scope] = weighted_score(true, vote_scope)
        end

        if self.respond_to?(scope_cache_field :cached_votes_score=, vote_scope)
          updates[scope_cache_field :cached_votes_score, vote_scope] = (
            (updates[scope_cache_field :cached_votes_up, vote_scope] || count_votes_up(true, vote_scope)) -
            (updates[scope_cache_field :cached_votes_down, vote_scope] || count_votes_down(true, vote_scope))
          )
        end

        if self.respond_to?(scope_cache_field :cached_weighted_average=, vote_scope)
          updates[scope_cache_field :cached_weighted_average, vote_scope] = weighted_average(true, vote_scope)
        end
      end

      self.send(acts_as_votable_options[:cacheable_strategy], updates) if updates.size > 0
    end

    # counting
    def count_votes_total(skip_cache = false, vote_scope = nil)
      from_cache(skip_cache, :cached_votes_total, vote_scope) do
        find_votes_for(scope_or_empty_hash(vote_scope)).count
      end
    end

    def count_votes_up(skip_cache = false, vote_scope = nil)
      from_cache(skip_cache, :cached_votes_up, vote_scope) do
        get_up_votes(vote_scope: vote_scope).count
      end
    end

    def count_votes_down(skip_cache = false, vote_scope = nil)
      from_cache(skip_cache, :cached_votes_down, vote_scope) do
        get_down_votes(vote_scope: vote_scope).count
      end
    end

    def count_votes_score(skip_cache = false, vote_scope = nil)
      from_cache(skip_cache, :cached_votes_score, vote_scope) do
        ups = count_votes_up(true, vote_scope)
        downs = count_votes_down(true, vote_scope)
        ups - downs
      end
    end

    def weighted_total(skip_cache = false, vote_scope = nil)
      from_cache(skip_cache, :cached_weighted_total, vote_scope) do
        find_votes_for(scope_or_empty_hash(vote_scope)).sum(:vote_weight)
      end
    end

    def weighted_score(skip_cache = false, vote_scope = nil)
      from_cache(skip_cache, :cached_weighted_score, vote_scope) do
        ups = get_up_votes(vote_scope: vote_scope).sum(:vote_weight)
        downs = get_down_votes(vote_scope: vote_scope).sum(:vote_weight)
        ups - downs
      end
    end

    def weighted_average(skip_cache = false, vote_scope = nil)
      from_cache(skip_cache, :cached_weighted_average, vote_scope) do
        count = count_votes_total(skip_cache, vote_scope).to_i
        if count > 0
          weighted_score(skip_cache, vote_scope).to_f / count
        else
          0.0
        end
      end
    end

    private

    def from_cache(skip_cache, cached_method, vote_scope)
      if !skip_cache && respond_to?(scope_cache_field(cached_method, vote_scope))
        send(scope_cache_field(cached_method, vote_scope))
      else
        yield
      end
    end
  end
end