Skip to content

Instantly share code, notes, and snippets.

@Empact
Created June 14, 2011 10:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Empact/1024643 to your computer and use it in GitHub Desktop.
Save Empact/1024643 to your computer and use it in GitHub Desktop.
has_counter_cache_on is a simple ActiveRecord extension which allows you to have multiple counters for a single association, counted when simple conditions are met
# has_counter_cache_on is a simple ActiveRecord extension
# which allows you to have multiple counters for a single
# association, counted when simple conditions are met
#
# The implementation is adapted from the current
# add_counter_cache_callbacks code in Rails.
#
# Example:
#
# class Submission < ActiveRecord::Base
# end
#
# class Vote < ActiveRecord::Base
# belongs_to :submission, counter_cache: :votes_count
# has_counter_cache_on :submission, :upvotes_count, support: true
# has_counter_cache_on :submission, :downvotes_count, support: false
# end
#
module HasCounterCacheOn
extend ActiveSupport::Concern
module ClassMethods
def has_counter_cache_on(association, cache_column, conditions)
association = self.reflect_on_association(association)
model = association.active_record
name = association.name
method_name = "belongs_to_counter_cache_after_create_for_#{name}_#{cache_column}"
model.redefine_method(method_name) do
record = send(name)
record.class.increment_counter(cache_column, record.id) if record && conditions.all? {|k, v| send(k) == v }
end
model.after_create(method_name)
method_name = "belongs_to_counter_cache_before_destroy_for_#{name}_#{cache_column}"
model.redefine_method(method_name) do
record = send(name)
record.class.decrement_counter(cache_column, record.id) if record && conditions.all? {|k, v| send(k) == v }
end
model.before_destroy(method_name)
model.send(:module_eval,
"#{association.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{association.class_name}) && #{association.class_name}.respond_to?(:attr_readonly)", __FILE__, __LINE__
)
end
end
end
ActiveRecord::Base.send(:include, HasCounterCacheOn)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment