lib/redis.rb in redis-0.1.2 vs lib/redis.rb in redis-0.2.0
- old
+ new
@@ -1,373 +1,23 @@
require 'socket'
-require File.join(File.dirname(__FILE__),'pipeline')
+module RedisRb
+ VERSION = "0.2.0"
+end
+
begin
if RUBY_VERSION >= '1.9'
require 'timeout'
- RedisTimer = Timeout
+ RedisRb::RedisTimer = Timeout
else
require 'system_timer'
- RedisTimer = SystemTimer
+ RedisRb::RedisTimer = SystemTimer
end
rescue LoadError
- RedisTimer = nil
+ RedisRb::RedisTimer = nil
end
-class Redis
- OK = "OK".freeze
- MINUS = "-".freeze
- PLUS = "+".freeze
- COLON = ":".freeze
- DOLLAR = "$".freeze
- ASTERISK = "*".freeze
+require 'redis/client'
+require 'redis/pipeline'
- BULK_COMMANDS = {
- "set" => true,
- "setnx" => true,
- "rpush" => true,
- "lpush" => true,
- "lset" => true,
- "lrem" => true,
- "sadd" => true,
- "srem" => true,
- "sismember" => true,
- "echo" => true,
- "getset" => true,
- "smove" => true,
- "zadd" => true,
- "zincrby" => true,
- "zrem" => true,
- "zscore" => true
- }
-
- MULTI_BULK_COMMANDS = {
- "mset" => true,
- "msetnx" => true
- }
-
- BOOLEAN_PROCESSOR = lambda{|r| r == 1 }
-
- REPLY_PROCESSOR = {
- "exists" => BOOLEAN_PROCESSOR,
- "sismember" => BOOLEAN_PROCESSOR,
- "sadd" => BOOLEAN_PROCESSOR,
- "srem" => BOOLEAN_PROCESSOR,
- "smove" => BOOLEAN_PROCESSOR,
- "zadd" => BOOLEAN_PROCESSOR,
- "zrem" => BOOLEAN_PROCESSOR,
- "move" => BOOLEAN_PROCESSOR,
- "setnx" => BOOLEAN_PROCESSOR,
- "del" => BOOLEAN_PROCESSOR,
- "renamenx" => BOOLEAN_PROCESSOR,
- "expire" => BOOLEAN_PROCESSOR,
- "keys" => lambda{|r| r.split(" ")},
- "info" => lambda{|r|
- info = {}
- r.each_line {|kv|
- k,v = kv.split(":",2).map{|x| x.chomp}
- info[k.to_sym] = v
- }
- info
- }
- }
-
- ALIASES = {
- "flush_db" => "flushdb",
- "flush_all" => "flushall",
- "last_save" => "lastsave",
- "key?" => "exists",
- "delete" => "del",
- "randkey" => "randomkey",
- "list_length" => "llen",
- "push_tail" => "rpush",
- "push_head" => "lpush",
- "pop_tail" => "rpop",
- "pop_head" => "lpop",
- "list_set" => "lset",
- "list_range" => "lrange",
- "list_trim" => "ltrim",
- "list_index" => "lindex",
- "list_rm" => "lrem",
- "set_add" => "sadd",
- "set_delete" => "srem",
- "set_count" => "scard",
- "set_member?" => "sismember",
- "set_members" => "smembers",
- "set_intersect" => "sinter",
- "set_intersect_store" => "sinterstore",
- "set_inter_store" => "sinterstore",
- "set_union" => "sunion",
- "set_union_store" => "sunionstore",
- "set_diff" => "sdiff",
- "set_diff_store" => "sdiffstore",
- "set_move" => "smove",
- "set_unless_exists" => "setnx",
- "rename_unless_exists" => "renamenx",
- "type?" => "type",
- "zset_add" => "zadd",
- "zset_count" => "zcard",
- "zset_range_by_score" => "zrangebyscore",
- "zset_reverse_range" => "zrevrange",
- "zset_range" => "zrange",
- "zset_delete" => "zrem",
- "zset_score" => "zscore",
- "zset_incr_by" => "zincrby",
- "zset_increment_by" => "zincrby"
- }
-
- DISABLED_COMMANDS = {
- "monitor" => true,
- "sync" => true
- }
-
- def initialize(options = {})
- @host = options[:host] || '127.0.0.1'
- @port = (options[:port] || 6379).to_i
- @db = (options[:db] || 0).to_i
- @timeout = (options[:timeout] || 5).to_i
- @password = options[:password]
- @logger = options[:logger]
- @thread_safe = options[:thread_safe]
- @mutex = Mutex.new if @thread_safe
- @sock = nil
-
- @logger.info { self.to_s } if @logger
- end
-
- def to_s
- "Redis Client connected to #{server} against DB #{@db}"
- end
-
- def server
- "#{@host}:#{@port}"
- end
-
- def connect_to_server
- @sock = connect_to(@host, @port, @timeout == 0 ? nil : @timeout)
- call_command(["auth",@password]) if @password
- call_command(["select",@db]) unless @db == 0
- end
-
- def connect_to(host, port, timeout=nil)
- # We support connect() timeout only if system_timer is availabe
- # or if we are running against Ruby >= 1.9
- # Timeout reading from the socket instead will be supported anyway.
- if @timeout != 0 and RedisTimer
- begin
- sock = TCPSocket.new(host, port)
- rescue Timeout::Error
- @sock = nil
- raise Timeout::Error, "Timeout connecting to the server"
- end
- else
- sock = TCPSocket.new(host, port)
- end
- sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
-
- # If the timeout is set we set the low level socket options in order
- # to make sure a blocking read will return after the specified number
- # of seconds. This hack is from memcached ruby client.
- if timeout
- secs = Integer(timeout)
- usecs = Integer((timeout - secs) * 1_000_000)
- optval = [secs, usecs].pack("l_2")
- begin
- sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
- sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
- rescue Exception => ex
- # Solaris, for one, does not like/support socket timeouts.
- @logger.info "Unable to use raw socket timeouts: #{ex.class.name}: #{ex.message}" if @logger
- end
- end
- sock
- end
-
- def method_missing(*argv)
- call_command(argv)
- end
-
- def call_command(argv)
- @logger.debug { argv.inspect } if @logger
-
- # this wrapper to raw_call_command handle reconnection on socket
- # error. We try to reconnect just one time, otherwise let the error
- # araise.
- connect_to_server if !@sock
-
- begin
- raw_call_command(argv.dup)
- rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED
- @sock.close rescue nil
- @sock = nil
- connect_to_server
- raw_call_command(argv.dup)
- end
- end
-
- def raw_call_command(argvp)
- pipeline = argvp[0].is_a?(Array)
-
- unless pipeline
- argvv = [argvp]
- else
- argvv = argvp
- end
-
- if MULTI_BULK_COMMANDS[argvv.flatten[0].to_s]
- # TODO improve this code
- argvp = argvv.flatten
- values = argvp.pop.to_a.flatten
- argvp = values.unshift(argvp[0])
- command = ["*#{argvp.size}"]
- argvp.each do |v|
- v = v.to_s
- command << "$#{get_size(v)}"
- command << v
- end
- command = command.map {|cmd| "#{cmd}\r\n"}.join
- else
- command = ""
- argvv.each do |argv|
- bulk = nil
- argv[0] = argv[0].to_s.downcase
- argv[0] = ALIASES[argv[0]] if ALIASES[argv[0]]
- raise "#{argv[0]} command is disabled" if DISABLED_COMMANDS[argv[0]]
- if BULK_COMMANDS[argv[0]] and argv.length > 1
- bulk = argv[-1].to_s
- argv[-1] = get_size(bulk)
- end
- command << "#{argv.join(' ')}\r\n"
- command << "#{bulk}\r\n" if bulk
- end
- end
- results = maybe_lock { process_command(command, argvv) }
-
- return pipeline ? results : results[0]
- end
-
- def process_command(command, argvv)
- @sock.write(command)
- argvv.map do |argv|
- processor = REPLY_PROCESSOR[argv[0]]
- processor ? processor.call(read_reply) : read_reply
- end
- end
-
- def maybe_lock(&block)
- if @thread_safe
- @mutex.synchronize &block
- else
- block.call
- end
- end
-
- def select(*args)
- raise "SELECT not allowed, use the :db option when creating the object"
- end
-
- def [](key)
- self.get(key)
- end
-
- def []=(key,value)
- set(key,value)
- end
-
- def set(key, value, expiry=nil)
- s = call_command([:set, key, value]) == OK
- expire(key, expiry) if s && expiry
- s
- end
-
- def sort(key, options = {})
- cmd = ["SORT"]
- cmd << key
- cmd << "BY #{options[:by]}" if options[:by]
- cmd << "GET #{[options[:get]].flatten * ' GET '}" if options[:get]
- cmd << "#{options[:order]}" if options[:order]
- cmd << "LIMIT #{options[:limit].join(' ')}" if options[:limit]
- call_command(cmd)
- end
-
- def incr(key, increment = nil)
- call_command(increment ? ["incrby",key,increment] : ["incr",key])
- end
-
- def decr(key,decrement = nil)
- call_command(decrement ? ["decrby",key,decrement] : ["decr",key])
- end
-
- # Similar to memcache.rb's #get_multi, returns a hash mapping
- # keys to values.
- def mapped_mget(*keys)
- result = {}
- mget(*keys).each do |value|
- key = keys.shift
- result.merge!(key => value) unless value.nil?
- end
- result
- end
-
- # Ruby defines a now deprecated type method so we need to override it here
- # since it will never hit method_missing
- def type(key)
- call_command(['type', key])
- end
-
- def quit
- call_command(['quit'])
- rescue Errno::ECONNRESET
- end
-
- def pipelined(&block)
- pipeline = Pipeline.new self
- yield pipeline
- pipeline.execute
- end
-
- def read_reply
- # We read the first byte using read() mainly because gets() is
- # immune to raw socket timeouts.
- begin
- rtype = @sock.read(1)
- rescue Errno::EAGAIN
- # We want to make sure it reconnects on the next command after the
- # timeout. Otherwise the server may reply in the meantime leaving
- # the protocol in a desync status.
- @sock = nil
- raise Errno::EAGAIN, "Timeout reading from the socket"
- end
-
- raise Errno::ECONNRESET,"Connection lost" if !rtype
- line = @sock.gets
- case rtype
- when MINUS
- raise MINUS + line.strip
- when PLUS
- line.strip
- when COLON
- line.to_i
- when DOLLAR
- bulklen = line.to_i
- return nil if bulklen == -1
- data = @sock.read(bulklen)
- @sock.read(2) # CRLF
- data
- when ASTERISK
- objects = line.to_i
- return nil if bulklen == -1
- res = []
- objects.times {
- res << read_reply
- }
- res
- else
- raise "Protocol error, got '#{rtype}' as initial reply byte"
- end
- end
-
- private
- def get_size(string)
- string.respond_to?(:bytesize) ? string.bytesize : string.size
- end
-end
+# For backwards compatibility
+Redis = RedisRb::Client