Friday 9 December 2011

Rails 3: Counter Cache in Mongoid Documents

Currently, mongoid gem doesn't support counter_cache and here's my workaround for that. Offcourse, there might be more better versions available, but just wanted to share mine too :)

In config/initializers/mongoid_counter_cache.rb

module Mongoid
  module CounterCache

    extend ActiveSupport::Concern
    # Enables AR counter_cache mechanism on all cols ending with '_count' in the document.
    # Ex. Usage: in Photo model, add 'include Mongoid::CounterCache', after fields definition.
    # NOTE:: for a column 'likes_count' to work, we should have 'Like' document, with 'photo' association.
    included do
      klass = self.to_s.downcase
      cache_cols = self.fields.keys.select { |col| col.include?('_count') }
      create_meth = "increment_counter_in_#{klass}"
      destroy_meth = "decrement_counter_in_#{klass}"
      cache_cols.each do |col|
        modal = col.to_s.sub(/_count/, '').classify.constantize
        modal.class_eval <<-STR
          after_create '#{create_meth}'.to_sym
          after_destroy '#{destroy_meth}'.to_sym

          def #{create_meth}
            modal = self.#{klass}
            count = modal.#{col} + 1
            modal.update_attribute('#{col}'.to_sym, count)
          end

          def #{destroy_meth}
            modal = self.#{klass}
            count = modal.#{col} - 1
            modal.update_attribute('#{col}'.to_sym, count)
          end
        STR
      end # cache_cols.each
    end # included

  end
end

In app/models/photo.rb

include Mongoid::CounterCache, after the field declarations.

That's it. You are good to go. All columns ending with '_count' will be managed, automatically (provided the regular rails conventions are followed, refer inline comments in the code block, above.)

No comments:

Post a Comment