A very simple, class-based namespace prefixing and encapsulation for Redis. Key features include:
- Establishes a maintainable convention by prefixing keys with the class name (e.g.
- Delegates all method calls to the redis-rb within the namespace
- Adds a better abstraction layer around Redis objects and commands
Here's an example:
class Timer < RedisClassy def start pipelined do set Time.now.to_i expire 120.seconds end end def stop del end def running? !!get end end timer = Timer.new(123) timer.start timer.running? => true Timer.keys => ["123"] RedisClassy.keys => ["Timer:123"]
The Timer class above is self-contained and more readable.
This library is made intentionally small, yet powerful when you need better abstraction on Redis objects to keep things organized.
UPGRADING FROM v1
redis-rb vs redis-namespace vs redis-classy
With the vanilla
redis gem, you've been doing this:
redis = Redis.new redis.set 'foo', 'bar' redis.get 'foo' # => "bar"
redis-namespace gem, you can add a prefix in the following manner:
redis_ns = Redis::Namespace.new('ns', :redis => redis) redis_ns['foo'] = 'bar' # equivalent of => redis.set 'ns:foo', 'bar' redis_ns['foo'] # => "bar"
Now, with the
redis-classy gem, you finally achieve a class-based naming convention:
class Something < RedisClassy end Something.on('foo').set('bar') # equivalent of => redis.set 'Something:foo', 'bar' Something.on('foo').get # => "bar" something = Something.new('foo') something.set 'bar' # equivalent of => redis.set 'Something:foo', 'bar' something.get # => "bar"
Register the Redis server: (e.g. in
config/initializers/redis_classy.rb for Rails)
RedisClassy.redis = Redis.current
Create a class that inherits RedisClassy. (e.g. in
app/redis/cache.rb for Rails, for auto- and eager-loading)
class Cache < RedisClassy def put(content) pipelined do set content expire 5.seconds end end end cache = Cache.new(123) cache.put "This tape will self-destruct in five seconds. Good luck."
on method is added as a syntactic sugar for
new, you can also run a command in one shot as well:
For convenience, singleton and predefined static keys are also supported.
class Counter < RedisClassy singleton end Counter.incr # 'Counter:singleton' => '1' Counter.incr # 'Counter:singleton' => '2' Counter.get # => '2'
class Stats < RedisClassy singletons :median, :average end ages = [21,22,24,28,30] Stats.median.set ages[ages.size/2] # 'Stats:median' => '24' Stats.average.set ages.inject(:+)/ages.size # 'Stats:average' => '25' Stats.median.get # => '24' Stats.average.get # => '25'
Finally, you can also pass an arbitrary object that responds to
id as a key. This is useful when used in combination with ActiveRecord, etc.
class Lock < RedisClassy end class Room < ActiveRecord::Base end room = Room.create lock = Lock.new(room)
When you need an access to the non-namespaced, raw Redis keys, it's available as
RedisClass.keys. Keep in mind that this method is very slow at O(N) computational complexity and potentially hazardous when you have many keys. Read the details.
RedisClassy.keys => ["Stats:median", "Stats:average", "Counter"] RedisClassy.keys 'Stats:*' => ["Stats:median", "Stats:average"]
redis attribute is a class instance variable, you can dynamically assign different databases for each class, without affecting other classes.
Cache.redis = Redis::Namespace.new('Cache', redis: Redis.new(host: 'another.host'))
If you run fork-based app servers such as Unicorn or Passenger, you need to reconnect to the Redis after forking.
after_fork do RedisClassy.redis.client.reconnect end
Note that since Redis Classy assigns a namespaced Redis instance upon the inheritance event of each subclass (
class Something < RedisClassy), reconnecting the master (non-namespaced) connection that is referenced from all subclasses should probably be the safest and the most efficient way to survive a forking event.