module CounterCulture::Extensions::ClassMethods

Public Instance Methods

after_commit_counter_cache() click to toggle source

this holds all configuration data

# File lib/counter_culture/extensions.rb, line 7
def after_commit_counter_cache
  config = @after_commit_counter_cache || []
  if superclass.respond_to?(:after_commit_counter_cache) && superclass.after_commit_counter_cache
    config = superclass.after_commit_counter_cache + config
  end
  config
end
counter_culture(relation, options = {}) click to toggle source

called to configure counter caches

# File lib/counter_culture/extensions.rb, line 16
def counter_culture(relation, options = {})
  unless @after_commit_counter_cache
    # initialize callbacks only once
    after_create :_update_counts_after_create

    before_destroy :_update_counts_after_destroy, unless: :destroyed_for_counter_culture?

    if respond_to?(:before_real_destroy) &&
        instance_methods.include?(:paranoia_destroyed?)
      before_real_destroy :_update_counts_after_destroy,
        if: -> (model) { !model.paranoia_destroyed? }
    end

    after_update :_update_counts_after_update, unless: :destroyed_for_counter_culture?

    if respond_to?(:before_restore)
      before_restore :_update_counts_after_create,
        if: -> (model) { model.deleted? }
    end

    if defined?(Discard::Model) && include?(Discard::Model)
      before_discard :_update_counts_after_destroy,
        if: ->(model) { !model.discarded? }

      before_undiscard :_update_counts_after_create,
        if: ->(model) { model.discarded? }
    end

    # we keep a list of all counter caches we must maintain
    @after_commit_counter_cache = []
  end

  if options[:column_names] && !options[:column_names].is_a?(Hash)
    raise ":column_names must be a Hash of conditions and column names"
  end

  # add the counter to our collection
  @after_commit_counter_cache << Counter.new(self, relation, options)
end
counter_culture_fix_counts(options = {}) click to toggle source

checks all of the declared counter caches on this class for correctnes based on original data; if the counter cache is incorrect, sets it to the correct count

options:

{ :exclude => list of relations to skip when fixing counts,
  :only => only these relations will have their counts fixed,
  :column_name => only this column will have its count fixed }

returns: a list of fixed record as an array of hashes of the form:

{ :entity => which model the count was fixed on,
  :id => the id of the model that had the incorrect count,
  :what => which column contained the incorrect count,
  :wrong => the previously saved, incorrect count,
  :right => the newly fixed, correct count }
# File lib/counter_culture/extensions.rb, line 71
def counter_culture_fix_counts(options = {})
  raise "No counter cache defined on #{name}" unless @after_commit_counter_cache

  options[:exclude] = Array(options[:exclude]) if options[:exclude]
  options[:exclude] = options[:exclude].try(:map) {|x| x.is_a?(Enumerable) ? x : [x] }
  options[:only] = [options[:only]] if options[:only] && !options[:only].is_a?(Enumerable)
  options[:only] = options[:only].try(:map) {|x| x.is_a?(Enumerable) ? x : [x] }

  @after_commit_counter_cache.flat_map do |counter|
    next if options[:exclude] && options[:exclude].include?(counter.relation)
    next if options[:only] && !options[:only].include?(counter.relation)

    reconciler_options = %i(batch_size column_name finish skip_unsupported start touch verbose where)

    reconciler = CounterCulture::Reconciler.new(counter, options.slice(*reconciler_options))
    reconciler.reconcile!
    reconciler.changes
  end.compact
end