]>
git.saurik.com Git - redis.git/blob - client-libraries/ruby_2/rubyredis.rb
ae572bcb597f91863a8929094c712a80584e36b9
   1 # RubyRedis is an alternative implementatin of Ruby client library written 
   2 # by Salvatore Sanfilippo. 
   4 # The aim of this library is to create an alternative client library that is 
   5 # much simpler and does not implement every command explicitly but uses 
   6 # method_missing instead. 
  12     if (RUBY_VERSION >= '1.9') 
  16         require 'system_timer' 
  17         RedisTimer 
= SystemTimer
 
  25         "set"=>true, "setnx"=>true, "rpush"=>true, "lpush"=>true, "lset"=>true, 
  26         "lrem"=>true, "sadd"=>true, "srem"=>true, "sismember"=>true, 
  27         "echo"=>true, "getset"=>true, "smove"=>true 
  30     ConvertToBool 
= lambda
{|r
| r 
== 0 ? false : r
} 
  31     ConvertToSet 
= lambda
{|r
| Set
.new(r
)} 
  34         "exists" => ConvertToBool
, 
  35         "sismember"=> ConvertToBool
, 
  36         "sadd"=> ConvertToBool
, 
  37         "srem"=> ConvertToBool
, 
  38         "smove"=> ConvertToBool
, 
  39         "move"=> ConvertToBool
, 
  40         "setnx"=> ConvertToBool
, 
  41         "del"=> ConvertToBool
, 
  42         "renamenx"=> ConvertToBool
, 
  43         "expire"=> ConvertToBool
, 
  44         "smembers" => ConvertToSet
, 
  45         "sinter" => ConvertToSet
, 
  46         "sunion" => ConvertToSet
, 
  47         "sdiff" => ConvertToSet
, 
  48         "keys" => lambda
{|r
| r
.split(" ")}, 
  52                 k
,v 
= kv
.split(":",2).map
{|x
| x
.chomp
} 
  60         "flush_db" => "flushdb", 
  61         "flush_all" => "flushall", 
  62         "last_save" => "lastsave", 
  65         "randkey" => "randomkey", 
  66         "list_length" => "llen", 
  67         "push_tail" => "rpush", 
  68         "push_head" => "lpush", 
  72         "list_range" => "lrange", 
  73         "list_trim" => "ltrim", 
  74         "list_index" => "lindex", 
  77         "set_delete" => "srem", 
  78         "set_count" => "scard", 
  79         "set_member?" => "sismember", 
  80         "set_members" => "smembers", 
  81         "set_intersect" => "sinter", 
  82         "set_intersect_store" => "sinterstore", 
  83         "set_inter_store" => "sinterstore", 
  84         "set_union" => "sunion", 
  85         "set_union_store" => "sunionstore", 
  86         "set_diff" => "sdiff", 
  87         "set_diff_store" => "sdiffstore", 
  88         "set_move" => "smove", 
  89         "set_unless_exists" => "setnx", 
  90         "rename_unless_exists" => "renamenx" 
  93     def initialize(opts
={}) 
  94         @host = opts
[:host] || '127.0.0.1' 
  95         @port = opts
[:port] || 6379 
  97         @timeout = opts
[:timeout] || 0 
 102         "Redis Client connected to #{@host}:#{@port} against DB #{@db}" 
 105     def connect_to_server
 
 106         @sock = connect_to(@host,@port,@timeout == 0 ? nil : @timeout) 
 107         call_command(["select",@db]) if @db !
= 0 
 110     def connect_to(host
, port
, timeout
=nil) 
 111         # We support connect() timeout only if system_timer is availabe 
 112         # or if we are running against Ruby >= 1.9 
 113         # Timeout reading from the socket instead will be supported anyway. 
 114         if @timeout !
= 0 and RedisTimer
 
 116                 sock 
= TCPSocket
.new(host
, port
, 0) 
 117             rescue Timeout
::Error 
 119                 raise Timeout
::Error, "Timeout connecting to the server" 
 122             sock 
= TCPSocket
.new(host
, port
, 0) 
 125         # If the timeout is set we set the low level socket options in order 
 126         # to make sure a blocking read will return after the specified number 
 127         # of seconds. This hack is from memcached ruby client. 
 129             secs 
= Integer(timeout
) 
 130             usecs 
= Integer((timeout 
- secs
) * 1_000_000) 
 131             optval 
= [secs
, usecs
].pack("l_2") 
 132             sock
.setsockopt Socket
::SOL_SOCKET, Socket
::SO_RCVTIMEO, optval
 
 133             sock
.setsockopt Socket
::SOL_SOCKET, Socket
::SO_SNDTIMEO, optval
 
 138     def method_missing(*argv) 
 142     def call_command(argv) 
 143         # this wrapper to raw_call_command handle reconnection on socket 
 144         # error. We try to reconnect just one time, otherwise let the error 
 146         connect_to_server 
if !
@sock 
 148             raw_call_command(argv) 
 149         rescue Errno
::ECONNRESET 
 152             raw_call_command(argv) 
 156     def raw_call_command(argv) 
 158         argv[0] = argv[0].to_s
.downcase
 
 159         argv[0] = Aliases
[argv[0]] if Aliases
[argv[0]] 
 160         if BulkCommands
[argv[0]] 
 162             argv[-1] = bulk
.length
 
 164         @sock.write(argv.join(" ")+
"\r\n") 
 165         @sock.write(bulk+
"\r\n") if bulk
 
 167         # Post process the reply if needed 
 168         processor 
= ReplyProcessor
[argv[0]] 
 169         processor 
? processor
.call(read_reply
) : read_reply
 
 173         raise "SELECT not allowed, use the :db option when creating the object" 
 184     def sort(key
, opts
={}) 
 187         cmd 
<< "BY #{opts[:by]}" if opts
[:by] 
 188         cmd 
<< "GET #{[opts[:get]].flatten * ' GET '}" if opts
[:get] 
 189         cmd 
<< "#{opts[:order]}" if opts
[:order] 
 190         cmd 
<< "LIMIT #{opts[:limit].join(' ')}" if opts
[:limit] 
 194     def incr(key
,increment
=nil) 
 195         call_command(increment 
? ["incrby",key
,increment
] :  ["incr",key
]) 
 198     def decr(key
,decrement
=nil) 
 199         call_command(decrement 
? ["decrby",key
,decrement
] :  ["decr",key
]) 
 203         # We read the first byte using read() mainly because gets() is 
 204         # immune to raw socket timeouts. 
 206             rtype 
= @sock.read(1) 
 208             # We want to make sure it reconnects on the next command after the 
 209             # timeout. Otherwise the server may reply in the meantime leaving 
 210             # the protocol in a desync status. 
 212             raise Errno
::EAGAIN, "Timeout reading from the socket" 
 215         raise Errno
::ECONNRESET,"Connection lost" if !rtype
 
 226             return nil if bulklen == -1 
 227             data = @sock.read(bulklen) 
 232             return nil if bulklen == -1 
 239             raise "Protocol error
, got 
'#{rtype}' as initial reply bye
"