]> git.saurik.com Git - redis.git/blob - client-libraries/ruby_2/rubyredis.rb
command postprocessing implemented into RubyRedis
[redis.git] / client-libraries / ruby_2 / rubyredis.rb
1 # RubyRedis is an alternative implementatin of Ruby client library written
2 # by Salvatore Sanfilippo.
3 #
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.
7
8 require 'socket'
9
10 class RedisClient
11 BulkCommands = {
12 "set"=>true, "setnx"=>true, "rpush"=>true, "lpush"=>true, "lset"=>true,
13 "lrem"=>true, "sadd"=>true, "srem"=>true, "sismember"=>true,
14 "echo"=>true, "getset"=>true, "smove"=>true
15 }
16
17 ConvertToBool = lambda{|r| r == 0 ? false : r}
18
19 ReplyProcessor = {
20 "exists" => ConvertToBool,
21 "sismember"=> ConvertToBool,
22 "sadd"=> ConvertToBool,
23 "srem"=> ConvertToBool,
24 "smove"=> ConvertToBool,
25 "move"=> ConvertToBool,
26 "setnx"=> ConvertToBool,
27 "del"=> ConvertToBool,
28 "renamenx"=> ConvertToBool,
29 "expire"=> ConvertToBool,
30 "keys" => lambda{|r| r.split(" ")},
31 "info" => lambda{|r|
32 info = {}
33 r.each_line {|kv|
34 k,v = kv.split(':', 2)
35 k,v = k.chomp, v = v.chomp
36 info[k.to_sym] = v
37 }
38 info
39 }
40 }
41
42 def convert_to_bool(r)
43 r == 0 ? false : r
44 end
45
46 def initialize(opts={})
47 opts = {:host => 'localhost', :port => '6379', :db => 0}.merge(opts)
48 @host = opts[:host]
49 @port = opts[:port]
50 @db = opts[:db]
51 connect_to_server
52 end
53
54 def to_s
55 "Redis Client connected to #{@host}:#{@port} against DB #{@db}"
56 end
57
58 def connect_to_server
59 @sock = TCPSocket.new(@host, @port, 0)
60 call_command(["select",@db]) if @db != 0
61 end
62
63 def method_missing(*argv)
64 call_command(argv)
65 end
66
67 def call_command(argv)
68 # this wrapper to raw_call_command handle reconnection on socket
69 # error. We try to reconnect just one time, otherwise let the error
70 # araise.
71 begin
72 raw_call_command(argv)
73 rescue Errno::ECONNRESET
74 @sock.close
75 connect_to_server
76 raw_call_command(argv)
77 end
78 end
79
80 def raw_call_command(argv)
81 bulk = nil
82 argv[0] = argv[0].to_s.downcase
83 if BulkCommands[argv[0]]
84 bulk = argv[-1].to_s
85 argv[-1] = bulk.length
86 end
87 @sock.write(argv.join(" ")+"\r\n")
88 @sock.write(bulk+"\r\n") if bulk
89
90 # Post process the reply if needed
91 processor = ReplyProcessor[argv[0]]
92 processor ? processor.call(read_reply) : read_reply
93 end
94
95 def select(*args)
96 raise "SELECT not allowed, use the :db option when creating the object"
97 end
98
99 def [](key)
100 get(key)
101 end
102
103 def []=(key,value)
104 set(key,value)
105 end
106
107 def read_reply
108 line = @sock.gets
109 raise Errno::ECONNRESET,"Connection lost" if !line
110 case line[0..0]
111 when "-"
112 raise line.strip
113 when "+"
114 line[1..-1].strip
115 when ":"
116 line[1..-1].to_i
117 when "$"
118 bulklen = line[1..-1].to_i
119 return nil if bulklen == -1
120 data = @sock.read(bulklen)
121 @sock.read(2) # CRLF
122 data
123 when "*"
124 objects = line[1..-1].to_i
125 return nil if bulklen == -1
126 res = []
127 objects.times {
128 res << read_reply
129 }
130 res
131 end
132 end
133 end