class Rating::Rating

Public Class Methods

averager_data(resource, scopeable) click to toggle source
# File lib/rating/models/rating/rating.rb, line 20
def averager_data(resource, scopeable)
  total_count    = how_many_resource_received_votes_sql(distinct: false, resource: resource, scopeable: scopeable)
  distinct_count = how_many_resource_received_votes_sql(distinct: true, resource: resource, scopeable: scopeable)
  values         = { resource_type: resource.class.base_class.name }

  values[:scopeable_type] = scopeable.class.base_class.name unless scopeable.nil?

  sql = %(
    SELECT
      (CAST(#{total_count} AS DECIMAL(17, 14)) / #{distinct_count}) count_avg,
      COALESCE(AVG(value), 0)                                       rating_avg
    FROM #{rate_table_name}
    WHERE
      resource_type = :resource_type
      #{scope_type_query(resource, scopeable)}
      #{scope_where_query(resource)}
  ).squish

  execute_sql [sql, values]
end
data(resource, scopeable) click to toggle source
# File lib/rating/models/rating/rating.rb, line 41
def data(resource, scopeable)
  averager = averager_data(resource, scopeable)
  values   = values_data(resource, scopeable)

  {
    average:  values.rating_avg,
    estimate: estimate(averager, values),
    sum:      values.rating_sum,
    total:    values.rating_count,
  }
end
update_rating(resource, scopeable) click to toggle source
# File lib/rating/models/rating/rating.rb, line 73
def update_rating(resource, scopeable)
  attributes             = { resource: resource }
  attributes[:scopeable] = unscoped_rating?(resource) ? nil : scopeable

  record = find_or_initialize_by(attributes)
  result = data(resource, scopeable)

  record.average  = result[:average]
  record.sum      = result[:sum]
  record.total    = result[:total]
  record.estimate = result[:estimate]

  record.save
end
values_data(resource, scopeable) click to toggle source
# File lib/rating/models/rating/rating.rb, line 53
def values_data(resource, scopeable)
  sql = %(
    SELECT
      COALESCE(AVG(value), 0) rating_avg,
      COALESCE(SUM(value), 0) rating_sum,
      COUNT(1)                rating_count
    FROM #{rate_table_name}
    WHERE
      resource_type = ?
      AND resource_id = ?
      #{scope_type_and_id_query(resource, scopeable)}
      #{scope_where_query(resource)}
  ).squish

  values =  [sql, resource.class.base_class.name, resource.id]
  values += [scopeable.class.base_class.name, scopeable.id] unless scopeable.nil? || unscoped_rating?(resource)

  execute_sql values
end

Private Class Methods

estimate(averager, values) click to toggle source
# File lib/rating/models/rating/rating.rb, line 90
def estimate(averager, values)
  resource_type_rating_avg = averager.rating_avg
  count_avg                = averager.count_avg
  resource_rating_avg      = values.rating_avg
  resource_rating_count    = values.rating_count.to_f

  (resource_rating_count / (resource_rating_count + count_avg)) * resource_rating_avg +
    (count_avg           / (resource_rating_count + count_avg)) * resource_type_rating_avg
end
execute_sql(sql) click to toggle source
# File lib/rating/models/rating/rating.rb, line 100
def execute_sql(sql)
  Rate.find_by_sql(sql).first
end
how_many_resource_received_votes_sql(distinct:, resource:, scopeable:) click to toggle source
# File lib/rating/models/rating/rating.rb, line 108
def how_many_resource_received_votes_sql(distinct:, resource:, scopeable:)
  count = distinct ? 'COUNT(DISTINCT resource_id)' : 'COUNT(1)'

  %((
    SELECT GREATEST(#{count}, 1)
    FROM #{rate_table_name}
    WHERE
      resource_type = :resource_type
      #{scope_type_query(resource, scopeable)}
      #{scope_where_query(resource)}
  ))
end
rate_table_name() click to toggle source
# File lib/rating/models/rating/rating.rb, line 121
def rate_table_name
  @rate_table_name ||= Rate.table_name
end
scope_type_and_id_query(resource, scopeable) click to toggle source
# File lib/rating/models/rating/rating.rb, line 131
def scope_type_and_id_query(resource, scopeable)
  return '' if unscoped_rating?(resource)
  return 'AND scopeable_type is NULL AND scopeable_id is NULL' if scopeable.nil?

  'AND scopeable_type = ? AND scopeable_id = ?'
end
scope_type_query(resource, scopeable) click to toggle source
# File lib/rating/models/rating/rating.rb, line 125
def scope_type_query(resource, scopeable)
  return '' if unscoped_rating?(resource)

  scopeable.nil? ? 'AND scopeable_type is NULL' : 'AND scopeable_type = :scopeable_type'
end
scope_where_query(resource) click to toggle source
# File lib/rating/models/rating/rating.rb, line 138
def scope_where_query(resource)
  return '' if where_condition(resource).blank?

  "AND #{where_condition(resource)}"
end
unscoped_rating?(resource) click to toggle source
# File lib/rating/models/rating/rating.rb, line 104
def unscoped_rating?(resource)
  resource.rating_options[:unscoped_rating]
end
where_condition(resource) click to toggle source
# File lib/rating/models/rating/rating.rb, line 144
def where_condition(resource)
  resource.rating_options[:where]
end