module Cachy
Constants
- HEALTH_CHECK_KEY
- KEY_VERSIONS_KEY
- KEY_VERSION_TIMEOUT
- Version
- WHILE_RUNNING_TIMEOUT
Attributes
Public Class Methods
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
# File lib/cachy.rb, line 33 def self.cache_if(cond, *args, &block) if cond cache(*args, &block) else block.call end end
# File lib/cachy.rb, line 127 def self.cache_store @cache_store || raise("Use: Cachy.cache_store = your_cache_store") end
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
# 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 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
# File lib/cachy.rb, line 82 def self.expire_view(*args) options = extract_options!(args) args = args + [options.merge(:prefix=>'views/')] expire(*args) end
# File lib/cachy.rb, line 41 def self.get(*args) cache_store.read(key(*args)) end
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
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
# File lib/cachy.rb, line 88 def self.key_versions memory_store.cache{ read_versions } end
# File lib/cachy.rb, line 92 def self.key_versions=(data) memory_store.clear write_version(data) end
# File lib/cachy.rb, line 135 def self.key_versions_cache_store @key_versions_cache_store || cache_store end
# File lib/cachy.rb, line 131 def self.key_versions_cache_store=(cache) @key_versions_cache_store = wrap_cache(cache) end
# 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
# File lib/cachy.rb, line 141 def self.locales=(x) @@locales = x end
Private Class Methods
# File lib/cachy.rb, line 191 def self.cache_healthy? cache_store.read(HEALTH_CHECK_KEY) == 'yes' end
# 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
# 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
# File lib/cachy.rb, line 242 def self.extract_options!(args) if args.last.is_a? Hash args.pop else {} end end
# File lib/cachy.rb, line 234 def self.global_cache_version defined?(CACHE_VERSION) ? CACHE_VERSION : nil end
# File lib/cachy.rb, line 229 def self.hash(string) require "digest/md5" Digest::MD5.hexdigest(string) end
# 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
# File lib/cachy.rb, line 238 def self.locale (defined?(I18n) and I18n.respond_to?(:locale)) ? I18n.locale : nil end
# File lib/cachy.rb, line 250 def self.memory_store @memory_store ||= MiniMemoryStore.new(:expires_in => KEY_VERSION_TIMEOUT) end
# 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
# 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
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
# 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
# File lib/cachy.rb, line 180 def self.write_version(data) key_versions_cache_store.write(KEY_VERSIONS_KEY, data) unless @@cache_error end