require 'monitor' class Class def cache_method *methods DeclarativeCache.cache_method self, *methods end def cache_method_with_params *methods DeclarativeCache.cache_method_with_params self, *methods end end module DeclarativeCache DISABLED = false warn "CASHE DISABLED" if DISABLED @versions, @alias_counter, @monitor = Hash.new{should! :be_never_called}, 0, Monitor.new class << self def alias_counter @alias_counter += 1 return :"m#{@alias_counter}" end def cache_method *arg vnames, klass, methods = parse_and_check_arguments *arg return if DISABLED methods.each do |m| als = (m.to_s =~ /^[_a-zA-Z0-9]+$/) ? m : DeclarativeCache.alias_counter.to_sym klass.class_eval{alias_method :"cached_#{als}", :"#{m}"} unless vnames.is_a? Array script = single_version_without_args.interpolate binding @versions[vnames] = 0 unless @versions.include? vnames else vnames_str = vnames.collect{|vname| "'#{vname}' => nil"}.join(', ') script = multiple_version_without_args.interpolate binding vnames.each{|vname| @versions[vname] = 0 unless @versions.include? vname} end klass.class_eval script, __FILE__, __LINE__ end end def cache_method_with_params *arg vnames, klass, methods = parse_and_check_arguments *arg return if DISABLED methods.each do |m| als = (m.to_s =~ /^[_a-zA-Z0-9]+$/) ? m : DeclarativeCache.alias_counter klass.class_eval{alias_method :"cached_#{als}", :"#{m}"} unless vnames.is_a? Array script = single_version_with_args.interpolate binding @versions[vnames] = 0 unless @versions.include? vnames else vnames_str = vnames.collect{|vname| "'#{vname}' => nil"}.join(', ') script = multiple_version_with_args.interpolate binding vnames.each{|vname| @versions[vname] = 0 unless @versions.include? vname} end klass.class_eval script, __FILE__, __LINE__ end end def version name @versions[name] end def update *names names.each do |n| n = n.to_s @versions[n] = 0 unless @versions.include? n @versions[n] += 1 end end attr_reader :monitor protected def parse_and_check_arguments *arg arg.size.should! :>=, 2 if arg.size == 3 version_names = arg.shift if version_names.is_a? Array version_names.size.should! :>, 0 version_names = version_names.collect{|n| n.to_s} else version_names = version_names.to_s end klass = arg.shift else version_names = klass = arg.shift end klass.class.should! :be, [Class, Module] methods = Array(arg.first) defined = klass.instance_methods methods.each do |m| raise "Invalid method_name '#{m}'!" unless defined.include? m.to_s end return version_names, klass, methods end def multiple_version_with_args <<-RUBY def \#{m} *params DeclarativeCache.monitor.synchronize do @cache_versions_\#{als} ||= {\#{vnames_str}} @cache_value_\#{als} ||= {} if @cache_versions_\#{als}.all?{|vname, v| v == DeclarativeCache.version(vname)} and @cache_value_\#{als}.include?(params) return @cache_value_\#{als}[params] else @cache_versions_\#{als}.keys.each{|vname| @cache_versions_\#{als}[vname] = DeclarativeCache.version(vname)} return @cache_value_\#{als}[params] = cached_\#{als}(*params) end end end RUBY end def multiple_version_without_args <<-RUBY def \#{m} DeclarativeCache.monitor.synchronize do @cache_versions_\#{als} ||= {\#{vnames_str}} if @cache_versions_\#{als}.all?{|vname, v| v == DeclarativeCache.version(vname)} return @cache_value_\#{als} else @cache_versions_\#{als}.keys.each{|vname| @cache_versions_\#{als}[vname] = DeclarativeCache.version(vname)} return @cache_value_\#{als} = cached_\#{als} end end end RUBY end def single_version_with_args <<-RUBY def \#{m} *params DeclarativeCache.monitor.synchronize do @cache_value_\#{als} ||= {} if @cache_version_\#{als} == DeclarativeCache.version("\#{vnames}") and @cache_value_\#{als}.include?(params) return @cache_value_\#{als}[params] else @cache_version_\#{als} = DeclarativeCache.version("\#{vnames}") return @cache_value_\#{als}[params] = cached_\#{als}(*params) end end end RUBY end def single_version_without_args <<-RUBY def \#{m} DeclarativeCache.monitor.synchronize do if @cache_version_\#{als} == DeclarativeCache.version("\#{vnames}") return @cache_value_\#{als} else @cache_version_\#{als} = DeclarativeCache.version("\#{vnames}") return @cache_value_\#{als} = cached_\#{als} end end end RUBY end end end