ezmobius-redis 0.0.3.4 → 0.1

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