module Cacheable
Public Class Methods
# File lib/cacheable.rb, line 77 def self.build_cache_key(attributes,args) "#{self.name}_#{attributes.join('_')}_#{args.map(&:to_s).join('_')}" end
store the time when the object is cached
# File lib/cacheable.rb, line 82 def self.build_cached_time_key(attributes,args) "#{build_cache_key(attributes,args)}_cached_time" end
# File lib/cache_configuration.rb, line 5 def self.cache CacheConfiguration.cache end
including classes that want to have time-based expire can implement this method can be 5.minutes, 1.day …etc… 0 is never expire
# File lib/cacheable.rb, line 48 def self.cache_expired_duration 600 # default to 10 minutes end
This method must be implemented by subclasses, because only subclasses know how to do the actual data fetch
# File lib/cacheable.rb, line 41 def self.fetch(attributes,args) raise 'The method <fetch> must be implemented by subclass' end
# File lib/cacheable.rb, line 53 def self.find_in_cache(attributes,args) cache_key = build_cache_key(attributes,args) obj = Cacheable::cache.fetch(cache_key) if cache_expired_duration > 0 cached_time = Cacheable::cache.fetch(build_cached_time_key(attributes,args)) || 0 return refresh_cache(attributes, args) if ( (Time.now.to_i - cached_time > cache_expired_duration) && obj) end obj end
# File lib/cacheable.rb, line 5 def self.included(base) base.class_eval do def self.method_missing(name, *args, &block) query_attributes = [] if name.to_s.start_with? 'fetch_by_' # puts("\n\nname = #{name}\nname['fetch_by_'.length, name.length] = #{name['fetch_by_'.length, name.length]}") query_attributes = name['fetch_by_'.length, name.length].split('_and_') return nil if query_attributes == [] # puts("\n\n Cacheable method_missing - query attributes = #{query_attributes}, arguments = #{args.inspect}\n\n") find_in_cache(query_attributes,args) or refresh_cache(query_attributes, args) else super end end # Use this method if you want to explicitly re-fetch the whole object with given attributes-values, and write the newly fetched object to the cache def self.refresh_cache(attributes, args) obj = fetch(attributes, args) return nil if obj.nil? obj.cached_by_attributes = attributes return obj.update_cache rescue => error Cacheable::logger.error("\n\nError in Cacheable.refresh_cache error = #{error.message}\n#{error.backtrace.join("\n")}\n\n") return nil end # This method must be implemented by subclasses, because only subclasses know how to do the actual data fetch def self.fetch(attributes,args) raise 'The method <fetch> must be implemented by subclass' end # including classes that want to have time-based expire can implement this method # can be 5.minutes, 1.day ...etc... # 0 is never expire def self.cache_expired_duration 600 # default to 10 minutes end private def self.find_in_cache(attributes,args) cache_key = build_cache_key(attributes,args) obj = Cacheable::cache.fetch(cache_key) if cache_expired_duration > 0 cached_time = Cacheable::cache.fetch(build_cached_time_key(attributes,args)) || 0 return refresh_cache(attributes, args) if ( (Time.now.to_i - cached_time > cache_expired_duration) && obj) end obj end def self.save_to_cache(attributes, args, obj) cache_key = build_cache_key(attributes,args) options = {} if cache_expired_duration > 0 options = { expires_in: cache_expired_duration.to_i } Cacheable::cache.write(build_cached_time_key(attributes,args), Time.now.to_i) end Cacheable::cache.write(cache_key, obj, options) end def self.build_cache_key(attributes,args) "#{self.name}_#{attributes.join('_')}_#{args.map(&:to_s).join('_')}" end # store the time when the object is cached def self.build_cached_time_key(attributes,args) "#{build_cache_key(attributes,args)}_cached_time" end end end
# File lib/cache_configuration.rb, line 9 def self.logger CacheConfiguration.logger end
# File lib/cacheable.rb, line 8 def self.method_missing(name, *args, &block) query_attributes = [] if name.to_s.start_with? 'fetch_by_' # puts("\n\nname = #{name}\nname['fetch_by_'.length, name.length] = #{name['fetch_by_'.length, name.length]}") query_attributes = name['fetch_by_'.length, name.length].split('_and_') return nil if query_attributes == [] # puts("\n\n Cacheable method_missing - query attributes = #{query_attributes}, arguments = #{args.inspect}\n\n") find_in_cache(query_attributes,args) or refresh_cache(query_attributes, args) else super end end
Use this method if you want to explicitly re-fetch the whole object with given attributes-values, and write the newly fetched object to the cache
# File lib/cacheable.rb, line 27 def self.refresh_cache(attributes, args) obj = fetch(attributes, args) return nil if obj.nil? obj.cached_by_attributes = attributes return obj.update_cache rescue => error Cacheable::logger.error("\n\nError in Cacheable.refresh_cache error = #{error.message}\n#{error.backtrace.join("\n")}\n\n") return nil end
# File lib/cacheable.rb, line 65 def self.save_to_cache(attributes, args, obj) cache_key = build_cache_key(attributes,args) options = {} if cache_expired_duration > 0 options = { expires_in: cache_expired_duration.to_i } Cacheable::cache.write(build_cached_time_key(attributes,args), Time.now.to_i) end Cacheable::cache.write(cache_key, obj, options) end
Public Instance Methods
All the attributes that instances of classes are cached by are dynamically created and store in cache in order to support multiple runtimes of cache server
# File lib/cacheable.rb, line 125 def cached_by_attributes=(attributes) all_attributes = all_cached_by_attributes cached_attributes_for_class = all_attributes[self.class.name] || [] cached_attributes_for_class << attributes if !cached_attributes_for_class.include?(attributes) all_attributes[self.class.name] = cached_attributes_for_class Cacheable::cache.write('all_cached_by_attributes', all_attributes) end
Delete cached object from cache For example if you have a cached current_profile, and you want to delete it out of cache use current_profile.delete_from_cache
# File lib/cacheable.rb, line 93 def delete_from_cache cached_key_attributes = cached_by_attributes raise "This object has never been cached in the cache #{self.inspect}" if cached_key_attributes.nil? cached_key_attributes.each do |attribute_keys| attribute_values = build_attribute_values(attribute_keys) cache_key = self.class.build_cache_key(attribute_keys,attribute_values) Cacheable::cache.delete(cache_key) end self end
This method updates all instances of an object with all cached keys (cached by different attributes) If you have an instance of a cached object, you just modify it or partially modify it, and want to write it back to the cache then use this method. For example if you have a current_profile, and its info changed, use current_profile.update_cache
# File lib/cacheable.rb, line 109 def update_cache cached_key_attributes = cached_by_attributes raise "This object has never been cached in the cache #{self.inspect}" if cached_key_attributes.nil? cached_key_attributes.each do |attribute_keys| attribute_values = build_attribute_values(attribute_keys) self.class.save_to_cache(attribute_keys, attribute_values, self) end self end
Private Instance Methods
# File lib/cacheable.rb, line 140 def all_cached_by_attributes Cacheable::cache.fetch('all_cached_by_attributes') || {} end
# File lib/cacheable.rb, line 144 def build_attribute_values(attribute_keys) attribute_values = [] attribute_keys.each do |attribute| attribute_values << self.send(attribute) end attribute_values end
# File lib/cacheable.rb, line 136 def cached_by_attributes all_cached_by_attributes[self.class.name] end