module MakeTaggable::Taggable module Cache def self.included(base) # When included, conditionally adds tag caching methods when the model # has any "cached_#{tag_type}_list" column base.extend Columns end module Columns # ActiveRecord::Base.columns makes a database connection and caches the # calculated columns hash for the record as @columns. Since we don't # want to add caching methods until we confirm the presence of a # caching column, and we don't want to force opening a database # connection when the class is loaded, here we intercept and cache # the call to :columns as @make_taggable_cache_columns # to mimic the underlying behavior. While processing this first # call to columns, we do the caching column check and dynamically add # the class and instance methods # FIXME: this method cannot compile in rubinius def columns @make_taggable_cache_columns ||= begin db_columns = super _add_tags_caching_methods if _has_tags_cache_columns?(db_columns) db_columns end end def reset_column_information super @make_taggable_cache_columns = nil end private # @private def _has_tags_cache_columns?(db_columns) db_column_names = db_columns.map(&:name) tag_types.any? do |context| db_column_names.include?("cached_#{context.to_s.singularize}_list") end end # @private def _add_tags_caching_methods send :include, MakeTaggable::Taggable::Cache::InstanceMethods extend MakeTaggable::Taggable::Cache::ClassMethods before_save :save_cached_tag_list initialize_tags_cache end end module ClassMethods def initialize_tags_cache tag_types.map(&:to_s).each do |tag_type| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def self.caching_#{tag_type.singularize}_list? caching_tag_list_on?("#{tag_type}") end RUBY end end def make_taggable(*args) super(*args) initialize_tags_cache end def caching_tag_list_on?(context) column_names.include?("cached_#{context.to_s.singularize}_list") end end module InstanceMethods def save_cached_tag_list tag_types.map(&:to_s).each do |tag_type| if self.class.send("caching_#{tag_type.singularize}_list?") if tag_list_cache_set_on(tag_type) list = tag_list_cache_on(tag_type).to_a.flatten.compact.join("#{MakeTaggable.delimiter} ") self["cached_#{tag_type.singularize}_list"] = list end end end true end end end end