ezmobius-redis 0.0.3.4 → 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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