module Cachy

Constants

HEALTH_CHECK_KEY
KEY_VERSIONS_KEY
KEY_VERSION_TIMEOUT
Version
WHILE_RUNNING_TIMEOUT

Attributes

hash_keys[RW]
prefix[RW]

Public Class Methods

cache(*args) { || ... } click to toggle source

Cache the result of a block

Cachy.cache(:my_key){ expensive() } Cachy.cache(:my_key, :expires_in=>1.hour){ expensive() } Cachy.cache(:my_key, :keys=>){ expensive() } Cachy.cache(:my_key, :without_locale=>true){ expensive() } Cachy.cache(:my_key, :hash_key=>true){ expensive() }

# File lib/cachy.rb, line 18
def self.cache(*args)
  key = key(*args)
  options = extract_options!(args)

  result = cache_store.read(key)
  return result unless result == nil

  # Calculate result!
  set_while_running(key, options)

  result = yield
  cache_store.write key, result, options
  result
end
cache_if(cond, *args, &block) click to toggle source
# File lib/cachy.rb, line 33
def self.cache_if(cond, *args, &block)
  if cond
    cache(*args, &block)
  else
    block.call
  end
end
cache_store() click to toggle source
# File lib/cachy.rb, line 127
def self.cache_store
  @cache_store || raise("Use: Cachy.cache_store = your_cache_store")
end
cache_store=(cache) click to toggle source

Wrap non ActiveSupport style cache stores, to get the same interface for all

# File lib/cachy.rb, line 120
def self.cache_store=(cache)
  @cache_store = wrap_cache(cache)
  @cache_store.write HEALTH_CHECK_KEY, 'yes'
  @key_versions_cache_store = nil
  memory_store.clear
end
delete_key(key) click to toggle source
# File lib/cachy.rb, line 107
def self.delete_key(key)
  versions = key_versions.dup
  versions.delete(key.to_sym)
  self.key_versions = versions
end
expire(*args) click to toggle source

Expire all possible locales of a cache, use the same arguments as with cache

Cachy.expire(:my_key, User.first) Cachy.expire(:my_key, User.first, :keys=>) Cachy.expire(:my_key, :prefix=>'views/')

# File lib/cachy.rb, line 72
def self.expire(*args)
  options = extract_options!(args)

  (locales+[false]).each do |locale|
    without_locale = (locale==false)
    args_with_locale = args + [options.merge(:locale=>locale, :without_locale=>without_locale)]
    cache_store.delete key(*args_with_locale)
  end
end
expire_view(*args) click to toggle source
# File lib/cachy.rb, line 82
def self.expire_view(*args)
  options = extract_options!(args)
  args = args + [options.merge(:prefix=>'views/')]
  expire(*args)
end
get(*args) click to toggle source
# File lib/cachy.rb, line 41
def self.get(*args)
  cache_store.read(key(*args))
end
increment_key(key) click to toggle source

Expires all caches that use this key

# File lib/cachy.rb, line 98
def self.increment_key(key)
  key = key.to_sym
  current_versions = read_versions
  version = current_versions[key] || 0
  version += 1
  self.key_versions = current_versions.merge(key => version)
  version
end
key(*args) click to toggle source

Constructs a cache-key (first argument must be a String/Symbol)

Cachy.key :my_key Cachy.key :my_key, User.first, :locale=>:de Cachy.key :my_key, User.first, :without_locale=>true, :hash_key=>true

# File lib/cachy.rb, line 50
def self.key(*args)
  options = extract_options!(args)
  ensure_valid_keys options

  key = (args + meta_key_parts(args.first, options)).compact.map do |part|
    if part.respond_to? :cache_key
      part.cache_key
    else
      part
    end
  end * "_"

  key = (options[:hash_key] || hash_keys) ? hash(key) : key
  my_prefix = (options[:prefix] || prefix).to_s
  (my_prefix + key + options[:suffix].to_s).gsub(' ', '_')
end
key_versions() click to toggle source
# File lib/cachy.rb, line 88
def self.key_versions
  memory_store.cache{ read_versions }
end
key_versions=(data) click to toggle source
# File lib/cachy.rb, line 92
def self.key_versions=(data)
  memory_store.clear
  write_version(data)
end
key_versions_cache_store() click to toggle source
# File lib/cachy.rb, line 135
def self.key_versions_cache_store
  @key_versions_cache_store || cache_store
end
key_versions_cache_store=(cache) click to toggle source
# File lib/cachy.rb, line 131
def self.key_versions_cache_store=(cache)
  @key_versions_cache_store = wrap_cache(cache)
