Skip to content
Browse files

Fix QUIT. Consistently marshal/unmarshal values. Broaden fetch capabi…

…lity to handle multi_bulk_reply.
  • Loading branch information...
1 parent d1c0c9c commit 9183102a21c216cff9af2201242c41ce9123f38f @jeremy committed Mar 21, 2009
Showing with 67 additions and 71 deletions.
  1. +37 −18 lib/redis.rb
  2. +30 −53 spec/redis_spec.rb
View
55 lib/redis.rb
@@ -69,7 +69,7 @@ def set_unless_exists(key, val)
def [](key)
timeout_retry(3, 3){
res = perform("GET #{key}\r\n")
- redis_unmarshal(bulk_reply(res))
+ bulk_reply(res)
}
end
@@ -226,6 +226,7 @@ def type?(key)
#
# Return value: status code reply
def push_tail(key, string)
+ string = redis_marshal(string)
timeout_retry(3, 3){
res = perform("RPUSH #{key} #{string.size}\r\n#{string}\r\n")
status_code_reply(res)
@@ -240,6 +241,7 @@ def push_tail(key, string)
#
# Return value: status code reply
def push_head(key, string)
+ string = redis_marshal(string)
timeout_retry(3, 3){
res = perform("LPUSH #{key} #{string.size}\r\n#{string}\r\n")
status_code_reply(res)
@@ -280,8 +282,9 @@ def pop_tail(key)
#
# Return value: status code reply
def list_set(key, index, val)
+ val = redis_marshal(val)
timeout_retry(3, 3){
- res = perform("LSET #{key} #{index} #{val.to_s.size}\r\n#{val}\r\n")
+ res = perform("LSET #{key} #{index} #{val.size}\r\n#{val}\r\n")
status_code_reply(res)
}
end
@@ -409,7 +412,8 @@ def list_index(key, index)
# -1 if the specified key does not exist
# -2 if the specified key does not hold a list value
def list_rm(key, count, value)
- res = perform("LREM #{key} #{count} #{value.to_s.size}\r\n#{value}\r\n").to_i
+ value = redis_marshal(value)
+ res = perform("LREM #{key} #{count} #{value.size}\r\n#{value}\r\n").to_i
case res
when -1
raise RedisError, "key: #{key} does not exist"
@@ -432,8 +436,9 @@ def list_rm(key, count, value)
# 1 if the new element was added 0 if the new element was already a member
# of the set -2 if the key contains a non set value
def set_add(key, member)
+ member = redis_marshal(member)
timeout_retry(3, 3){
- res = perform("SADD #{key} #{member.to_s.size}\r\n#{member}\r\n").to_i
+ res = perform("SADD #{key} #{member.size}\r\n#{member}\r\n").to_i
case res
when 1
true
@@ -457,8 +462,9 @@ def set_add(key, member)
# 1 if the new element was removed 0 if the new element was not a member
# of the set -2 if the key does not hold a set value
def set_delete(key, member)
+ member = redis_marshal(member)
timeout_retry(3, 3){
- res = perform("SREM #{key} #{member.to_s.size}\r\n#{member}\r\n")
+ res = perform("SREM #{key} #{member.size}\r\n#{member}\r\n")
case res
when 1
true
@@ -507,8 +513,9 @@ def set_count(key)
# 1 if the element is a member of the set 0 if the element is not a member of
# the set OR if the key does not exist -2 if the key does not hold a set value
def set_member?(key, member)
+ member = redis_marshal(member)
timeout_retry(3, 3){
- res = perform("SISMEMBER #{key} #{member.size}\r\n#{member}\r\n")
+ res = perform("SISMEMBER #{key} #{member.size}\r\n#{member}\r\n").to_i
case res
when 1
true
@@ -707,17 +714,18 @@ def lastsave
def quit
timeout_retry(3, 3){
- status_code_reply(perform("QUIT\r\n"))
+ perform("QUIT\r\n")
}
+ close
end
def info
info = {}
x = timeout_retry(3, 3){
- write "INFO\r\n"
- read(read_proto.to_i.abs).split("\r\n")
+ size = perform("INFO\r\n").to_i.abs
+ @socket.read(size).split("\r\n")
}
x.each do |kv|
@@ -759,17 +767,25 @@ def reconnect
private
def perform(command)
+ puts "> #{command}" if ENV['DEBUG']
@socket.write(command)
- read_proto
+ res = read_proto
+ puts "< #{res}" if ENV['DEBUG']
+ res
end
- def fetch(res)
- res = res.to_i.abs
- @socket.read(res + 2).chop if res > 0
+ def fetch(len)
+ len = [0, len.to_i].max
+ res = @socket.read(len + 2)
+ res = res.chop if res
+ puts "| #{res}" if ENV['DEBUG']
+ res
end
def read_proto
- @socket.gets.chop
+ if res = @socket.gets
+ res.chop
+ end
end
@@ -785,7 +801,7 @@ def bulk_reply(res)
if res.index(ERRCODE) == 0
raise RedisError, fetch(res)
elsif res != NIL
- fetch(res)
+ redis_unmarshal(fetch(res))
else
nil
end
@@ -798,8 +814,11 @@ def multi_bulk_reply(res)
elsif res == NIL
nil
else
- items = Integer(res)
- (0..items).map { fetch(Integer(read_proto)) }
+ list = []
+ Integer(res).times do
+ list << redis_unmarshal(fetch(Integer(read_proto)))
+ end
+ list
end
end
@@ -811,7 +830,7 @@ def timeout_retry(time, retries, &block)
end
def redis_unmarshal(obj)
- if obj[0] == 4
+ if obj && obj[0] == 4
Marshal.load(obj)
else
obj
View
83 spec/redis_spec.rb
@@ -16,11 +16,14 @@ def ==(other)
@r = Redis.new
@r.select_db(15) # use database 15 for testing so we dont accidentally step on you real data
@r['foo'] = 'bar'
+
+ @hello = [:hello]
+ @goodbye = { :goodbye => true }
end
after do
@r.keys('*').each {|k| @r.delete k }
- @r.close
+ @r.quit
end
it "should properly marshall objects" do
@@ -48,14 +51,12 @@ class MyFail; def fail; 'it will' end; end
end
it "should be able to INCR(increment) a key" do
- @r.delete('counter')
@r.incr('counter').should == 1
@r.incr('counter').should == 2
@r.incr('counter').should == 3
end
it "should be able to DECR(decrement) a key" do
- @r.delete('counter')
@r.incr('counter').should == 1
@r.incr('counter').should == 2
@r.incr('counter').should == 3
@@ -69,16 +70,12 @@ class MyFail; def fail; 'it will' end; end
end
it "should be able to RENAME a key" do
- @r.delete 'foo'
- @r.delete 'bar'
@r['foo'] = 'hi'
@r.rename! 'foo', 'bar'
@r['bar'].should == 'hi'
end
it "should be able to RENAMENX(rename unless the new key already exists) a key" do
- @r.delete 'foo'
- @r.delete 'bar'
@r['foo'] = 'hi'
@r['bar'] = 'ohai'
lambda {@r.rename 'foo', 'bar'}.should raise_error(RedisError)
@@ -93,118 +90,104 @@ class MyFail; def fail; 'it will' end; end
end
it "should be able to KEYS(glob for keys)" do
- @r.keys("f*").each do |key|
- @r.delete key
- end
@r['f'] = 'nik'
@r['fo'] = 'nak'
@r['foo'] = 'qux'
@r.keys("f*").sort.should == ['f','fo', 'foo'].sort
end
it "should be able to check the TYPE of a key" do
- @r.type?('foo').should be_nil
@r['foo'] = 'nik'
@r.type?('foo').should == "string"
@r.delete 'foo'
@r.type?('foo').should == "none"
end
it "should be able to push to the head of a list" do
- @r.push_head "list", 'hello'
+ @r.push_head "list", @hello
@r.push_head "list", 42
@r.type?('list').should == "list"
@r.list_length('list').should == 2
@r.pop_head('list').should == '42'
- @r.delete('list')
end
it "should be able to push to the tail of a list" do
- @r.push_tail "list", 'hello'
+ @r.push_tail "list", @hello
@r.type?('list').should == "list"
@r.list_length('list').should == 1
- @r.delete('list')
end
it "should be able to pop the tail of a list" do
- @r.push_tail "list", 'hello'
- @r.push_tail "list", 'goodbye'
+ @r.push_tail "list", @hello
+ @r.push_tail "list", @goodbye
@r.type?('list').should == "list"
@r.list_length('list').should == 2
- @r.pop_tail('list').should == 'goodbye'
- @r.delete('list')
+ @r.pop_tail('list').should == @goodbye
end
it "should be able to pop the head of a list" do
- @r.push_tail "list", 'hello'
- @r.push_tail "list", 'goodbye'
+ @r.push_tail "list", @hello
+ @r.push_tail "list", @goodbye
@r.type?('list').should == "list"
@r.list_length('list').should == 2
- @r.pop_head('list').should == 'hello'
- @r.delete('list')
+ @r.pop_head('list').should == @hello
end
it "should be able to get the length of a list" do
- @r.push_tail "list", 'hello'
- @r.push_tail "list", 'goodbye'
+ @r.push_tail "list", @hello
+ @r.push_tail "list", @goodbye
@r.type?('list').should == "list"
@r.list_length('list').should == 2
- @r.delete('list')
end
it "should be able to get a range of values from a list" do
- @r.push_tail "list", 'hello'
- @r.push_tail "list", 'goodbye'
+ @r.push_tail "list", @hello
+ @r.push_tail "list", @goodbye
@r.push_tail "list", '1'
@r.push_tail "list", '2'
@r.push_tail "list", '3'
@r.type?('list').should == "list"
@r.list_length('list').should == 5
@r.list_range('list', 2, -1).should == ['1', '2', '3']
- @r.delete('list')
end
it "should be able to trim a list" do
- @r.push_tail "list", 'hello'
- @r.push_tail "list", 'goodbye'
+ @r.push_tail "list", @hello
+ @r.push_tail "list", @goodbye
@r.push_tail "list", '1'
@r.push_tail "list", '2'
@r.push_tail "list", '3'
@r.type?('list').should == "list"
@r.list_length('list').should == 5
@r.list_trim 'list', 0, 1
@r.list_length('list').should == 2
- @r.list_range('list', 0, -1).should == ['hello', 'goodbye']
- @r.delete('list')
+ @r.list_range('list', 0, -1).should == [@hello, @goodbye]
end
it "should be able to get a value by indexing into a list" do
- @r.push_tail "list", 'hello'
- @r.push_tail "list", 'goodbye'
+ @r.push_tail "list", @hello
+ @r.push_tail "list", @goodbye
@r.type?('list').should == "list"
@r.list_length('list').should == 2
- @r.list_index('list', 1).should == 'goodbye'
- @r.delete('list')
+ @r.list_index('list', 1).should == @goodbye
end
it "should be able to set a value by indexing into a list" do
- @r.push_tail "list", 'hello'
- @r.push_tail "list", 'hello'
+ @r.push_tail "list", @hello
+ @r.push_tail "list", @hello
@r.type?('list').should == "list"
@r.list_length('list').should == 2
- @r.list_set('list', 1, 'goodbye').should be_true
- @r.list_index('list', 1).should == 'goodbye'
- @r.delete('list')
+ @r.list_set('list', 1, @goodbye).should be_true
+ @r.list_index('list', 1).should == @goodbye
end
it "should be able to remove values from a list LREM" do
- @r.push_tail "list", 'hello'
- @r.push_tail "list", 'goodbye'
+ @r.push_tail "list", @hello
+ @r.push_tail "list", @goodbye
@r.type?('list').should == "list"
@r.list_length('list').should == 2
- @r.list_rm('list', 1, 'hello').should == 1
- @r.list_range('list', 0, -1).should == ['goodbye']
- @r.delete('list')
+ @r.list_rm('list', 1, @hello).should == 1
+ @r.list_range('list', 0, -1).should == [@goodbye]
end
it "should be able add members to a set" do
@@ -213,7 +196,6 @@ class MyFail; def fail; 'it will' end; end
@r.type?('set').should == "set"
@r.set_count('set').should == 2
@r.set_members('set').sort.should == ['key1', 'key2'].sort
- @r.delete('set')
end
it "should be able delete members to a set" do
@@ -225,15 +207,13 @@ class MyFail; def fail; 'it will' end; end
@r.set_delete('set', 'key1')
@r.set_count('set').should == 1
@r.set_members('set').should == Set.new(['key2'])
- @r.delete('set')
end
it "should be able count the members of a set" do
@r.set_add "set", 'key1'
@r.set_add "set", 'key2'
@r.type?('set').should == "set"
@r.set_count('set').should == 2
- @r.delete('set')
end
it "should be able test for set membership" do
@@ -244,15 +224,13 @@ class MyFail; def fail; 'it will' end; end
@r.set_member?('set', 'key1').should be_true
@r.set_member?('set', 'key2').should be_true
@r.set_member?('set', 'notthere').should be_false
- @r.delete('set')
end
it "should be able to do set intersection" do
@r.set_add "set", 'key1'
@r.set_add "set", 'key2'
@r.set_add "set2", 'key2'
@r.set_intersect('set', 'set2').should == Set.new(['key2'])
- @r.delete('set')
end
it "should be able to do set intersection and store the results in a key" do
@@ -261,7 +239,6 @@ class MyFail; def fail; 'it will' end; end
@r.set_add "set2", 'key2'
@r.set_inter_store('newone', 'set', 'set2')
@r.set_members('newone').should == Set.new(['key2'])
- @r.delete('set')
end
it "should be able to do crazy SORT queries" do

0 comments on commit 9183102

Please sign in to comment.
Something went wrong with that request. Please try again.