ezmobius-redis-rb 0.0.3 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,7 +12,10 @@ See [redis on code.google.com](http://code.google.com/p/redis/wiki/README) for m
12
12
 
13
13
  ## Dependencies
14
14
 
15
- 1. redis -
15
+ 1. rspec -
16
+ sudo gem install rspec
17
+
18
+ 2. redis -
16
19
 
17
20
  rake redis:install
18
21
 
@@ -20,7 +23,7 @@ See [redis on code.google.com](http://code.google.com/p/redis/wiki/README) for m
20
23
 
21
24
  rake dtach:install
22
25
 
23
- 3. svn - git is the new black, but we need it for the google codes.
26
+ 3. git - git is the new black.
24
27
 
25
28
  ## Setup
26
29
 
data/Rakefile CHANGED
@@ -6,9 +6,10 @@ require 'spec/rake/spectask'
6
6
  require 'tasks/redis.tasks'
7
7
 
8
8
 
9
- GEM = 'redis'
10
- GEM_VERSION = '0.0.3'
11
- AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley']
9
+ GEM = 'redis-rb'
10
+ GEM_NAME = 'redis'
11
+ GEM_VERSION = '0.1'
12
+ AUTHORS = ['Ezra Zygmuntowicz', 'Taylor Weibley', 'Matthew Clark', 'Brian McKinney', 'Salvatore Sanfilippo', 'Luca Guidi']
12
13
  EMAIL = "ez@engineyard.com"
13
14
  HOMEPAGE = "http://github.com/ezmobius/redis-rb"
14
15
  SUMMARY = "Ruby client library for redis key value storage server"
@@ -24,10 +25,7 @@ spec = Gem::Specification.new do |s|
24
25
  s.authors = AUTHORS
25
26
  s.email = EMAIL
26
27
  s.homepage = HOMEPAGE
27
-
28
- # Uncomment this to add a dependency
29
- # s.add_dependency "foo"
30
-
28
+ s.add_dependency "rspec"
31
29
  s.require_path = 'lib'
32
30
  s.autorequire = GEM
33
31
  s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{lib,spec}/**/*")
@@ -55,4 +53,10 @@ task :make_spec do
55
53
  File.open("#{GEM}.gemspec", "w") do |file|
56
54
  file.puts spec.to_ruby
57
55
  end
58
- end
56
+ end
57
+
58
+ desc "Run all examples with RCov"
59
+ Spec::Rake::SpecTask.new(:rcov) do |t|
60
+ t.spec_files = FileList['spec/**/*_spec.rb']
61
+ t.rcov = true
62
+ end
@@ -2,13 +2,20 @@ require 'redis'
2
2
  require 'hash_ring'
3
3
  class DistRedis
4
4
  attr_reader :ring
5
- def initialize(*servers)
6
- srvs = []
7
- servers.each do |s|
8
- server, port = s.split(':')
9
- srvs << Redis.new(:host => server, :port => port)
5
+ def initialize(opts={})
6
+ hosts = []
7
+
8
+ db = opts[:db] || nil
9
+ timeout = opts[:timeout] || nil
10
+
11
+ raise Error, "No hosts given" unless opts[:hosts]
12
+
13
+ opts[:hosts].each do |h|
14
+ host, port = h.split(':')
15
+ hosts << Redis.new(:host => host, :port => port, :db => db, :timeout => timeout, :db => db)
10
16
  end
11
- @ring = HashRing.new srvs
17
+
18
+ @ring = HashRing.new hosts
12
19
  end
13
20
 
14
21
  def node_for_key(key)
@@ -0,0 +1,22 @@
1
+ require "redis"
2
+
3
+ class Redis
4
+ class Pipeline < Redis
5
+ BUFFER_SIZE = 50_000
6
+
7
+ def initialize(redis)
8
+ @redis = redis
9
+ @commands = []
10
+ end
11
+
12
+ def call_command(command)
13
+ @commands << command
14
+ end
15
+
16
+ def execute
17
+ @redis.call_command(@commands)
18
+ @commands.clear
19
+ end
20
+
21
+ end
22
+ end
@@ -1,472 +1,297 @@
1
1
  require 'socket'
2
- require 'set'
3
- require File.join(File.dirname(__FILE__),'server')
2
+ require File.join(File.dirname(__FILE__),'pipeline')
4
3
 
5
-
6
- class RedisError < StandardError
7
- end
8
- class RedisRenameError < StandardError
4
+ begin
5
+ if RUBY_VERSION >= '1.9'
6
+ require 'timeout'
7
+ RedisTimer = Timeout
8
+ else
9
+ require 'system_timer'
10
+ RedisTimer = SystemTimer
11
+ end
12
+ rescue LoadError
13
+ RedisTimer = nil
9
14
  end
15
+
10
16
  class Redis
11
- ERR = "-".freeze
12
- OK = 'OK'.freeze
13
- SINGLE = '+'.freeze
14
- BULK = '$'.freeze
15
- MULTI = '*'.freeze
16
- INT = ':'.freeze
17
-
18
- attr_reader :server
19
-
20
-
21
- def initialize(opts={})
22
- @opts = {:host => 'localhost', :port => '6379', :db => 0}.merge(opts)
23
- $debug = @opts[:debug]
24
- @db = @opts[:db]
25
- @server = Server.new(@opts[:host], @opts[:port])
26
- end
27
-
28
- def to_s
29
- "#{host}:#{port}"
30
- end
31
-
32
- def port
33
- @opts[:port]
34
- end
35
-
36
- def host
37
- @opts[:host]
38
- end
39
-
40
- def with_socket_management(server, &block)
41
- begin
42
- block.call(server.socket)
43
- #Timeout or server down
44
- rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNREFUSED => e
45
- server.close
46
- puts "Client (#{server.inspect}) disconnected from server: #{e.inspect}\n" if $debug
47
- retry
48
- #Server down
49
- rescue NoMethodError => e
50
- puts "Client (#{server.inspect}) tryin server that is down: #{e.inspect}\n Dying!" if $debug
51
- raise Errno::ECONNREFUSED
52
- #exit
53
- end
54
- end
17
+ OK = "OK".freeze
18
+ MINUS = "-".freeze
19
+ PLUS = "+".freeze
20
+ COLON = ":".freeze
21
+ DOLLAR = "$".freeze
22
+ ASTERISK = "*".freeze
55
23
 
56
- def monitor
57
- with_socket_management(@server) do |socket|
58
- trap("INT") { puts "\nGot ^C! Dying!"; exit }
59
- write "MONITOR\r\n"
60
- puts "Now Monitoring..."
61
- socket.read(12)
62
- loop do
63
- x = socket.gets
64
- puts x unless x.nil?
65
- end
66
- end
67
- end
24
+ BULK_COMMANDS = {
25
+ "set" => true,
26
+ "setnx" => true,
27
+ "rpush" => true,
28
+ "lpush" => true,
29
+ "lset" => true,
30
+ "lrem" => true,
31
+ "sadd" => true,
32
+ "srem" => true,
33
+ "sismember" => true,
34
+ "echo" => true,
35
+ "getset" => true,
36
+ "smove" => true
37
+ }
68
38
 
69
- def quit
70
- write "QUIT\r\n"
71
- end
72
-
73
- def select_db(index)
74
- @db = index
75
- write "SELECT #{index}\r\n"
76
- get_response
77
- end
78
-
79
- def flush_db
80
- write "FLUSHDB\r\n"
81
- get_response == OK
82
- end
83
-
84
- def flush_all
85
- ensure_retry do
86
- puts "Warning!\nFlushing *ALL* databases!\n5 Seconds to Hit ^C!"
87
- trap('INT') {quit; return false}
88
- sleep 5
89
- write "FLUSHALL\r\n"
90
- get_response == OK
91
- end
92
- end
39
+ BOOLEAN_PROCESSOR = lambda{|r| r == 0 ? false : r}
93
40
 
94
- def last_save
95
- write "LASTSAVE\r\n"
96
- get_response.to_i
97
- end
98
-
99
- def bgsave
100
- write "BGSAVE\r\n"
101
- get_response == OK
102
- end
103
-
104
- def info
105
- info = {}
106
- write("INFO\r\n")
107
- x = get_response
108
- x.each do |kv|
109
- k,v = kv.split(':', 2)
110
- k,v = k.chomp, v = v.chomp
111
- info[k.to_sym] = v
112
- end
113
- info
114
- end
115
-
116
-
117
- def bulk_reply
118
- begin
119
- x = read.chomp
120
- puts "bulk_reply read value is #{x.inspect}" if $debug
121
- return x
122
- rescue => e
123
- puts "error in bulk_reply #{e}" if $debug
124
- nil
125
- end
126
- end
127
-
128
- def write(data)
129
- with_socket_management(@server) do |socket|
130
- puts "writing: #{data}" if $debug
131
- socket.write(data)
132
- end
133
- end
134
-
135
- def fetch(len)
136
- with_socket_management(@server) do |socket|
137
- len = [0, len.to_i].max
138
- res = socket.read(len + 2)
139
- res = res.chomp if res
140
- res
141
- end
142
- end
143
-
144
- def read(length = read_proto)
145
- with_socket_management(@server) do |socket|
146
- res = socket.read(length)
147
- puts "read is #{res.inspect}" if $debug
148
- res
149
- end
150
- end
41
+ REPLY_PROCESSOR = {
42
+ "exists" => BOOLEAN_PROCESSOR,
43
+ "sismember" => BOOLEAN_PROCESSOR,
44
+ "sadd" => BOOLEAN_PROCESSOR,
45
+ "srem" => BOOLEAN_PROCESSOR,
46
+ "smove" => BOOLEAN_PROCESSOR,
47
+ "move" => BOOLEAN_PROCESSOR,
48
+ "setnx" => BOOLEAN_PROCESSOR,
49
+ "del" => BOOLEAN_PROCESSOR,
50
+ "renamenx" => BOOLEAN_PROCESSOR,
51
+ "expire" => BOOLEAN_PROCESSOR,
52
+ "keys" => lambda{|r| r.split(" ")},
53
+ "info" => lambda{|r|
54
+ info = {}
55
+ r.each_line {|kv|
56
+ k,v = kv.split(":",2).map{|x| x.chomp}
57
+ info[k.to_sym] = v
58
+ }
59
+ info
60
+ }
61
+ }
151
62
 
152
- def keys(glob)
153
- write "KEYS #{glob}\r\n"
154
- get_response.split(' ')
155
- end
63
+ ALIASES = {
64
+ "flush_db" => "flushdb",
65
+ "flush_all" => "flushall",
66
+ "last_save" => "lastsave",
67
+ "key?" => "exists",
68
+ "delete" => "del",
69
+ "randkey" => "randomkey",
70
+ "list_length" => "llen",
71
+ "push_tail" => "rpush",
72
+ "push_head" => "lpush",
73
+ "pop_tail" => "rpop",
74
+ "pop_head" => "lpop",
75
+ "list_set" => "lset",
76
+ "list_range" => "lrange",
77
+ "list_trim" => "ltrim",
78
+ "list_index" => "lindex",
79
+ "list_rm" => "lrem",
80
+ "set_add" => "sadd",
81
+ "set_delete" => "srem",
82
+ "set_count" => "scard",
83
+ "set_member?" => "sismember",
84
+ "set_members" => "smembers",
85
+ "set_intersect" => "sinter",
86
+ "set_intersect_store" => "sinterstore",
87
+ "set_inter_store" => "sinterstore",
88
+ "set_union" => "sunion",
89
+ "set_union_store" => "sunionstore",
90
+ "set_diff" => "sdiff",
91
+ "set_diff_store" => "sdiffstore",
92
+ "set_move" => "smove",
93
+ "set_unless_exists" => "setnx",
94
+ "rename_unless_exists" => "renamenx",
95
+ "type?" => "type"
96
+ }
156
97
 
157
- def rename!(oldkey, newkey)
158
- write "RENAME #{oldkey} #{newkey}\r\n"
159
- get_response
160
- end
161
-
162
- def rename(oldkey, newkey)
163
- write "RENAMENX #{oldkey} #{newkey}\r\n"
164
- case get_response
165
- when -1
166
- raise RedisRenameError, "source key: #{oldkey} does not exist"
167
- when 0
168
- raise RedisRenameError, "target key: #{oldkey} already exists"
169
- when -3
170
- raise RedisRenameError, "source and destination keys are the same"
171
- when 1
172
- true
173
- end
174
- end
175
-
176
- def key?(key)
177
- write "EXISTS #{key}\r\n"
178
- get_response == 1
179
- end
180
-
181
- def delete(key)
182
- write "DEL #{key}\r\n"
183
- get_response == 1
184
- end
185
-
186
- def [](key)
187
- get(key)
188
- end
98
+ DISABLED_COMMANDS = {
99
+ "monitor" => true,
100
+ "sync" => true
101
+ }
189
102
 
190
- def get(key)
191
- write "GET #{key}\r\n"
192
- get_response
193
- end
194
-
195
- def mget(*keys)
196
- write "MGET #{keys.join(' ')}\r\n"
197
- get_response
103
+ def initialize(options = {})
104
+ @host = options[:host] || '127.0.0.1'
105
+ @port = (options[:port] || 6379).to_i
106
+ @db = (options[:db] || 0).to_i
107
+ @timeout = (options[:timeout] || 5).to_i
108
+ $debug = options[:debug]
109
+ connect_to_server
198
110
  end
199
111
 
200
- def incr(key, increment=nil)
201
- if increment
202
- write "INCRBY #{key} #{increment}\r\n"
203
- else
204
- write "INCR #{key}\r\n"
205
- end
206
- get_response
112
+ def to_s
113
+ "Redis Client connected to #{@host}:#{@port} against DB #{@db}"
207
114
  end
208
115
 
209
- def decr(key, decrement=nil)
210
- if decrement
211
- write "DECRBY #{key} #{decrement}\r\n"
212
- else
213
- write "DECR #{key}\r\n"
214
- end
215
- get_response
216
- end
217
-
218
- def randkey
219
- write "RANDOMKEY\r\n"
220
- get_response
116
+ def connect_to_server
117
+ @sock = connect_to(@host, @port, @timeout == 0 ? nil : @timeout)
118
+ call_command(["select",@db]) unless @db == 0
221
119
  end
222
120
 
223
- def list_length(key)
224
- write "LLEN #{key}\r\n"
225
- case i = get_response
226
- when -2
227
- raise RedisError, "key: #{key} does not hold a list value"
121
+ def connect_to(host, port, timeout=nil)
122
+ # We support connect() timeout only if system_timer is availabe
123
+ # or if we are running against Ruby >= 1.9
124
+ # Timeout reading from the socket instead will be supported anyway.
125
+ if @timeout != 0 and RedisTimer
126
+ begin
127
+ sock = TCPSocket.new(host, port)
128
+ rescue Timeout::Error
129
+ @sock = nil
130
+ raise Timeout::Error, "Timeout connecting to the server"
131
+ end
228
132
  else
229
- i
133
+ sock = TCPSocket.new(host, port)
230
134
  end
231
- end
135
+ sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
232
136
 
233
- def type?(key)
234
- write "TYPE #{key}\r\n"
235
- get_response
236
- end
237
-
238
- def push_tail(key, string)
239
- write "RPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
240
- get_response
241
- end
242
-
243
- def push_head(key, string)
244
- write "LPUSH #{key} #{string.to_s.size}\r\n#{string.to_s}\r\n"
245
- get_response
246
- end
247
-
248
- def pop_head(key)
249
- write "LPOP #{key}\r\n"
250
- get_response
137
+ # If the timeout is set we set the low level socket options in order
138
+ # to make sure a blocking read will return after the specified number
139
+ # of seconds. This hack is from memcached ruby client.
140
+ if timeout
141
+ secs = Integer(timeout)
142
+ usecs = Integer((timeout - secs) * 1_000_000)
143
+ optval = [secs, usecs].pack("l_2")
144
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
145
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
146
+ end
147
+ sock
251
148
  end
252
149
 
253
- def pop_tail(key)
254
- write "RPOP #{key}\r\n"
255
- get_response
256
- end
257
-
258
- def list_set(key, index, val)
259
- write "LSET #{key} #{index} #{val.to_s.size}\r\n#{val}\r\n"
260
- get_response == OK
150
+ def method_missing(*argv)
151
+ call_command(argv)
261
152
  end
262
153
 
263
- def list_length(key)
264
- write "LLEN #{key}\r\n"
265
- case i = get_response
266
- when -2
267
- raise RedisError, "key: #{key} does not hold a list value"
268
- else
269
- i
154
+ def call_command(argv)
155
+ puts argv.inspect if $debug
156
+ # this wrapper to raw_call_command handle reconnection on socket
157
+ # error. We try to reconnect just one time, otherwise let the error
158
+ # araise.
159
+ connect_to_server if !@sock
160
+ begin
161
+ raw_call_command(argv.dup)
162
+ rescue Errno::ECONNRESET, Errno::EPIPE
163
+ @sock.close
164
+ @sock = nil
165
+ connect_to_server
166
+ raw_call_command(argv.dup)
270
167
  end
271
168
  end
272
169
 
273
- def list_range(key, start, ending)
274
- write "LRANGE #{key} #{start} #{ending}\r\n"
275
- get_response
276
- end
170
+ def raw_call_command(argvp)
171
+ pipeline = argvp[0].is_a?(Array)
277
172
 
278
- def list_trim(key, start, ending)
279
- write "LTRIM #{key} #{start} #{ending}\r\n"
280
- get_response
281
- end
282
-
283
- def list_index(key, index)
284
- write "LINDEX #{key} #{index}\r\n"
285
- get_response
286
- end
287
-
288
- def list_rm(key, count, value)
289
- write "LREM #{key} #{count} #{value.to_s.size}\r\n#{value}\r\n"
290
- case num = get_response
291
- when -1
292
- raise RedisError, "key: #{key} does not exist"
293
- when -2
294
- raise RedisError, "key: #{key} does not hold a list value"
173
+ unless pipeline
174
+ argvv = [argvp]
295
175
  else
296
- num
297
- end
298
- end
299
-
300
- def set_add(key, member)
301
- write "SADD #{key} #{member.to_s.size}\r\n#{member}\r\n"
302
- case get_response
303
- when 1
304
- true
305
- when 0
306
- false
307
- when -2
308
- raise RedisError, "key: #{key} contains a non set value"
176
+ argvv = argvp
309
177
  end
310
- end
311
178
 
312
- def set_delete(key, member)
313
- write "SREM #{key} #{member.to_s.size}\r\n#{member}\r\n"
314
- case get_response
315
- when 1
316
- true
317
- when 0
318
- false
319
- when -2
320
- raise RedisError, "key: #{key} contains a non set value"
179
+ command = ''
180
+
181
+ argvv.each do |argv|
182
+ bulk = nil
183
+ argv[0] = argv[0].to_s.downcase
184
+ argv[0] = ALIASES[argv[0]] if ALIASES[argv[0]]
185
+ raise "#{argv[0]} command is disabled" if DISABLED_COMMANDS[argv[0]]
186
+ if BULK_COMMANDS[argv[0]] and argv.length > 1
187
+ bulk = argv[-1].to_s
188
+ argv[-1] = bulk.length
189
+ end
190
+ command << argv.join(' ') + "\r\n"
191
+ command << bulk + "\r\n" if bulk
321
192
  end
322
- end
323
193
 
324
- def set_count(key)
325
- write "SCARD #{key}\r\n"
326
- case i = get_response
327
- when -2
328
- raise RedisError, "key: #{key} contains a non set value"
329
- else
330
- i
194
+ @sock.write(command)
195
+
196
+ results = argvv.map do |argv|
197
+ processor = REPLY_PROCESSOR[argv[0]]
198
+ processor ? processor.call(read_reply) : read_reply
331
199
  end
200
+
201
+ return pipeline ? results : results[0]
332
202
  end
333
203
 
334
- def set_member?(key, member)
335
- write "SISMEMBER #{key} #{member.to_s.size}\r\n#{member}\r\n"
336
- case get_response
337
- when 1
338
- true
339
- when 0
340
- false
341
- when -2
342
- raise RedisError, "key: #{key} contains a non set value"
343
- end
204
+ def select(*args)
205
+ raise "SELECT not allowed, use the :db option when creating the object"
344
206
  end
345
207
 
346
- def set_members(key)
347
- write "SMEMBERS #{key}\r\n"
348
- Set.new(get_response)
208
+ def [](key)
209
+ get(key)
349
210
  end
350
211
 
351
- def set_intersect(*keys)
352
- write "SINTER #{keys.join(' ')}\r\n"
353
- Set.new(get_response)
212
+ def []=(key,value)
213
+ set(key,value)
354
214
  end
355
215
 
356
- def set_inter_store(destkey, *keys)
357
- write "SINTERSTORE #{destkey} #{keys.join(' ')}\r\n"
358
- get_response
216
+ def set(key, value, expiry=nil)
217
+ s = call_command([:set, key, value]) == OK
218
+ expire(key, expiry) if s && expiry
219
+ s
359
220
  end
360
221
 
361
- def sort(key, opts={})
362
- cmd = "SORT #{key}"
363
- cmd << " BY #{opts[:by]}" if opts[:by]
364
- cmd << " GET #{opts[:get]}" if opts[:get]
365
- cmd << " INCR #{opts[:incr]}" if opts[:incr]
366
- cmd << " DEL #{opts[:del]}" if opts[:del]
367
- cmd << " DECR #{opts[:decr]}" if opts[:decr]
368
- cmd << " #{opts[:order]}" if opts[:order]
369
- cmd << " LIMIT #{opts[:limit].join(' ')}" if opts[:limit]
370
- cmd << "\r\n"
371
- write(cmd)
372
- get_response
222
+ def sort(key, options = {})
223
+ cmd = []
224
+ cmd << "SORT #{key}"
225
+ cmd << "BY #{options[:by]}" if options[:by]
226
+ cmd << "GET #{[options[:get]].flatten * ' GET '}" if options[:get]
227
+ cmd << "#{options[:order]}" if options[:order]
228
+ cmd << "LIMIT #{options[:limit].join(' ')}" if options[:limit]
229
+ call_command(cmd)
373
230
  end
374
-
375
- def multi_bulk
376
- res = read_proto
377
- puts "mb res is #{res.inspect}" if $debug
378
- list = []
379
- Integer(res).times do
380
- vf = get_response
381
- puts "curren vf is #{vf.inspect}" if $debug
382
- list << vf
383
- puts "current list is #{list.inspect}" if $debug
384
- end
385
- list
231
+
232
+ def incr(key, increment = nil)
233
+ call_command(increment ? ["incrby",key,increment] : ["incr",key])
386
234
  end
387
-
388
- def get_reply
389
- begin
390
- r = read(1)
391
- raise RedisError if (r == "\r" || r == "\n")
392
- rescue RedisError
393
- retry
394
- end
395
- r
235
+
236
+ def decr(key,decrement = nil)
237
+ call_command(decrement ? ["decrby",key,decrement] : ["decr",key])
396
238
  end
397
-
398
- def []=(key, val)
399
- set(key,val)
239
+
240
+ # Ruby defines a now deprecated type method so we need to override it here
241
+ # since it will never hit method_missing
242
+ def type(key)
243
+ call_command(['type', key])
400
244
  end
401
-
402
245
 
403
- def set(key, val, expiry=nil)
404
- write("SET #{key} #{val.to_s.size}\r\n#{val}\r\n")
405
- get_response == OK
246
+ def quit
247
+ call_command(['quit'])
248
+ rescue Errno::ECONNRESET
406
249
  end
407
250
 
408
- def set_unless_exists(key, val)
409
- write "SETNX #{key} #{val.to_s.size}\r\n#{val}\r\n"
410
- get_response == 1
411
- end
412
-
413
- def status_code_reply
414
- begin
415
- res = read_proto
416
- if res.index('-') == 0
417
- raise RedisError, res
418
- else
419
- true
420
- end
421
- rescue RedisError
422
- raise RedisError
423
- end
251
+ def pipelined(&block)
252
+ pipeline = Pipeline.new self
253
+ yield pipeline
254
+ pipeline.execute
424
255
  end
425
-
426
- def get_response
256
+
257
+ def read_reply
258
+ # We read the first byte using read() mainly because gets() is
259
+ # immune to raw socket timeouts.
427
260
  begin
428
- rtype = get_reply
429
- rescue => e
430
- raise RedisError, e.inspect
261
+ rtype = @sock.read(1)
262
+ rescue Errno::EAGAIN
263
+ # We want to make sure it reconnects on the next command after the
264
+ # timeout. Otherwise the server may reply in the meantime leaving
265
+ # the protocol in a desync status.
266
+ @sock = nil
267
+ raise Errno::EAGAIN, "Timeout reading from the socket"
431
268
  end
432
- puts "reply_type is #{rtype.inspect}" if $debug
269
+
270
+ raise Errno::ECONNRESET,"Connection lost" if !rtype
271
+ line = @sock.gets
433
272
  case rtype
434
- when SINGLE
435
- single_line
436
- when BULK
437
- bulk_reply
438
- when MULTI
439
- multi_bulk
440
- when INT
441
- integer_reply
442
- when ERR
443
- raise RedisError, single_line
273
+ when MINUS
274
+ raise MINUS + line.strip
275
+ when PLUS
276
+ line.strip
277
+ when COLON
278
+ line.to_i
279
+ when DOLLAR
280
+ bulklen = line.to_i
281
+ return nil if bulklen == -1
282
+ data = @sock.read(bulklen)
283
+ @sock.read(2) # CRLF
284
+ data
285
+ when ASTERISK
286
+ objects = line.to_i
287
+ return nil if bulklen == -1
288
+ res = []
289
+ objects.times {
290
+ res << read_reply
291
+ }
292
+ res
444
293
  else
445
- raise RedisError, "Unknown response.."
446
- end
447
- end
448
-
449
- def integer_reply
450
- Integer(read_proto)
451
- end
452
-
453
- def single_line
454
- buff = ""
455
- while buff[-2..-1] != "\r\n"
456
- buff << read(1)
457
- end
458
- puts "single_line value is #{buff[0..-3].inspect}" if $debug
459
- buff[0..-3]
460
- end
461
-
462
- def read_proto
463
- with_socket_management(@server) do |socket|
464
- if res = socket.gets
465
- x = res.chomp
466
- puts "read_proto is #{x.inspect}\n\n" if $debug
467
- x.to_i
468
- end
294
+ raise "Protocol error, got '#{rtype}' as initial reply byte"
469
295
  end
470
296
  end
471
-
472
297
  end