AnyCache ·

AnyCache - a simplest cache wrapper that provides a minimalistic generic interface for all well-known cache storages and includes a minimal set of necessary operations:
fetch, read, write, delete, expire, persist, exist?, clear, increment, decrement.
Supported clients:
Redis(gem redis) (redis storage)Redis::Store(gem redis-store) (redis storage)Dalli::Client(gem dalli) (memcached storage)ActiveSupport::Cache::RedisCacheStore(gem activesupport) (redis cache storage)ActiveSupport::Cache::MemCacheStore(gem activesupport) (memcache storage)ActiveSupport::Cache::FileStore(gem activesupport) (file storage)ActiveSupport::Cache::MemoryStore(gem activesupport) (in memory storage)
Installation
gem 'any_cache'
bundle install
# --- or ---
gem install any_cache
require 'any_cache'
Usage / Table of Contents
Creation
To instantiate AnyCache instance you have to provide a client. Client - an independent driver that works with a corresponding cache storage (external dependency).
Supported clients:
RedisRedis::StoreDalli::ClientActiveSupport::Cache::RedisCacheStoreActiveSupport::Cache::MemCacheStoreActiveSupport::Cache::FileStoreActiveSupport::Cache::MemoryStore
AnyCache can be instantiated by two ways:
Manual creation
Custom instantiation with explicit client objects:
# 1) create client object
client = Redis.new(...)
# -- or --
client = Redis::Store.new(...)
# -- or --
client = Dalli::Client.new(...)
# -- or --
client = ActiveSupport::Cache::RedisCacheStore.new(...)
# --- or ---
client = ActiveSupport::Cache::MemCacheStore.new(...)
# -- or --
client = ActiveSupport::Cache::FileStore.new(...)
# -- or --
client = ActiveSupport::Cache::MemoryStore.new(...)
# 2) build AnyCache instance
any_cache = AnyCache.build(client) # => <AnyCache:0x00007f990527f268 ...>
Config-based creation
You can configure AnyCache globally or create subclasses and configure each of them. After that
storage instantiation works via .build method without explicit attributes.
AnyCache.configureis used for configuration;config.driveris used for determine which client should be used;config.__driver_name__.optionsstores client-related options;
Supported drivers:
:redis- Redis;:redis_tore- Redis::Client;:dalli- Dalli::Client;:as_redis_cache_store- ActiveSupport::Cache::RedisCacheStore;:as_mem_cache_store- ActiveSupport::Cache::MemCacheStore;:as_file_store- ActiveSupport::Cache::FileStore;:as_memory_store- ActiveSupport::Cache::MemoryStore;
AnyCache with Redis:
require 'redis'
require 'any_cache'
AnyCache.configure do |conf|
conf.driver = :redis
conf.redis. = { ... } # Redis-related options
end
AnyCache.build
AnyCache with Redis::Store:
require 'redis-store'
require 'any_cache'
AnyCache.configure do |conf|
conf.driver = :redis_store
conf.redis_store. = { ... } # Redis::Store-related options
end
AnyCache.build
AnyCache with Dalli::Client:
require 'dalli'
require 'any_cache'
AnyCache.configure do |conf|
conf.driver = :dalli
conf.dalli.servers = ... # string or array of strings
conf.dalli. = { ... } # Dalli::Client-related options
end
AnyCache.build
AnyCache with ActiveSupport::Cache::RedisCacheStore:
require 'active_support'
require 'any_cache'
AnyCache.configure do |conf|
conf.driver = :as_redis_cache_store
conf.as_redis_cache_store. = { ... } # ActiveSupport::Cache::RedisCacheStore-related options
end
AnyCache.build
AnyCache with ActiveSupport::Cache::MemCacheStore:
require 'active_support'
require 'any_cache'
AnyCache.configure do |conf|
conf.driver = :as_mem_cache_store
conf.as_memory_store.servers = ... # string or array of strings
conf.as_memory_store. = { ... } # ActiveSupport::Cache::MemCacheStore-related options
end
AnyCache.build
AnyCache with ActiveSupport::Cache::FileStore:
require 'active_support'
require 'any_cache'
AnyCache.configure do |conf|
conf.driver = :as_file_store
conf.as_file_store.cache_path = '/path/to/cache'
conf.as_file_store. = { ... } # ActiveSupport::Cache::FileStore-related options
end
AnyCache.build
AnyCache with ActiveSupport::Cache::MemoryStore:
require 'activesupport'
require 'any_cache'
AnyCache.configure do |conf|
conf.driver = :as_memory_store
conf.as_memory_store. = { ... } # ActiveSupport::Cache::MemoryStore-related options
end
AnyCache.build
Many cache storages
You can inherit AnyCache class and create and configure as many cache storages as you want:
class RedisCache < AnyCache
configure do |conf|
conf.driver = :redis
end
end
class DalliCache < AnyCache
configure do |conf|
conf.driver = :dalli
end
end
redis_cache = RedisCache.build
dalli_cache = DalliCache.build
Custom cache clients
If you want to use your own cache client implementation, you should provide an object that responds to:
#fetch(*key, [**options])(doc)#read(key, [**options])(doc)#write(key, value, [**options])(doc)#delete(key, [**options])(doc)#increment(key, amount, [**options])(doc)#decrement(key, amount, [**options])(doc)#expire(key, [**options])(doc)#persist(key, [**options])(doc)#exist?(key, [**options])(doc)#clear([**options])(doc)
class MyCacheClient
# ...
def read(key, **)
# ...
end
def write(key, value, **)
# ...
end
# ...
end
AnyCache.build(MyCacheClient.new)
Operations
AnyCache provides a following operation set:
Fetch
AnyCache#fetch(key, [force:], [expires_in:], [&block])- works in
ActiveSupport::Cache::Store#fetch-manner; - fetches data from the cache, using the given key;
- if there is data in the cache with the given key, then that data is returned;
- if there is no such data in the cache (a cache miss), then nil will be returned:
- if a block has been passed, that block will be passed the key and executed in the event of a cache miss;
- the return value of the block will be written to the cache under the given cache key, and that return value will be returned;
- works in
# --- entry exists ---
any_cache.fetch("data") # => "some_data"
any_cache.fetch("data") { "new_data" } # => "some_data"
# --- entry does not exist ---
any_cache.fetch("data") # => nil
any_cache.fetch("data") { "new_data" } # => "new_data"
any_cache.fetch("data") # => "new_data"
# --- new entry with expiration time ---
any_cache.fetch("data") # => nil
any_cache.fetch("data", expires_in: 8) { "new_data" } # => "new_data"
any_cache.fetch("data") # => "new_data"
# ...sleep 8 seconds...
any_cache.fetch("data") # => nil
# --- force update/rewrite ---
any_cache.fetch("data") # => "some_data"
any_cache.fetch("data", expires_in: 8, force: true) { "new_data" } # => "new_data"
any_cache.fetch("data") # => "new_data"
# ...sleep 8 seconds...
any_cache.fetch("data") # => nil
Read
AnyCache#read(key)- get an entry value from the cache storage
# --- entry exists ---
any_cache.read("data") # => "some_data"
# --- entry doesnt exist ---
any_cache.read("data") # => nil
Write
AnyCache#write(key, value, [expires_in:])- write a new entry to the cache storage
# --- permanent entry ---
any_cache.write("data", 123)
# --- temporal entry (expires in 60 seconds) ---
any_cache.write("data", 123, expires_in: 60)
Delete
AnyCache#delete(key)- remove entry from the cache storage
any_cache.delete("data")
Increment
AnyCache#increment(key, amount = 1, [expires_in:])- increment entry's value by the given amount and set the new expiration time if needed
# --- increment existing entry ---
any_cache.write("data", 1)
# --- increment by default value (1) ---
any_cache.increment("data") # => 2
# --- increment by custom value ---
any_cache.increment("data", 12) # => 14
# --- increment and expire after 31 seconds
any_cache.incrmeent("data", expires_in: 31) # => 15
# --- increment nonexistent entry (create new entry) ---
any_cache.increment("another_data", 5, expires_in: 5) # => 5
Decrement
AnyCache#decrement(key, amount = 1, [expires_in:])- decrement entry's value by the given amount and set the new expiration time if needed
# --- decrement existing entry ---
any_cache.write("data", 15)
# --- decrement by default value (1) ---
any_cache.decrement("data") # => 14
# --- decrement by custom value ---
any_cache.decrement("data", 10) # => 4
# --- decrement and expire after 5 seconds
any_cache.decrememnt("data", expirs_in: 5) # => 3
# --- decrement nonexistent entry (create new entry) ---
any_cache.decrememnt("another_data", 2, expires_in: 10) # => -2 (or 0 for Dalli::Client)
Expire
AnyCache#expire(key, [expires_in:])- expire entry immediately or set the new expiration time
# --- expire immediately ---
any_cache.expire("data")
# --- set new expiration time (in seconds) --
any_cache.expire("data", expires_in: 36)
Persist
AnyCache#persist(key)- change entry's expiration time to permanent
# --- create temporal entry (30 seconds) ---
any_cache.write("data", { a: 1 }, expires_in: 30)
# --- remove entry expiration (make it permanent) ---
any_cache.persist("data")
Existence
AnyCache#exist?(key)- determine if an entry exists
# --- entry exists ---
any_cache.exist?("data") # => true
# --- entry does not exist ---
any_cache.exist?("another-data") # => false
Clear
AnyCache#clear()- clear cache database
# --- prepare cache data ---
any_cache.write("data", { a: 1, b: 2 })
any_cache.write("another_data", 123_456)
any_cache.read("data") # => { a: 1, b: 2 }
any_cache.read("another_data") # => 123_456
# --- clear cache ---
any_cache.clear
any_cache.read("data") # => nil
any_cache.read("another_data") # => nil
Build
- see bin/rspec
bin/rspec --test-redis # run specs with Redis
bin/rspec --test-redis-store # run specs with Redis::Store
bin/rspec --test-dalli # run specs with Dalli::Client
bin/rspec --test-as-file-store # run specs with ActiveSupport::Cache::FileStore
bin/rspec --test-as-memory-store # run specs with ActiveSupport::Cache::MemoryStore
bin/rspec --test-as-redis-cache-store # run specs with ActiveSupport::Cache::RedisCacheStore
bin/rspec --test-as-mem-cache-store # run specs with ActiveSupport::Cache::MemCacheStore
Contributing
- Fork it (https://github.com/0exp/any_cache/fork)
- Create your feature branch (
git checkout -b feature/my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin feature/my-new-feature) - Create new Pull Request
License
Released under MIT License.
Authors
Created by Rustam Ibragimov