module CounterCache::ClassMethods

Public Instance Methods

counter_cache(relationship, options = {}) click to toggle source

counter_cache :items counter_cache :items, scope: { status: :approved } counter_cache :items, column: :items_count counter_cache :items, scope: { status: :approved }, column: :approved_items_count

# File lib/counter_cache.rb, line 13
def counter_cache(relationship, options = {})
  raise ArgumentError, "counter_cache: Hash expected, got #{options.class.name} from options" if not options.is_a?(Hash)

  association = self.reflect_on_all_associations(:belongs_to).find { |ast| ast.name == relationship }
  raise ArgumentError, "counter_cache: #{relationship} is not defined in #{self.model_name.to_s}" if association.nil?

  if options[:scope].is_a?(Symbol) && self.methods.exclude?(options[:scope].to_sym)
    raise ArgumentError, "counter_cache: #{options[:scope]} is not a scope of #{self.model_name.to_s}"
  end

  column_name = options[:column] || "#{self.table_name}_count".to_sym
  callback_method_name = "callback_to_cache_for_#{column_name}!".to_sym

  self.after_commit callback_method_name

  self.instance_eval do

    define_method callback_method_name do
      association_object = self.public_send(association.name)
      if association_object && association_object.class.column_names.include?(column_name.to_s)
        relation = self.class.unscoped.where(association.name => association_object)
        case options[:scope]
        when Hash, String
          relation = relation.where(options[:scope])
        when Symbol
          relation = relation.public_send(options[:scope].to_sym)
        end
        association_object.update(column_name => relation.size)
      end
    end

    private callback_method_name

  end
end