Redis+CompSci 101 (Redis::Stack)
Recently I got introduced to Redis, a super-cool, im-memory (but less-volitale), key-value store. As usual, I was late to the party.
Redis, is several things, including a sort of data structures server. So to flash back to CompSci 101, in this article I build a stack on top of the Redis Ruby client, which is one of the first things I did with it.
Redis Resources
Before we get started here are some Redis resources I used to get up and running:
Redis::Stack
Our Redis::Stack class will implement 4 operations: push, pop, peek, to_a, and size. Redis provides us with a list data structure, implemented as a Linked List, which we will use to store our stack. The operations that act on lists we will concern ourself with are below. The commands used (passed to the Redis client object) are not specific to the Redis Ruby client but as this is a Ruby blog, I express them in Ruby code.
redis_client = Redis.new
redis_client.lpush 'stack', 'member' # push element onto top of stack
redis_client.lpop 'stack' # pop an element off the stack
redis_client.lrange, 'stack', 0, 0 # peek at the top element of
# the stack. lrange takes a
# key, a starting index in the
# list, and an ending index
The Ruby Redis client allows commands to be called in two ways. The way you will probably want to use it is by calling the command as an instance method of the client. Redis-rb does a little method missing magic with that and calls the instance method #call_command, passing it an array representing the command and its arguments.
#the following will produce the same result
redis_client.call_command ['lpush', 'stack', 'member']
redis_client.lpush 'stack', 'member'
With that, lets implement our Stack. It will take a hash of options when initalized. The options should include a :key to store our stack in and a :redis instance. Each of the commands will only operate on the given :key using the given :redis client.
class Redis::Stack
attr_reader :key
def initialize(options)
@redis = options[:redis]
@key = options[:key]
end
def push(member)
@redis.lpush key, member
end
def pop
@redis.lpop
end
def peek
(@redis.lrange key, 0, 0).first # lrange returns an array
# peek should return a member
end
def to_a
@redis.lrange key, 0, -1
end
def size
to_a.size
end
end
stack = Stack.new(:redis => Redis.new, :key => 'my_stack')
stack.size # 0
stack.to_a # []
stack.peek # nil
stack.pop # nil
stack.push 1
stack.push 2
stack.size # 2
stack.to_a # [2, 1]
stack.peek # 2
stack.pop # 2
stack.peek # 1
There you have it. A dirt simple stack using Redis. Of course, there are some edge cases to handle before using the class but this is more about using Redis so I have ignored those factors here. You will also, of course, need an instance of redis-server running.