module Cacheable

Public Class Methods

build_cache_key(attributes,args) click to toggle source
# File lib/cacheable.rb, line 77
def self.build_cache_key(attributes,args)
  "#{self.name}_#{attributes.join('_')}_#{args.map(&:to_s).join('_')}"
end
build_cached_time_key(attributes,args) click to toggle source

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
cache() click to toggle source
# File lib/cache_configuration.rb, line 5
def self.cache
  CacheConfiguration.cache
end
cache_expired_duration() click to toggle source

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
fetch(attributes,args) click to toggle source

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
find_in_cache(attributes,args) click to toggle source
# 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
included(base) click to toggle source
# 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
logger() click to toggle source
# File lib/cache_configuration.rb, line 9
def self.logger
  CacheConfiguration.logger
end
method_missing(name, *args, &block) click to toggle source
Calls superclass method
# 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
refresh_cache(attributes, args) click to toggle source

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
save_to_cache(attributes, args, obj) click to toggle source
# 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

cached_by_attributes=(attributes) click to toggle source

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_from_cache() click to toggle source

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
update_cache() click to toggle source

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

all_cached_by_attributes() click to toggle source
# File lib/cacheable.rb, line 140
def all_cached_by_attributes
  Cacheable::cache.fetch('all_cached_by_attributes') || {}
end
build_attribute_values(attribute_keys) click to toggle source
# 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
cached_by_attributes() click to toggle source
# File lib/cacheable.rb, line 136
def cached_by_attributes
  all_cached_by_attributes[self.class.name]
end