module RunningCount::Counter

Public Class Methods

add_callbacks(klass, opts) click to toggle source
# File lib/running_count/counter.rb, line 104
def add_callbacks(klass, opts)
  klass.after_commit :enqueue_changes, on: [:create, :update], if: opts[:if]
  klass.after_commit :enqueue_deletion, on: [:destroy], if: opts[:if]
end
counter_data(name, table_name, relation, opts = {}) click to toggle source
# File lib/running_count/counter.rb, line 70
def counter_data(name, table_name, relation, opts = {})
  destination_class = destination_class_name(relation, opts)

  counter_column = opts.fetch(:counter_column, "#{name.underscore.pluralize}_count")
  running_set_name = "running_#{counter_column}"
  statement = "update_#{counter_column}"

  destination_class.redefine_method(running_set_name) do
    self.send(counter_column) + Counter.running_count(self, running_set_name)
  end

  sql = Statement.statement_sql(
    table_name,
    statement,
    destination_class.table_name,
    counter_column,
    relation,
    opts,
  )

  {
    counter_column: counter_column,
    relation: relation,
    source: opts[:source],
    destination: table_name,
    running_set_name: running_set_name,
    statement: statement,
    aggregated_field: opts[:aggregated_field],
    changed_field: opts[:changed_field],
    statement_sql: sql,
    if: opts[:if],
  }
end
enqueue_changes(record, counter_data) click to toggle source
# File lib/running_count/counter.rb, line 8
def enqueue_changes(record, counter_data)
  counter_data
    .values
    .partition { |data| data[:aggregated_field].present? }
    .tap do |sums, counts|
      sums.each { |data| Counter.enqueue_sum(record, data) }
      counts.each { |data| Counter.enqueue_count(record, data) }
    end
end
enqueue_count(record, counter_data) click to toggle source
# File lib/running_count/counter.rb, line 30
def enqueue_count(record, counter_data)
  if (changed_field = counter_data[:changed_field])
    return true unless record.previous_changes.has_key?(changed_field) && counter_data[:if].call(record)
  end

  destination = record.send(counter_data[:relation])
  item = Format.item(destination)

  Storage.add_item(item, counter_data[:running_set_name], counter_data.fetch(:amount, 1))
end
enqueue_deletion(record, counter_data) click to toggle source
# File lib/running_count/counter.rb, line 41
def enqueue_deletion(record, counter_data)
  counter_data.each_value do |data|
    Counter.enqueue_single_delete(record, data)
  end
end
enqueue_single_delete(record, data) click to toggle source
# File lib/running_count/counter.rb, line 47
def enqueue_single_delete(record, data)
  destination = record.send(data[:relation])
  item = Format.item(destination)
  amount = amount_from_deleted_record(record, data)

  Storage.add_item(item, data[:running_set_name], 0 - amount)
rescue StandardError => exception
end
enqueue_sum(record, counter_data) click to toggle source
# File lib/running_count/counter.rb, line 18
def enqueue_sum(record, counter_data)
  aggregated_field = counter_data[:aggregated_field]

  return true unless record.previous_changes.has_key?(aggregated_field)

  diff = record.previous_changes[aggregated_field].last.to_i - record.previous_changes[aggregated_field].first.to_i

  if diff != 0
    Counter.enqueue_count(record, counter_data.merge(amount: diff))
  end
end
reconcile_changes(counter_data) click to toggle source
# File lib/running_count/counter.rb, line 56
def reconcile_changes(counter_data)
  Statement.prepare_statement(counter_data)

  Storage
    .scores(counter_data[:running_set_name])
    .each { |item, _score| Statement.reconcile_item(item, counter_data) }
end
running_count(destination, running_set_name) click to toggle source
# File lib/running_count/counter.rb, line 64
def running_count(destination, running_set_name)
  item = Format.item(destination)

  Storage.scores(running_set_name, item).to_i
end

Private Class Methods

amount_from_deleted_record(record, counter_data) click to toggle source
# File lib/running_count/counter.rb, line 111
def amount_from_deleted_record(record, counter_data)
  if counter_data[:aggregated_field]
    record.send(counter_data[:aggregated_field])
  else
    counter_data.fetch(:amount, 1)
  end
end
destination_class_name(relation, opts) click to toggle source
# File lib/running_count/counter.rb, line 119
def destination_class_name(relation, opts)
  opts[:class_name] ? opts[:class_name].to_s.constantize : relation.to_s.camelcase.constantize
end