wbotelhos/rating

View on GitHub
lib/rating/models/rating/extension.rb

Summary

Maintainability
A
35 mins
Test Coverage
# frozen_string_literal: true

module Rating
  module Extension
    extend ActiveSupport::Concern

    included do
      def rate(resource, value, author: self, extra_scopes: {}, metadata: {}, scope: nil)
        Rate.create(
          author:       author,
          extra_scopes: extra_scopes,
          metadata:     metadata,
          resource:     resource,
          scopeable:    scope,
          value:        value
        )
      end

      def rate_for(resource, extra_scopes: {}, scope: nil)
        Rate.rate_for author: self, extra_scopes: extra_scopes, resource: resource, scopeable: scope
      end

      def rated?(resource, extra_scopes: {}, scope: nil)
        Rate.exists?(extra_scopes.merge(author: self, resource: resource, scopeable: scope))
      end

      def rates(extra_scopes: {}, scope: nil)
        attributes = { scopeable: scope }.merge(extra_scopes)

        rates_records.where attributes
      end

      def rated(extra_scopes: {}, scope: nil)
        attributes = { scopeable: scope }.merge(extra_scopes)

        rated_records.where attributes
      end

      def rating(scope: nil)
        rating_records.find_by scopeable: scope
      end

      def rating_warm_up(scoping: nil)
        return Rating.find_or_create_by(resource: self) if scoping.blank?

        [scoping].flatten.compact.map do |attribute|
          next unless respond_to?(attribute)

          [public_send(attribute)].flatten.compact.map do |object|
            Rating.find_or_create_by! resource: self, scopeable: object
          end
        end.flatten.compact
      end
    end

    module ClassMethods
      def rating(options = {})
        after_create -> { rating_warm_up scoping: options[:scoping] }, unless: -> { options[:as] == :author }

        has_many :rating_records,
          as:         :resource,
          class_name: '::Rating::Rating',
          dependent:  :destroy

        has_many :rates_records,
          as:         :resource,
          class_name: '::Rating::Rate',
          dependent:  :destroy

        has_many :rated_records,
          as:         :author,
          class_name: '::Rating::Rate',
          dependent:  :destroy

        scope :order_by_rating, lambda { |opts = {}|
          column = opts.fetch(:column, :estimate)
          direction = opts.fetch(:direction, :desc)
          scope = opts[:scope]

          includes(:rating_records)
            .where(Rating.table_name => { scopeable_id: scope&.id, scopeable_type: scope&.class&.base_class&.name })
            .order("#{Rating.table_name}.#{column} #{direction}")
        }

        define_method :rating_options do
          options
        end
      end
    end
  end
end