require File.dirname(__FILE__) + '/enumerable_object' class Redis # # Class representing a Redis list. Instances of Redis::List are designed to # behave as much like Ruby arrays as possible. # class List < EnumerableObject # Works like push. Can chain together: list << 'a' << 'b' def <<(value) push(value) # marshal in push() self # for << 'a' << 'b' end # Add a member before or after pivot in the list. Redis: LINSERT def insert(where,pivot,value) allow_expiration do redis.linsert(key,where,marshal(pivot),marshal(value)) end end # Add a member to the end of the list. Redis: RPUSH def push(*values) allow_expiration do count = redis.rpush(key, values.map{|v| marshal(v) }) redis.ltrim(key, -options[:maxlength], -1) if options[:maxlength] count end end # Remove a member from the end of the list. Redis: RPOP def pop(n=nil) if n result, = redis.multi do redis.lrange(key, -n, -1) redis.ltrim(key, 0, -n - 1) end unmarshal result else unmarshal redis.rpop(key) end end # Atomically pops a value from one list, pushes to another and returns the # value. Destination can be a String or a Redis::List # # list.rpoplpush(destination) # # Returns the popped/pushed value. # # Redis: RPOPLPUSH def rpoplpush(destination) unmarshal redis.rpoplpush(key, destination.is_a?(Redis::List) ? destination.key : destination.to_s) end # Add a member to the start of the list. Redis: LPUSH def unshift(*values) allow_expiration do count = redis.lpush(key, values.map{|v| marshal(v) }) redis.ltrim(key, 0, options[:maxlength] - 1) if options[:maxlength] count end end # Remove a member from the start of the list. Redis: LPOP def shift(n=nil) if n result, = redis.multi do redis.lrange(key, 0, n - 1) redis.ltrim(key, n, -1) end unmarshal result else unmarshal redis.lpop(key) end end # Return all values in the list. Redis: LRANGE(0,-1) def values vals = range(0, -1) vals.nil? ? [] : vals end alias_method :get, :values alias_method :value, :values # Same functionality as Ruby arrays. If a single number is given, return # just the element at that index using Redis: LINDEX. Otherwise, return # a range of values using Redis: LRANGE. def [](index, length=nil) if index.is_a? Range range(index.first, index.max) elsif length case length <=> 0 when 1 then range(index, index + length - 1) when 0 then [] when -1 then nil # Ruby does this (a bit weird) end else at(index) end end alias_method :slice, :[] # Same functionality as Ruby arrays. def []=(index, value) allow_expiration do redis.lset(key, index, marshal(value)) end end # Delete the element(s) from the list that match name. If count is specified, # only the first-N (if positive) or last-N (if negative) will be removed. # Use .del to completely delete the entire key. # Redis: LREM def delete(name, count=0) redis.lrem(key, count, marshal(name)) # weird api end # Return a range of values from +start_index+ to +end_index+. Can also use # the familiar list[start,end] Ruby syntax. Redis: LRANGE def range(start_index, end_index) redis.lrange(key, start_index, end_index).map{|v| unmarshal(v) } end # Return the value at the given index. Can also use familiar list[index] syntax. # Redis: LINDEX def at(index) unmarshal redis.lindex(key, index) end # Return the first element in the list. Redis: LINDEX(0) def first at(0) end # Return the last element in the list. Redis: LINDEX(-1) def last at(-1) end # Return the length of the list. Aliased as size. Redis: LLEN def length redis.llen(key) end alias_method :size, :length # Returns true if there are no elements in the list. Redis: LLEN == 0 def empty? length == 0 end def ==(x) values == x end def to_s values.join(', ') end end end