<img src=“https://badge.fury.io/rb/custom_counter_cache.svg” alt=“Gem Version” /> <img src=“https://travis-ci.org/cedric/custom_counter_cache.svg?branch=master” alt=“Build Status” />
This is a simple approach to creating a custom counter cache in Rails that can be used across multiple models.
Add the following to your Gemfile:
gem 'custom_counter_cache'
This is the block that will be used to calculate the value for the counter cache. It will be called by other models through their association via an after_save or after_destroy callback.
include CustomCounterCache::Model define_counter_cache :articles_count do |user| user.articles.where(state: 'published').count end
This will define the after_create, after_update and after_destroy callbacks. An :if option can be provided to limit when these callbacks get triggered.
include CustomCounterCache::Model update_counter_cache :user, :articles_count, if: -> (article) { article.state_changed? }
These callbacks can be added to any number of models that might need to change the counter cache.
To store the counter cache you need to create a column for the model with the counter cache (example: articles_count).
If you would like to store all of your counter caches in a single table, you can use this migration:
create_table :counters do |t| t.references :countable, polymorphic: true t.string :key, null: false t.integer :value, null: false, default: 0 t.timestamps end add_index :counters, [ :countable_id, :countable_type, :key ], unique: true
Here is the example model to go with:
class Counter < ActiveRecord::Base belongs_to :countable, polymorphic: true validates :countable, presence: true end
If you would like to store your counter cache in an existing table, you can use this migration:
def change add_column :users, :articles_count, :integer, default: 0, null: false end
To backfill your counters, run something like this either in a migration or in the console:
User.select(:id).find_each(batch_size: 100) { |u| u.update_articles_count }