class Sfn::Cache
Data caching helper
Attributes
@return [String] custom key for this cache
Public Class Methods
Set/get time limit on data type
@param kind [String, Symbol] data type @param seconds [Integer] return [Integer] seconds
# File lib/sfn/cache.rb, line 51 def apply_limit(kind, seconds = nil) @apply_limit ||= {} if seconds @apply_limit[kind.to_sym] = seconds.to_i end @apply_limit[kind.to_sym].to_i end
Configure the caching approach to use
@param type [Symbol] :redis or :local @param args [Hash] redis connection arguments if used
# File lib/sfn/cache.rb, line 14 def configure(type, args = {}) type = type.to_sym case type when :redis begin require "redis-objects" rescue LoadError $stderr.puts "The `redis-objects` gem is required for Cache support!" raise end @_pid = Process.pid Redis::Objects.redis = Redis.new(args) when :local else raise TypeError.new("Unsupported caching type: #{type}") end enable(type) end
@return [Hash] default limits
# File lib/sfn/cache.rb, line 60 def default_limits (@apply_limit || {}).dup end
Set enabled caching type
@param type [Symbol] @return [Symbol]
# File lib/sfn/cache.rb, line 37 def enable(type) @type = type.to_sym end
Create new instance
@param key [String, Array]
# File lib/sfn/cache.rb, line 79 def initialize(key) if key.respond_to?(:sort) key = key.flatten if key.respond_to?(:flatten) key = key.map(&:to_s).sort end @key = Digest::SHA256.hexdigest(key.to_s) @apply_limit = self.class.default_limits end
Ping the redis connection and reconnect if dead
# File lib/sfn/cache.rb, line 65 def redis_ping! if (@_pid && @_pid != Process.pid) || !Redis::Objects.redis.connected? Redis::Objects.redis.client.reconnect @_pid = Process.pid end end
@return [Symbol] type of caching enabled
# File lib/sfn/cache.rb, line 42 def type @type || :local end
Public Instance Methods
Fetch data
@param name [String, Symbol] @return [Object, NilClass]
# File lib/sfn/cache.rb, line 216 def [](name) if kind = registry[name.to_s] get_storage(self.class.type, kind, name) else nil end end
Set data
@param key [Object] @param val [Object] @note this will never work, thus you should never use it
# File lib/sfn/cache.rb, line 229 def []=(key, val) raise "Setting backend data is not allowed" end
Apply time limit for data type
@param kind [String, Symbol] data type @param seconds [Integer] return [Integer]
# File lib/sfn/cache.rb, line 247 def apply_limit(kind, seconds = nil) @apply_limit ||= {} if seconds @apply_limit[kind.to_sym] = seconds.to_i end @apply_limit[kind.to_sym].to_i end
Clear data
@param args [Symbol] list of names to delete @return [TrueClass] @note clears all data if no names provided
# File lib/sfn/cache.rb, line 108 def clear!(*args) internal_lock do args = registry.keys if args.empty? args.each do |key| value = self[key] if value.respond_to?(:clear) value.clear elsif value.respond_to?(:value) value.value = nil end registry.delete(key) end yield if block_given? end true end
Fetch item from local storage
@param data_type [Symbol] @param full_name [Symbol] @param args [Hash] @return [Object] @todo make proper singleton for local storage
# File lib/sfn/cache.rb, line 184 def get_local_storage(data_type, full_name, args = {}) @storage ||= {} @storage[full_name] ||= case data_type.to_sym when :array [] when :hash {} when :value LocalValue.new when :lock LocalLock.new(full_name, {:expiration => 60, :timeout => 0.1}.merge(args)) when :stamped Stamped.new(full_name.sub("#{key}_", "").to_sym, get_local_storage(:value, full_name), self) else raise TypeError.new("Unsupported caching data type encountered: #{data_type}") end end
Fetch item from redis storage
@param data_type [Symbol] @param full_name [Symbol] @param args [Hash] @return [Object]
# File lib/sfn/cache.rb, line 159 def get_redis_storage(data_type, full_name, args = {}) self.class.redis_ping! case data_type.to_sym when :array Redis::List.new(full_name, {:marshal => true}.merge(args)) when :hash Redis::HashKey.new(full_name) when :value Redis::Value.new(full_name, {:marshal => true}.merge(args)) when :lock Redis::Lock.new(full_name, {:expiration => 60, :timeout => 0.1}.merge(args)) when :stamped Stamped.new(full_name.sub("#{key}_", "").to_sym, get_redis_storage(:value, full_name), self) else raise TypeError.new("Unsupported caching data type encountered: #{data_type}") end end
Fetch item from storage
@param store_type [Symbol] @param data_type [Symbol] @param name [Symbol] name of data @param args [Hash] options for underlying storage @return [Object]
# File lib/sfn/cache.rb, line 132 def get_storage(store_type, data_type, name, args = {}) full_name = "#{key}_#{name}" result = nil case store_type.to_sym when :redis result = get_redis_storage(data_type, full_name.to_s, args) when :local @_local_cache ||= {} unless @_local_cache[full_name.to_s] @_local_cache[full_name.to_s] = get_local_storage(data_type, full_name.to_s, args) end result = @_local_cache[full_name.to_s] else raise TypeError.new("Unsupported caching storage type encountered: #{store_type}") end unless full_name == "#{key}_registry_#{key}" registry[name.to_s] = data_type end result end
Initialize a new data type
@param name [Symbol] name of data @param kind [Symbol] data type @param args [Hash] options for data type
# File lib/sfn/cache.rb, line 93 def init(name, kind, args = {}) get_storage(self.class.type, kind, name, args) true end
Execute block within internal lock
@return [Object] result of yield @note for internal use
# File lib/sfn/cache.rb, line 206 def internal_lock get_storage(self.class.type, :lock, :internal_access, :timeout => 20, :expiration => 120).lock do yield end end
Perform action within lock
@param lock_name [String, Symbol] name of lock @param raise_on_locked [TrueClass, FalseClass] raise execption if lock wait times out @return [Object] result of yield
# File lib/sfn/cache.rb, line 260 def locked_action(lock_name, raise_on_locked = false) begin self[lock_name].lock do yield end rescue => e if e.class.to_s.end_with?("Timeout") raise if raise_on_locked else raise end end end
@return [Hash] data registry
# File lib/sfn/cache.rb, line 99 def registry get_storage(self.class.type, :hash, "registry_#{key}") end
Check if cache time has expired
@param key [String, Symbol] value key @param stamp [Time, Integer] @return [TrueClass, FalseClass]
# File lib/sfn/cache.rb, line 238 def time_check_allow?(key, stamp) Time.now.to_i - stamp.to_i > apply_limit(key) end