end
locales() click to toggle source
# File lib/cachy.rb, line 145
def self.locales
  return @@locales if @@locales
  if defined?(I18n) and I18n.respond_to?(:available_locales)
    I18n.available_locales
  else
    []
  end
end
locales=(x) click to toggle source
# File lib/cachy.rb, line 141
def self.locales=(x)
  @@locales = x
end

Private Class Methods

cache_healthy?() click to toggle source
# File lib/cachy.rb, line 191
def self.cache_healthy?
  cache_store.read(HEALTH_CHECK_KEY) == 'yes'
end
detect_cache_error(store) click to toggle source
# File lib/cachy.rb, line 184
def self.detect_cache_error(store)
  data = store.instance_variable_get('@data')
  if data.respond_to? :read_error_occurred
    @@cache_error = data.read_error_occurred
  end
end
ensure_valid_keys(options) click to toggle source
# File lib/cachy.rb, line 224
def self.ensure_valid_keys(options)
  invalid = options.keys - [:keys, :expires_in, :without_locale, :locale, :while_running, :hash_key, :prefix, :suffix]
  raise "unknown keys #{invalid.inspect}" unless invalid.empty?
end
extract_options!(args) click to toggle source
# File lib/cachy.rb, line 242
def self.extract_options!(args)
  if args.last.is_a? Hash
    args.pop
  else
    {}
  end
end
global_cache_version() click to toggle source
# File lib/cachy.rb, line 234
def self.global_cache_version
  defined?(CACHE_VERSION) ? CACHE_VERSION : nil
end
hash(string) click to toggle source
# File lib/cachy.rb, line 229
def self.hash(string)
  require "digest/md5"
  Digest::MD5.hexdigest(string)
end
key_version_for(key) click to toggle source
# File lib/cachy.rb, line 219
def self.key_version_for(key)
  key = key.to_sym
  key_versions[key] || (cache_healthy? ? increment_key(key) : 1)
end
locale() click to toggle source
# File lib/cachy.rb, line 238
def self.locale
  (defined?(I18n) and I18n.respond_to?(:locale)) ? I18n.locale : nil
end
memory_store() click to toggle source
# File lib/cachy.rb, line 250
def self.memory_store
  @memory_store ||= MiniMemoryStore.new(:expires_in => KEY_VERSION_TIMEOUT)
end
meta_key_parts(key, options) click to toggle source
# File lib/cachy.rb, line 204
def self.meta_key_parts(key, options)
  unless [String, Symbol].include?(key.class)
    raise ":key must be first argument of Cachy call"
  end

  parts = []
  parts << "v#{key_version_for(key)}"
  parts << global_cache_version
  parts << (options[:locale] || locale) unless options[:without_locale]

  keys = [*options[:keys]].compact # [*x] == .to_a without warnings
  parts += keys.map{|k| "#{k}v#{key_version_for(k)}" }
  parts
end
read_versions() click to toggle source
# File lib/cachy.rb, line 173
def self.read_versions
  store = key_versions_cache_store
  result = store.read(KEY_VERSIONS_KEY) || {}
  detect_cache_error(store)
  result
end
set_while_running(key, options) click to toggle source

Temorarily store something else in the cache, so that a often-called and slow cache-block is not run by multiple processes in parallel

# File lib/cachy.rb, line 198
def self.set_while_running(key, options)
  return unless options.key? :while_running
  warn "You cannot set while_running to nil" if options[:while_running] == nil
  cache_store.write key, options[:while_running], :expires_in=>WHILE_RUNNING_TIMEOUT
end
wrap_cache(cache) click to toggle source
# File lib/cachy.rb, line 156
def self.wrap_cache(cache)
  if cache.respond_to? :read and cache.respond_to? :write
    cache
  elsif cache.class.to_s =~ /^Redis/
    require 'cachy/redis_wrapper'
    RedisWrapper.new(cache)
  elsif cache.respond_to? :get and cache.respond_to? :set
    require 'cachy/memcached_wrapper'
    MemcachedWrapper.new(cache)
  elsif cache.respond_to? :[] and cache.respond_to? :store
    require 'cachy/moneta_wrapper'
    MonetaWrapper.new(cache)
  else
    raise "This cache_store type is not usable for Cachy!"
  end
end
write_version(data) click to toggle source
# File lib/cachy.rb, line 180
def self.write_version(data)
  key_versions_cache_store.write(KEY_VERSIONS_KEY, data) unless @@cache_error
end