Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
69664139
Commit
69664139
authored
May 29, 2009
by
antirez
Browse files
ruby library client is not Redis-rb merged with RubyRedis "engine" by Brian McKinney
parent
d90b352d
Changes
8
Show whitespace changes
Inline
Side-by-side
client-libraries/ruby/lib/dist_redis.rb
View file @
69664139
...
@@ -2,13 +2,20 @@ require 'redis'
...
@@ -2,13 +2,20 @@ require 'redis'
require
'hash_ring'
require
'hash_ring'
class
DistRedis
class
DistRedis
attr_reader
:ring
attr_reader
:ring
def
initialize
(
*
servers
)
def
initialize
(
opts
=
{})
srvs
=
[]
hosts
=
[]
servers
.
each
do
|
s
|
server
,
port
=
s
.
split
(
':'
)
db
=
opts
[
:db
]
||
nil
srvs
<<
Redis
.
new
(
:host
=>
server
,
:port
=>
port
)
timeout
=
opts
[
:timeout
]
||
nil
raise
Error
,
"No hosts given"
unless
opts
[
:hosts
]
opts
[
:hosts
].
each
do
|
h
|
host
,
port
=
h
.
split
(
':'
)
hosts
<<
Redis
.
new
(
:host
=>
host
,
:port
=>
port
,
:db
=>
db
,
:timeout
=>
timeout
,
:db
=>
db
)
end
end
@ring
=
HashRing
.
new
srvs
@ring
=
HashRing
.
new
hosts
end
end
def
node_for_key
(
key
)
def
node_for_key
(
key
)
...
...
client-libraries/ruby/lib/pipeline.rb
View file @
69664139
...
@@ -9,18 +9,12 @@ class Redis
...
@@ -9,18 +9,12 @@ class Redis
@commands
=
[]
@commands
=
[]
end
end
def
execute_command
(
data
)
def
call_command
(
command
)
@commands
<<
data
@commands
<<
command
write_and_read
if
@commands
.
size
>=
BUFFER_SIZE
end
end
def
finish
def
execute
write_and_read
@redis
.
call_command
(
@commands
)
end
def
write_and_read
@redis
.
execute_command
(
@commands
.
join
,
true
)
@redis
.
read_socket
@commands
.
clear
@commands
.
clear
end
end
...
...
client-libraries/ruby/lib/redis.rb
View file @
69664139
require
'socket'
require
'socket'
require
'set'
require
File
.
join
(
File
.
dirname
(
__FILE__
),
'server'
)
require
File
.
join
(
File
.
dirname
(
__FILE__
),
'pipeline'
)
require
File
.
join
(
File
.
dirname
(
__FILE__
),
'pipeline'
)
class
RedisError
<
StandardError
begin
end
if
(
RUBY_VERSION
>=
'1.9'
)
class
RedisRenameError
<
StandardError
require
'timeout'
RedisTimer
=
Timeout
else
require
'system_timer'
RedisTimer
=
SystemTimer
end
rescue
LoadError
RedisTimer
=
nil
end
end
class
Redis
class
Redis
ERR
=
"-"
.
freeze
BulkCommands
=
{
OK
=
'OK'
.
freeze
"set"
=>
true
,
"setnx"
=>
true
,
"rpush"
=>
true
,
"lpush"
=>
true
,
"lset"
=>
true
,
PONG
=
'PONG'
.
freeze
"lrem"
=>
true
,
"sadd"
=>
true
,
"srem"
=>
true
,
"sismember"
=>
true
,
SINGLE
=
'+'
.
freeze
"echo"
=>
true
,
"getset"
=>
true
,
"smove"
=>
true
BULK
=
'$'
.
freeze
}
MULTI
=
'*'
.
freeze
INT
=
':'
.
freeze
ConvertToBool
=
lambda
do
|
r
|
case
r
attr_reader
:server
when
0
then
false
when
1
then
true
def
initialize
(
opts
=
{})
else
r
@opts
=
{
:host
=>
'localhost'
,
:port
=>
'6379'
,
:db
=>
0
}.
merge
(
opts
)
end
$debug
=
@opts
[
:debug
]
end
@db
=
@opts
[
:db
]
@server
=
Server
.
new
(
@opts
[
:host
],
@opts
[
:port
],
(
@opts
[
:timeout
]
||
10
))
ReplyProcessor
=
{
end
"exists"
=>
ConvertToBool
,
"sismember"
=>
ConvertToBool
,
def
pipelined
"sadd"
=>
ConvertToBool
,
pipeline
=
Pipeline
.
new
(
self
)
"srem"
=>
ConvertToBool
,
yield
pipeline
"smove"
=>
ConvertToBool
,
pipeline
.
finish
"move"
=>
ConvertToBool
,
end
"setnx"
=>
ConvertToBool
,
"del"
=>
ConvertToBool
,
def
to_s
"renamenx"
=>
ConvertToBool
,
"
#{
host
}
:
#{
port
}
->
#{
@db
}
"
"expire"
=>
ConvertToBool
,
end
"keys"
=>
lambda
{
|
r
|
r
.
split
(
" "
)},
"info"
=>
lambda
{
|
r
|
def
port
@opts
[
:port
]
end
def
host
@opts
[
:host
]
end
def
quit
execute_command
(
"QUIT
\r\n
"
,
true
)
end
def
ping
execute_command
(
"PING
\r\n
"
)
==
PONG
end
def
select_db
(
index
)
@db
=
index
execute_command
(
"SELECT
#{
index
}
\r\n
"
)
end
def
flush_db
execute_command
(
"FLUSHDB
\r\n
"
)
==
OK
end
def
flush_all
puts
"Warning!
\n
Flushing *ALL* databases!
\n
5 Seconds to Hit ^C!"
trap
(
'INT'
)
{
quit
;
return
false
}
sleep
5
execute_command
(
"FLUSHALL
\r\n
"
)
==
OK
end
def
last_save
execute_command
(
"LASTSAVE
\r\n
"
).
to_i
end
def
bgsave
execute_command
(
"BGSAVE
\r\n
"
)
==
OK
end
def
info
info
=
{}
info
=
{}
x
=
execute_command
(
"INFO
\r\n
"
)
r
.
each_line
{
|
kv
|
x
.
each_line
do
|
kv
|
k
,
v
=
kv
.
split
(
":"
,
2
).
map
{
|
x
|
x
.
chomp
}
k
,
v
=
kv
.
split
(
':'
,
2
)
k
,
v
=
k
.
chomp
,
v
=
v
.
chomp
info
[
k
.
to_sym
]
=
v
info
[
k
.
to_sym
]
=
v
end
}
info
info
end
}
}
def
keys
(
glob
)
execute_command
(
"KEYS
#{
glob
}
\r\n
"
).
split
(
' '
)
Aliases
=
{
end
"flush_db"
=>
"flushdb"
,
"flush_all"
=>
"flushall"
,
def
rename!
(
oldkey
,
newkey
)
"last_save"
=>
"lastsave"
,
execute_command
(
"RENAME
#{
oldkey
}
#{
newkey
}
\r\n
"
)
"key?"
=>
"exists"
,
end
"delete"
=>
"del"
,
"randkey"
=>
"randomkey"
,
def
rename
(
oldkey
,
newkey
)
"list_length"
=>
"llen"
,
case
execute_command
(
"RENAMENX
#{
oldkey
}
#{
newkey
}
\r\n
"
)
"push_tail"
=>
"rpush"
,
when
-
1
"push_head"
=>
"lpush"
,
raise
RedisRenameError
,
"source key:
#{
oldkey
}
does not exist"
"pop_tail"
=>
"rpop"
,
when
0
"pop_head"
=>
"lpop"
,
raise
RedisRenameError
,
"target key:
#{
oldkey
}
already exists"
"list_set"
=>
"lset"
,
when
-
3
"list_range"
=>
"lrange"
,
raise
RedisRenameError
,
"source and destination keys are the same"
"list_trim"
=>
"ltrim"
,
when
1
"list_index"
=>
"lindex"
,
true
"list_rm"
=>
"lrem"
,
end
"set_add"
=>
"sadd"
,
end
"set_delete"
=>
"srem"
,
"set_count"
=>
"scard"
,
def
key?
(
key
)
"set_member?"
=>
"sismember"
,
execute_command
(
"EXISTS
#{
key
}
\r\n
"
)
==
1
"set_members"
=>
"smembers"
,
end
"set_intersect"
=>
"sinter"
,
"set_intersect_store"
=>
"sinterstore"
,
def
delete
(
key
)
"set_inter_store"
=>
"sinterstore"
,
execute_command
(
"DEL
#{
key
}
\r\n
"
)
==
1
"set_union"
=>
"sunion"
,
end
"set_union_store"
=>
"sunionstore"
,
"set_diff"
=>
"sdiff"
,
def
[]
(
key
)
"set_diff_store"
=>
"sdiffstore"
,
get
(
key
)
"set_move"
=>
"smove"
,
end
"set_unless_exists"
=>
"setnx"
,
"rename_unless_exists"
=>
"renamenx"
,
def
get
(
key
)
"type?"
=>
"type"
execute_command
(
"GET
#{
key
}
\r\n
"
)
}
end
def
mget
(
*
keys
)
def
initialize
(
opts
=
{})
execute_command
(
"MGET
#{
keys
.
join
(
' '
)
}
\r\n
"
)
@host
=
opts
[
:host
]
||
'127.0.0.1'
@port
=
opts
[
:port
]
||
6379
@db
=
opts
[
:db
]
||
0
@timeout
=
opts
[
:timeout
]
||
5
$debug
=
opts
[
:debug
]
||
false
connect_to_server
end
end
def
incr
(
key
,
increment
=
nil
)
def
to_s
if
increment
"Redis Client connected to
#{
@host
}
:
#{
@port
}
against DB
#{
@db
}
"
execute_command
(
"INCRBY
#{
key
}
#{
increment
}
\r\n
"
)
else
execute_command
(
"INCR
#{
key
}
\r\n
"
)
end
end
end
def
decr
(
key
,
decrement
=
nil
)
def
connect_to_server
if
decrement
@sock
=
connect_to
(
@host
,
@port
,
@timeout
==
0
?
nil
:
@timeout
)
execute_command
(
"DECRBY
#{
key
}
#{
decrement
}
\r\n
"
)
call_command
([
"select"
,
@db
])
if
@db
!=
0
else
execute_command
(
"DECR
#{
key
}
\r\n
"
)
end
end
end
def
randkey
def
connect_to
(
host
,
port
,
timeout
=
nil
)
execute_command
(
"RANDOMKEY
\r\n
"
)
# We support connect() timeout only if system_timer is availabe
# or if we are running against Ruby >= 1.9
# Timeout reading from the socket instead will be supported anyway.
if
@timeout
!=
0
and
RedisTimer
begin
sock
=
TCPSocket
.
new
(
host
,
port
)
rescue
Timeout
::
Error
@sock
=
nil
raise
Timeout
::
Error
,
"Timeout connecting to the server"
end
end
def
list_length
(
key
)
case
i
=
execute_command
(
"LLEN
#{
key
}
\r\n
"
)
when
-
2
raise
RedisError
,
"key:
#{
key
}
does not hold a list value"
else
else
i
sock
=
TCPSocket
.
new
(
host
,
port
)
end
end
end
sock
.
setsockopt
Socket
::
IPPROTO_TCP
,
Socket
::
TCP_NODELAY
,
1
def
type?
(
key
)
# If the timeout is set we set the low level socket options in order
execute_command
(
"TYPE
#{
key
}
\r\n
"
)
# to make sure a blocking read will return after the specified number
# of seconds. This hack is from memcached ruby client.
if
timeout
secs
=
Integer
(
timeout
)
usecs
=
Integer
((
timeout
-
secs
)
*
1_000_000
)
optval
=
[
secs
,
usecs
].
pack
(
"l_2"
)
sock
.
setsockopt
Socket
::
SOL_SOCKET
,
Socket
::
SO_RCVTIMEO
,
optval
sock
.
setsockopt
Socket
::
SOL_SOCKET
,
Socket
::
SO_SNDTIMEO
,
optval
end
end
sock
def
push_tail
(
key
,
val
)
execute_command
(
"RPUSH
#{
key
}
#{
value_to_wire
(
val
)
}
\r\n
"
)
end
def
push_head
(
key
,
val
)
execute_command
(
"LPUSH
#{
key
}
#{
value_to_wire
(
val
)
}
\r\n
"
)
end
end
def
pop_head
(
key
)
def
method_missing
(
*
argv
)
execute
_command
(
"LPOP
#{
key
}
\r\n
"
)
call
_command
(
argv
)
end
end
def
pop_tail
(
key
)
def
call_command
(
argv
)
execute_command
(
"RPOP
#{
key
}
\r\n
"
)
puts
argv
.
inspect
if
$debug
end
# this wrapper to raw_call_command handle reconnection on socket
# error. We try to reconnect just one time, otherwise let the error
def
list_set
(
key
,
index
,
val
)
# araise.
execute_command
(
"LSET
#{
key
}
#{
index
}
#{
value_to_wire
(
val
)
}
\r\n
"
)
==
OK
connect_to_server
if
!
@sock
end
begin
raw_call_command
(
argv
)
def
list_range
(
key
,
start
,
ending
)
rescue
Errno
::
ECONNRESET
execute_command
(
"LRANGE
#{
key
}
#{
start
}
#{
ending
}
\r\n
"
)
@sock
.
close
connect_to_server
raw_call_command
(
argv
)
end
end
def
list_trim
(
key
,
start
,
ending
)
execute_command
(
"LTRIM
#{
key
}
#{
start
}
#{
ending
}
\r\n
"
)
end
end
def
list_index
(
key
,
index
)
def
raw_call_command
(
argvp
)
execute_command
(
"LINDEX
#{
key
}
#{
index
}
\r\n
"
)
pipeline
=
argvp
[
0
].
is_a?
(
Array
)
end
def
list_rm
(
key
,
count
,
val
)
unless
pipeline
case
num
=
execute_command
(
"LREM
#{
key
}
#{
count
}
#{
value_to_wire
(
val
)
}
\r\n
"
)
argvv
=
[
argvp
]
when
-
1
raise
RedisError
,
"key:
#{
key
}
does not exist"
when
-
2
raise
RedisError
,
"key:
#{
key
}
does not hold a list value"
else
else
num
argvv
=
argvp
end
end
def
set_add
(
key
,
member
)
case
execute_command
(
"SADD
#{
key
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
when
1
true
when
0
false
when
-
2
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
end
end
def
set_delete
(
key
,
member
)
case
execute_command
(
"SREM
#{
key
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
when
1
true
when
0
false
when
-
2
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
end
end
end
def
set_count
(
key
)
command
=
''
case
i
=
execute_command
(
"SCARD
#{
key
}
\r\n
"
)
when
-
2
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
else
i
end
end
def
set_member?
(
key
,
member
)
argvv
.
each
do
|
argv
|
case
execute_command
(
"SISMEMBER
#{
key
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
bulk
=
nil
when
1
argv
[
0
]
=
argv
[
0
].
to_s
.
downcase
true
argv
[
0
]
=
Aliases
[
argv
[
0
]]
if
Aliases
[
argv
[
0
]]
when
0
if
BulkCommands
[
argv
[
0
]]
and
argv
.
length
>
1
false
bulk
=
argv
[
-
1
].
to_s
when
-
2
argv
[
-
1
]
=
bulk
.
length
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
end
end
command
<<
argv
.
join
(
' '
)
+
"
\r\n
"
command
<<
bulk
+
"
\r\n
"
if
bulk
end
end
def
set_members
(
key
)
@sock
.
write
(
command
)
Set
.
new
(
execute_command
(
"SMEMBERS
#{
key
}
\r\n
"
))
end
def
set_intersect
(
*
keys
)
Set
.
new
(
execute_command
(
"SINTER
#{
keys
.
join
(
' '
)
}
\r\n
"
))
end
def
set_inter_store
(
destkey
,
*
keys
)
results
=
argvv
.
map
do
|
argv
|
execute_command
(
"SINTERSTORE
#{
destkey
}
#{
keys
.
join
(
' '
)
}
\r\n
"
)
processor
=
ReplyProcessor
[
argv
[
0
]]
processor
?
processor
.
call
(
read_reply
)
:
read_reply
end
end
def
set_union
(
*
keys
)
return
pipeline
?
results
:
results
[
0
]
Set
.
new
(
execute_command
(
"SUNION
#{
keys
.
join
(
' '
)
}
\r\n
"
))
end
end
def
se
t_union_store
(
destkey
,
*
key
s
)
def
se
lect
(
*
arg
s
)
execute_command
(
"SUNIONSTORE
#{
destkey
}
#{
keys
.
join
(
' '
)
}
\r\n
"
)
raise
"SELECT not allowed, use the :db option when creating the object"
end
end
def
set_diff
(
*
key
s
)
def
[]
(
key
)
Set
.
new
(
execute_command
(
"SDIFF
#{
keys
.
join
(
' '
)
}
\r\n
"
)
)
get
(
key
)
end
end
def
set_diff_store
(
destkey
,
*
keys
)
def
[]=
(
key
,
value
)
execute_command
(
"SDIFFSTORE
#{
destkey
}
#{
keys
.
join
(
' '
)
}
\r\n
"
)
set
(
key
,
value
)
end
end
def
set_move
(
srckey
,
destkey
,
member
)
def
set
(
key
,
value
,
expiry
=
nil
)
execute_command
(
"SMOVE
#{
srckey
}
#{
destkey
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
==
1
call_command
([
:set
,
key
,
value
])
expire
(
key
,
expiry
)
unless
expiry
.
nil?
end
end
def
sort
(
key
,
opts
=
{})
def
sort
(
key
,
opts
=
{})
cmd
=
"SORT
#{
key
}
"
cmd
=
[]
cmd
<<
" BY
#{
opts
[
:by
]
}
"
if
opts
[
:by
]
cmd
<<
"SORT
#{
key
}
"
cmd
<<
" GET
#{
[
opts
[
:get
]].
flatten
*
' GET '
}
"
if
opts
[
:get
]
cmd
<<
"BY
#{
opts
[
:by
]
}
"
if
opts
[
:by
]
cmd
<<
" INCR
#{
opts
[
:incr
]
}
"
if
opts
[
:incr
]
cmd
<<
"GET
#{
[
opts
[
:get
]].
flatten
*
' GET '
}
"
if
opts
[
:get
]
cmd
<<
" DEL
#{
opts
[
:del
]
}
"
if
opts
[
:del
]
cmd
<<
"
#{
opts
[
:order
]
}
"
if
opts
[
:order
]
cmd
<<
" DECR
#{
opts
[
:decr
]
}
"
if
opts
[
:decr
]
cmd
<<
"LIMIT
#{
opts
[
:limit
].
join
(
' '
)
}
"
if
opts
[
:limit
]
cmd
<<
"
#{
opts
[
:order
]
}
"
if
opts
[
:order
]
call_command
(
cmd
)
cmd
<<
" LIMIT
#{
opts
[
:limit
].
join
(
' '
)
}
"
if
opts
[
:limit
]
cmd
<<
"
\r\n
"
execute_command
(
cmd
)
end
def
[]=
(
key
,
val
)
set
(
key
,
val
)
end
def
set
(
key
,
val
,
expiry
=
nil
)
s
=
execute_command
(
"SET
#{
key
}
#{
value_to_wire
(
val
)
}
\r\n
"
)
==
OK
return
expire
(
key
,
expiry
)
if
s
&&
expiry
s
end
def
dbsize
execute_command
(
"DBSIZE
\r\n
"
)
end
def
expire
(
key
,
expiry
=
nil
)
execute_command
(
"EXPIRE
#{
key
}
#{
expiry
}
\r\n
"
)
==
1
end
def
set_unless_exists
(
key
,
val
)
execute_command
(
"SETNX
#{
key
}
#{
value_to_wire
(
val
)
}
\r\n
"
)
==
1
end
end
def
bulk_reply
def
incr
(
key
,
increment
=
nil
)
begin
call_command
(
increment
?
[
"incrby"
,
key
,
increment
]
:
[
"incr"
,
key
])
x
=
read
puts
"bulk_reply read value is
#{
x
.
inspect
}
"
if
$debug
return
x
rescue
=>
e
puts
"error in bulk_reply
#{
e
}
"
if
$debug
nil
end
end
end
def
write
(
data
)
def
decr
(
key
,
decrement
=
nil
)
puts
"writing:
#{
data
}
"
if
$debug
call_command
(
decrement
?
[
"decrby"
,
key
,
decrement
]
:
[
"decr"
,
key
])
@socket
.
write
(
data
)
end
end
def
read
(
length
=
0
)
# Ruby defines a now deprecated type method so we need to override it here
length
=
read_proto
unless
length
>
0
# since it will never hit method_missing
res
=
@socket
.
read
(
length
)
def
type
(
key
)
puts
"read is
#{
res
.
inspect
}
"
if
$debug
call_command
([
'type'
,
key
])
res
end
end
def
multi_bulk
def
quit
res
=
read_proto
call_command
([
'quit'
])
puts
"mb res is
#{
res
.
inspect
}
"
if
$debug
rescue
Errno
::
ECONNRESET
list
=
[]
Integer
(
res
).
times
do
vf
=
get_response
puts
"curren vf is
#{
vf
.
inspect
}
"
if
$debug
list
<<
vf
puts
"current list is
#{
list
.
inspect
}
"
if
$debug
end
list
end
end
def
get_reply
def
pipelined
(
&
block
)
begin
pipeline
=
Pipeline
.
new
self
r
=
read
(
1
)
yield
pipeline
raise
RedisError
if
(
r
==
"
\r
"
||
r
==
"
\n
"
)
pipeline
.
execute
rescue
RedisError
retry
end
r
end
end
def
status_code_reply
def
read_reply
# We read the first byte using read() mainly because gets() is
# immune to raw socket timeouts.
begin
begin
res
=
read_proto
rtype
=
@sock
.
read
(
1
)
if
res
.
index
(
'-'
)
==
0
rescue
Errno
::
EAGAIN
raise
RedisError
,
res
# We want to make sure it reconnects on the next command after the
else
# timeout. Otherwise the server may reply in the meantime leaving
true
# the protocol in a desync status.
end
@sock
=
nil
rescue
RedisError
raise
Errno
::
EAGAIN
,
"Timeout reading from the socket"
raise
RedisError
end
end
def
execute_command
(
command
,
ignore_response
=
false
)
ss
=
server
.
socket
unless
ss
.
object_id
==
@socket
.
object_id
@socket
=
ss
puts
"Socket changed, selecting DB"
if
$debug
unless
command
[
0
..
6
]
==
'SELECT'
#BTM - Ugh- DRY but better than infinite recursion
write
(
"SELECT
#{
@db
}
\r\n
"
)
get_response
end
end
write
(
command
)
get_response
unless
ignore_response
rescue
Errno
::
ECONNRESET
,
Errno
::
EPIPE
,
NoMethodError
,
Timeout
::
Error
=>
e
raise
RedisError
,
"Connection error"
end
end
def
get_response
raise
Errno
::
ECONNRESET
,
"Connection lost"
if
!
rtype
rtype
=
get_reply
line
=
@sock
.
gets
puts
"reply_type is
#{
rtype
.
inspect
}
"
if
$debug
case
rtype
case
rtype
when
SINGLE
when
"-"
single_line
raise
"-"
+
line
.
strip
when
BULK
when
"+"
bulk_reply
line
.
strip
when
MULTI
when
":"
multi_bulk
line
.
to_i
when
INT
when
"$"
integer_reply
bulklen
=
line
.
to_i
when
ERR
return
nil
if
bulklen
==
-
1
raise
RedisError
,
single_line
data
=
@sock
.
read
(
bulklen
)
else
@sock
.
read
(
2
)
# CRLF
raise
RedisError
,
"Unknown response.."
data
end
when
"*"
end
objects
=
line
.
to_i
return
nil
if
bulklen
==
-
1
def
integer_reply
res
=
[]
Integer
(
read_proto
)
objects
.
times
{
end
res
<<
read_reply
}
def
single_line
res
buff
=
""
while
buff
[
-
2
..-
1
]
!=
"
\r\n
"
buff
<<
read
(
1
)
end
puts
"single_line value is
#{
buff
[
0
..-
3
].
inspect
}
"
if
$debug
buff
[
0
..-
3
]
end
def
read_socket
begin
socket
=
@server
.
socket
while
res
=
socket
.
read
(
8096
)
break
if
res
.
size
!=
8096
end
#Timeout or server down
rescue
Errno
::
ECONNRESET
,
Errno
::
EPIPE
,
Errno
::
ECONNREFUSED
=>
e
server
.
close
puts
"Client (
#{
server
.
inspect
}
) disconnected from server:
#{
e
.
inspect
}
\n
"
if
$debug
retry
rescue
Timeout
::
Error
=>
e
#BTM - Ignore this error so we don't go into an endless loop
puts
"Client (
#{
server
.
inspect
}
) Timeout
\n
"
if
$debug
#Server down
rescue
NoMethodError
=>
e
puts
"Client (
#{
server
.
inspect
}
) tryin server that is down:
#{
e
.
inspect
}
\n
Dying!"
if
$debug
raise
Errno
::
ECONNREFUSED
#exit
end
end
def
read_proto
res
=
@socket
.
readline
x
=
res
.
chomp
puts
"read_proto is
#{
x
.
inspect
}
\n\n
"
if
$debug
x
.
to_i
end
private
def
value_to_wire
(
value
)
value_str
=
value
.
to_s
if
value_str
.
respond_to?
(
:bytesize
)
value_size
=
value_str
.
bytesize
else
else
value_size
=
value_str
.
size
raise
"Protocol error, got '
#{
rtype
}
' as initial reply byte"
end
end
"
#{
value_size
}
\r\n
#{
value_str
}
"
end
end
end
end
client-libraries/ruby/lib/server.rb
deleted
100644 → 0
View file @
d90b352d
begin
# Timeout code is courtesy of Ruby memcache-client
# http://github.com/mperham/memcache-client/tree
# Try to use the SystemTimer gem instead of Ruby's timeout library
# when running on something that looks like Ruby 1.8.x. See:
# http://ph7spot.com/articles/system_timer
# We don't want to bother trying to load SystemTimer on jruby and
# ruby 1.9+.
if
defined?
(
JRUBY_VERSION
)
||
(
RUBY_VERSION
>=
'1.9'
)
require
'timeout'
RedisTimer
=
Timeout
else
require
'system_timer'
RedisTimer
=
SystemTimer
end
rescue
LoadError
=>
e
puts
"[redis-rb] Could not load SystemTimer gem, falling back to Ruby's slower/unsafe timeout library:
#{
e
.
message
}
"
require
'timeout'
RedisTimer
=
Timeout
end
##
# This class represents a redis server instance.
class
Server
##
# The host the redis server is running on.
attr_reader
:host
##
# The port the redis server is listening on.
attr_reader
:port
##
# A text status string describing the state of the server.
attr_reader
:status
##
# Create a new Redis::Server object for the redis instance
# listening on the given host and port.
def
initialize
(
host
,
port
=
DEFAULT_PORT
,
timeout
=
10
)
raise
ArgumentError
,
"No host specified"
if
host
.
nil?
or
host
.
empty?
raise
ArgumentError
,
"No port specified"
if
port
.
nil?
or
port
.
to_i
.
zero?
@host
=
host
@port
=
port
.
to_i
@sock
=
nil
@status
=
'NOT CONNECTED'
@timeout
=
timeout
end
##
# Return a string representation of the server object.
def
inspect
"<Redis::Server: %s:%d (%s)>"
%
[
@host
,
@port
,
@status
]
end
##
# Try to connect to the redis server targeted by this object.
# Returns the connected socket object on success or nil on failure.
def
socket
return
@sock
if
socket_alive?
close
# Attempt to connect if not already connected.
begin
@sock
=
connect_to
(
@host
,
@port
,
@timeout
)
@sock
.
setsockopt
Socket
::
IPPROTO_TCP
,
Socket
::
TCP_NODELAY
,
1
@status
=
'CONNECTED'
rescue
Errno
::
EPIPE
,
Errno
::
ECONNREFUSED
=>
e
puts
"Socket died... :
#{
e
}
\n
"
if
$debug
retry
rescue
SocketError
,
SystemCallError
,
IOError
=>
err
puts
"Unable to open socket:
#{
err
.
class
.
name
}
,
#{
err
.
message
}
"
if
$debug
end
@sock
end
def
connect_to
(
host
,
port
,
timeout
=
nil
)
socket
=
TCPSocket
.
new
(
host
,
port
)
socket
.
set_encoding
(
Encoding
::
BINARY
)
if
socket
.
respond_to?
(
:set_encoding
)
if
timeout
socket
.
instance_eval
<<-
EOR
alias :blocking_readline :readline
def readline(*args)
RedisTimer.timeout(
#{
timeout
}
) do
self.blocking_readline(*args)
end
end
alias :blocking_read :read
def read(*args)
RedisTimer.timeout(
#{
timeout
}
) do
self.blocking_read(*args)
end
end
alias :blocking_write :write
def write(*args)
RedisTimer.timeout(
#{
timeout
}
) do
self.blocking_write(*args)
end
end
EOR
end
socket
end
# Close the connection to the redis server targeted by this
# object.
def
close
@sock
.
close
if
!
@sock
.
nil?
&&
!
@sock
.
closed?
@sock
=
nil
@status
=
"NOT CONNECTED"
end
private
def
socket_alive?
#BTM - TODO - FileStat is borked under JRuby
unless
defined?
(
JRUBY_VERSION
)
!
@sock
.
nil?
&&
!
@sock
.
closed?
&&
@sock
.
stat
.
readable?
else
!
@sock
.
nil?
&&
!
@sock
.
closed?
end
end
end
client-libraries/ruby/redis-rb.gemspec
View file @
69664139
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
Gem
::
Specification
.
new
do
|
s
|
Gem
::
Specification
.
new
do
|
s
|
s
.
name
=
%q{redis}
s
.
name
=
%q{redis}
s
.
version
=
"0.0.
3.
5"
s
.
version
=
"0.0.5"
s
.
required_rubygems_version
=
Gem
::
Requirement
.
new
(
">= 0"
)
if
s
.
respond_to?
:required_rubygems_version
=
s
.
required_rubygems_version
=
Gem
::
Requirement
.
new
(
">= 0"
)
if
s
.
respond_to?
:required_rubygems_version
=
s
.
authors
=
[
"Ezra Zygmuntowicz"
,
"Taylor Weibley"
,
"Matthew Clark"
]
s
.
authors
=
[
"Ezra Zygmuntowicz"
,
"Taylor Weibley"
,
"Matthew Clark"
]
...
...
client-libraries/ruby/spec/redis_spec.rb
View file @
69664139
...
@@ -22,7 +22,7 @@ describe "redis" do
...
@@ -22,7 +22,7 @@ describe "redis" do
end
end
after
(
:each
)
do
after
(
:each
)
do
@r
.
keys
(
'*'
).
each
{
|
k
|
@r
.
del
ete
k
}
@r
.
keys
(
'*'
).
each
{
|
k
|
@r
.
del
k
}
end
end
after
(
:all
)
do
after
(
:all
)
do
...
@@ -30,7 +30,7 @@ describe "redis" do
...
@@ -30,7 +30,7 @@ describe "redis" do
end
end
it
'should be able to PING'
do
it
'should be able to PING'
do
@r
.
ping
.
should
==
true
@r
.
ping
.
should
==
'PONG'
end
end
it
"should be able to GET a key"
do
it
"should be able to GET a key"
do
...
@@ -62,22 +62,22 @@ describe "redis" do
...
@@ -62,22 +62,22 @@ describe "redis" do
@r
[
'foo'
].
should
==
nil
@r
[
'foo'
].
should
==
nil
end
end
it
"should be able to SETNX
(set_unless_exists)
"
do
it
"should be able to SETNX"
do
@r
[
'foo'
]
=
'nik'
@r
[
'foo'
]
=
'nik'
@r
[
'foo'
].
should
==
'nik'
@r
[
'foo'
].
should
==
'nik'
@r
.
set
_unless_exists
'foo'
,
'bar'
@r
.
set
nx
'foo'
,
'bar'
@r
[
'foo'
].
should
==
'nik'
@r
[
'foo'
].
should
==
'nik'
end
end
#
#
it
"should be able to INCR
(increment)
a key"
do
it
"should be able to INCR a key"
do
@r
.
del
ete
(
'counter'
)
@r
.
del
(
'counter'
)
@r
.
incr
(
'counter'
).
should
==
1
@r
.
incr
(
'counter'
).
should
==
1
@r
.
incr
(
'counter'
).
should
==
2
@r
.
incr
(
'counter'
).
should
==
2
@r
.
incr
(
'counter'
).
should
==
3
@r
.
incr
(
'counter'
).
should
==
3
end
end
#
#
it
"should be able to DECR
(decrement)
a key"
do
it
"should be able to DECR a key"
do
@r
.
del
ete
(
'counter'
)
@r
.
del
(
'counter'
)
@r
.
incr
(
'counter'
).
should
==
1
@r
.
incr
(
'counter'
).
should
==
1
@r
.
incr
(
'counter'
).
should
==
2
@r
.
incr
(
'counter'
).
should
==
2
@r
.
incr
(
'counter'
).
should
==
3
@r
.
incr
(
'counter'
).
should
==
3
...
@@ -85,24 +85,24 @@ describe "redis" do
...
@@ -85,24 +85,24 @@ describe "redis" do
@r
.
decr
(
'counter'
,
2
).
should
==
0
@r
.
decr
(
'counter'
,
2
).
should
==
0
end
end
#
#
it
"should be able to RANDKEY
(return a random key)
"
do
it
"should be able to RANDKEY"
do
@r
.
randkey
.
should_not
be_nil
@r
.
randkey
.
should_not
be_nil
end
end
#
#
it
"should be able to RENAME a key"
do
it
"should be able to RENAME a key"
do
@r
.
del
ete
'foo'
@r
.
del
'foo'
@r
.
del
ete
'bar'
@r
.
del
'bar'
@r
[
'foo'
]
=
'hi'
@r
[
'foo'
]
=
'hi'
@r
.
rename
!
'foo'
,
'bar'
@r
.
rename
'foo'
,
'bar'
@r
[
'bar'
].
should
==
'hi'
@r
[
'bar'
].
should
==
'hi'
end
end
#
#
it
"should be able to RENAMENX
(rename unless the new key already exists)
a key"
do
it
"should be able to RENAMENX a key"
do
@r
.
del
ete
'foo'
@r
.
del
'foo'
@r
.
del
ete
'bar'
@r
.
del
'bar'
@r
[
'foo'
]
=
'hi'
@r
[
'foo'
]
=
'hi'
@r
[
'bar'
]
=
'ohai'
@r
[
'bar'
]
=
'ohai'
lambda
{
@r
.
rename
'foo'
,
'bar'
}.
should
raise_error
(
RedisRenameError
)
@r
.
rename
nx
'foo'
,
'bar'
@r
[
'bar'
].
should
==
'ohai'
@r
[
'bar'
].
should
==
'ohai'
end
end
#
#
...
@@ -117,251 +117,224 @@ describe "redis" do
...
@@ -117,251 +117,224 @@ describe "redis" do
#
#
it
"should be able to EXPIRE a key"
do
it
"should be able to EXPIRE a key"
do
@r
[
'foo'
]
=
'bar'
@r
[
'foo'
]
=
'bar'
@r
.
expire
(
'foo'
,
1
)
@r
.
expire
'foo'
,
1
@r
[
'foo'
].
should
==
"bar"
@r
[
'foo'
].
should
==
"bar"
sleep
2
sleep
2
@r
[
'foo'
].
should
==
nil
@r
[
'foo'
].
should
==
nil
end
end
#
#
it
"should be able to EXISTS
(check if key exists)
"
do
it
"should be able to EXISTS"
do
@r
[
'foo'
]
=
'nik'
@r
[
'foo'
]
=
'nik'
@r
.
key?
(
'foo'
).
should
be_true
@r
.
exists
(
'foo'
).
should
be_true
@r
.
del
ete
'foo'
@r
.
del
'foo'
@r
.
key?
(
'foo'
).
should
be_false
@r
.
exists
(
'foo'
).
should
be_false
end
end
#
#
it
"should be able to KEYS(glob for keys)"
do
it
"should be able to KEYS"
do
@r
.
keys
(
"f*"
).
each
do
|
key
|
@r
.
keys
(
"f*"
).
each
{
|
key
|
@r
.
del
key
}
@r
.
delete
key
end
@r
[
'f'
]
=
'nik'
@r
[
'f'
]
=
'nik'
@r
[
'fo'
]
=
'nak'
@r
[
'fo'
]
=
'nak'
@r
[
'foo'
]
=
'qux'
@r
[
'foo'
]
=
'qux'
@r
.
keys
(
"f*"
).
sort
.
should
==
[
'f'
,
'fo'
,
'foo'
].
sort
@r
.
keys
(
"f*"
).
sort
.
should
==
[
'f'
,
'fo'
,
'foo'
].
sort
end
end
#
#
BTM - TODO
it
"should be able to check the TYPE of a key"
do
it
"should be able to check the TYPE of a key"
do
@r
[
'foo'
]
=
'nik'
@r
[
'foo'
]
=
'nik'
@r
.
type?
(
'foo'
).
should
==
"string"
@r
.
type
(
'foo'
).
should
==
"string"
@r
.
delete
'foo'
@r
.
del
'foo'
@r
.
type?
(
'foo'
).
should
==
"none"
@r
.
type
(
'foo'
).
should
==
"none"
end
end
#
#
it
"should be able to push to the head of a list"
do
it
"should be able to push to the head of a list (LPUSH)"
do
@r
.
push_head
"list"
,
'hello'
@r
.
lpush
"list"
,
'hello'
@r
.
push_head
"list"
,
42
@r
.
lpush
"list"
,
42
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
llen
(
'list'
).
should
==
2
@r
.
pop_head
(
'list'
).
should
==
'42'
@r
.
lpop
(
'list'
).
should
==
'42'
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able to push to the tail of a list (RPUSH)"
do
it
"should be able to push to the tail of a list"
do
@r
.
rpush
"list"
,
'hello'
@r
.
push_tail
"list"
,
'hello'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
1
@r
.
list_length
(
'list'
).
should
==
1
end
@r
.
delete
(
'list'
)
#
end
it
"should be able to pop the tail of a list (RPOP)"
do
#
@r
.
rpush
"list"
,
'hello'
it
"should be able to pop the tail of a list"
do
@r
.
rpush
"list"
,
'goodbye'
@r
.
push_tail
"list"
,
'hello'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
push_tail
"list"
,
'goodbye'
@r
.
llen
(
'list'
).
should
==
2
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
rpop
(
'list'
).
should
==
'goodbye'
@r
.
list_length
(
'list'
).
should
==
2
end
@r
.
pop_tail
(
'list'
).
should
==
'goodbye'
#
@r
.
delete
(
'list'
)
it
"should be able to pop the head of a list (LPOP)"
do
end
@r
.
rpush
"list"
,
'hello'
#
@r
.
rpush
"list"
,
'goodbye'
it
"should be able to pop the head of a list"
do
@r
.
type
(
'list'
).
should
==
"list"
@r
.
push_tail
"list"
,
'hello'
@r
.
llen
(
'list'
).
should
==
2
@r
.
push_tail
"list"
,
'goodbye'
@r
.
lpop
(
'list'
).
should
==
'hello'
@r
.
type?
(
'list'
).
should
==
"list"
end
@r
.
list_length
(
'list'
).
should
==
2
#
@r
.
pop_head
(
'list'
).
should
==
'hello'
it
"should be able to get the length of a list (LLEN)"
do
@r
.
delete
(
'list'
)
@r
.
rpush
"list"
,
'hello'
end
@r
.
rpush
"list"
,
'goodbye'
#
@r
.
type
(
'list'
).
should
==
"list"
it
"should be able to get the length of a list"
do
@r
.
llen
(
'list'
).
should
==
2
@r
.
push_tail
"list"
,
'hello'
end
@r
.
push_tail
"list"
,
'goodbye'
#
@r
.
type?
(
'list'
).
should
==
"list"
it
"should be able to get a range of values from a list (LRANGE)"
do
@r
.
list_length
(
'list'
).
should
==
2
@r
.
rpush
"list"
,
'hello'
@r
.
delete
(
'list'
)
@r
.
rpush
"list"
,
'goodbye'
end
@r
.
rpush
"list"
,
'1'
#
@r
.
rpush
"list"
,
'2'
it
"should be able to get a range of values from a list"
do
@r
.
rpush
"list"
,
'3'
@r
.
push_tail
"list"
,
'hello'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
push_tail
"list"
,
'goodbye'
@r
.
llen
(
'list'
).
should
==
5
@r
.
push_tail
"list"
,
'1'
@r
.
lrange
(
'list'
,
2
,
-
1
).
should
==
[
'1'
,
'2'
,
'3'
]
@r
.
push_tail
"list"
,
'2'
end
@r
.
push_tail
"list"
,
'3'
#
@r
.
type?
(
'list'
).
should
==
"list"
it
"should be able to trim a list (LTRIM)"
do
@r
.
list_length
(
'list'
).
should
==
5
@r
.
rpush
"list"
,
'hello'
@r
.
list_range
(
'list'
,
2
,
-
1
).
should
==
[
'1'
,
'2'
,
'3'
]
@r
.
rpush
"list"
,
'goodbye'
@r
.
delete
(
'list'
)
@r
.
rpush
"list"
,
'1'
end
@r
.
rpush
"list"
,
'2'
#
@r
.
rpush
"list"
,
'3'
it
"should be able to trim a list"
do
@r
.
type
(
'list'
).
should
==
"list"
@r
.
push_tail
"list"
,
'hello'
@r
.
llen
(
'list'
).
should
==
5
@r
.
push_tail
"list"
,
'goodbye'
@r
.
ltrim
'list'
,
0
,
1
@r
.
push_tail
"list"
,
'1'
@r
.
llen
(
'list'
).
should
==
2
@r
.
push_tail
"list"
,
'2'
@r
.
lrange
(
'list'
,
0
,
-
1
).
should
==
[
'hello'
,
'goodbye'
]
@r
.
push_tail
"list"
,
'3'
end
@r
.
type?
(
'list'
).
should
==
"list"
#
@r
.
list_length
(
'list'
).
should
==
5
it
"should be able to get a value by indexing into a list (LINDEX)"
do
@r
.
list_trim
'list'
,
0
,
1
@r
.
rpush
"list"
,
'hello'
@r
.
list_length
(
'list'
).
should
==
2
@r
.
rpush
"list"
,
'goodbye'
@r
.
list_range
(
'list'
,
0
,
-
1
).
should
==
[
'hello'
,
'goodbye'
]
@r
.
type
(
'list'
).
should
==
"list"
@r
.
delete
(
'list'
)
@r
.
llen
(
'list'
).
should
==
2
end
@r
.
lindex
(
'list'
,
1
).
should
==
'goodbye'
#
end
it
"should be able to get a value by indexing into a list"
do
#
@r
.
push_tail
"list"
,
'hello'
it
"should be able to set a value by indexing into a list (LSET)"
do
@r
.
push_tail
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'hello'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
rpush
"list"
,
'hello'
@r
.
list_length
(
'list'
).
should
==
2
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_index
(
'list'
,
1
).
should
==
'goodbye'
@r
.
llen
(
'list'
).
should
==
2
@r
.
delete
(
'list'
)
@r
.
lset
(
'list'
,
1
,
'goodbye'
).
should
==
'OK'
end
@r
.
lindex
(
'list'
,
1
).
should
==
'goodbye'
#
end
it
"should be able to set a value by indexing into a list"
do
#
@r
.
push_tail
"list"
,
'hello'
it
"should be able to remove values from a list (LREM)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
rpush
"list"
,
'goodbye'
@r
.
list_length
(
'list'
).
should
==
2
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_set
(
'list'
,
1
,
'goodbye'
).
should
be_true
@r
.
llen
(
'list'
).
should
==
2
@r
.
list_index
(
'list'
,
1
).
should
==
'goodbye'
@r
.
lrem
(
'list'
,
1
,
'hello'
).
should
==
1
@r
.
delete
(
'list'
)
@r
.
lrange
(
'list'
,
0
,
-
1
).
should
==
[
'goodbye'
]
end
end
#
#
it
"should be able to remove values from a list LREM"
do
it
"should be able add members to a set (SADD)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
sadd
"set"
,
'key1'
@r
.
push_tail
"list"
,
'goodbye'
@r
.
sadd
"set"
,
'key2'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'set'
).
should
==
"set"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
scard
(
'set'
).
should
==
2
@r
.
list_rm
(
'list'
,
1
,
'hello'
).
should
==
1
@r
.
smembers
(
'set'
).
sort
.
should
==
[
'key1'
,
'key2'
].
sort
@r
.
list_range
(
'list'
,
0
,
-
1
).
should
==
[
'goodbye'
]
end
@r
.
delete
(
'list'
)
#
end
it
"should be able delete members to a set (SREM)"
do
#
@r
.
sadd
"set"
,
'key1'
it
"should be able add members to a set"
do
@r
.
sadd
"set"
,
'key2'
@r
.
set_add
"set"
,
'key1'
@r
.
type
(
'set'
).
should
==
"set"
@r
.
set_add
"set"
,
'key2'
@r
.
scard
(
'set'
).
should
==
2
@r
.
type?
(
'set'
).
should
==
"set"
@r
.
smembers
(
'set'
).
sort
.
should
==
[
'key1'
,
'key2'
].
sort
@r
.
set_count
(
'set'
).
should
==
2
@r
.
srem
(
'set'
,
'key1'
)
@r
.
set_members
(
'set'
).
sort
.
should
==
[
'key1'
,
'key2'
].
sort
@r
.
scard
(
'set'
).
should
==
1
@r
.
delete
(
'set'
)
@r
.
smembers
(
'set'
).
should
==
[
'key2'
]
end
end
#
#
it
"should be able delete members to a set"
do
it
"should be able count the members of a set (SCARD)"
do
@r
.
set_add
"set"
,
'key1'
@r
.
sadd
"set"
,
'key1'
@r
.
set_add
"set"
,
'key2'
@r
.
sadd
"set"
,
'key2'
@r
.
type?
(
'set'
).
should
==
"set"
@r
.
type
(
'set'
).
should
==
"set"
@r
.
set_count
(
'set'
).
should
==
2
@r
.
scard
(
'set'
).
should
==
2
@r
.
set_members
(
'set'
).
should
==
Set
.
new
([
'key1'
,
'key2'
])
end
@r
.
set_delete
(
'set'
,
'key1'
)
#
@r
.
set_count
(
'set'
).
should
==
1
it
"should be able test for set membership (SISMEMBER)"
do
@r
.
set_members
(
'set'
).
should
==
Set
.
new
([
'key2'
])
@r
.
sadd
"set"
,
'key1'
@r
.
delete
(
'set'
)
@r
.
sadd
"set"
,
'key2'
end
@r
.
type
(
'set'
).
should
==
"set"
#
@r
.
scard
(
'set'
).
should
==
2
it
"should be able count the members of a set"
do
@r
.
sismember
(
'set'
,
'key1'
).
should
be_true
@r
.
set_add
"set"
,
'key1'
@r
.
sismember
(
'set'
,
'key2'
).
should
be_true
@r
.
set_add
"set"
,
'key2'
@r
.
sismember
(
'set'
,
'notthere'
).
should
be_false
@r
.
type?
(
'set'
).
should
==
"set"
end
@r
.
set_count
(
'set'
).
should
==
2
#
@r
.
delete
(
'set'
)
it
"should be able to do set intersection (SINTER)"
do
end
@r
.
sadd
"set"
,
'key1'
#
@r
.
sadd
"set"
,
'key2'
it
"should be able test for set membership"
do
@r
.
sadd
"set2"
,
'key2'
@r
.
set_add
"set"
,
'key1'
@r
.
sinter
(
'set'
,
'set2'
).
should
==
[
'key2'
]
@r
.
set_add
"set"
,
'key2'
end
@r
.
type?
(
'set'
).
should
==
"set"
#
@r
.
set_count
(
'set'
).
should
==
2
it
"should be able to do set intersection and store the results in a key (SINTERSTORE)"
do
@r
.
set_member?
(
'set'
,
'key1'
).
should
be_true
@r
.
sadd
"set"
,
'key1'
@r
.
set_member?
(
'set'
,
'key2'
).
should
be_true
@r
.
sadd
"set"
,
'key2'
@r
.
set_member?
(
'set'
,
'notthere'
).
should
be_false
@r
.
sadd
"set2"
,
'key2'
@r
.
delete
(
'set'
)
@r
.
sinterstore
(
'newone'
,
'set'
,
'set2'
).
should
==
1
end
@r
.
smembers
(
'newone'
).
should
==
[
'key2'
]
#
end
it
"should be able to do set intersection"
do
#
@r
.
set_add
"set"
,
'key1'
it
"should be able to do set union (SUNION)"
do
@r
.
set_add
"set"
,
'key2'
@r
.
sadd
"set"
,
'key1'
@r
.
set_add
"set2"
,
'key2'
@r
.
sadd
"set"
,
'key2'
@r
.
set_intersect
(
'set'
,
'set2'
).
should
==
Set
.
new
([
'key2'
])
@r
.
sadd
"set2"
,
'key2'
@r
.
delete
(
'set'
)
@r
.
sadd
"set2"
,
'key3'
end
@r
.
sunion
(
'set'
,
'set2'
).
sort
.
should
==
[
'key1'
,
'key2'
,
'key3'
].
sort
#
end
it
"should be able to do set intersection and store the results in a key"
do
#
@r
.
set_add
"set"
,
'key1'
it
"should be able to do set union and store the results in a key (SUNIONSTORE)"
do
@r
.
set_add
"set"
,
'key2'
@r
.
sadd
"set"
,
'key1'
@r
.
set_add
"set2"
,
'key2'
@r
.
sadd
"set"
,
'key2'
@r
.
set_inter_store
(
'newone'
,
'set'
,
'set2'
).
should
==
'OK'
@r
.
sadd
"set2"
,
'key2'
@r
.
set_members
(
'newone'
).
should
==
Set
.
new
([
'key2'
])
@r
.
sadd
"set2"
,
'key3'
@r
.
delete
(
'set'
)
@r
.
sunionstore
(
'newone'
,
'set'
,
'set2'
).
should
==
3
@r
.
delete
(
'set2'
)
@r
.
smembers
(
'newone'
).
sort
.
should
==
[
'key1'
,
'key2'
,
'key3'
].
sort
end
end
#
#
it
"should be able to do set union"
do
it
"should be able to do set difference (SDIFF)"
do
@r
.
set_add
"set"
,
'key1'
@r
.
sadd
"set"
,
'a'
@r
.
set_add
"set"
,
'key2'
@r
.
sadd
"set"
,
'b'
@r
.
set_add
"set2"
,
'key2'
@r
.
sadd
"set2"
,
'b'
@r
.
set_add
"set2"
,
'key3'
@r
.
sadd
"set2"
,
'c'
@r
.
set_union
(
'set'
,
'set2'
).
should
==
Set
.
new
([
'key1'
,
'key2'
,
'key3'
])
@r
.
sdiff
(
'set'
,
'set2'
).
should
==
[
'a'
]
@r
.
delete
(
'set'
)
end
@r
.
delete
(
'set2'
)
#
end
it
"should be able to do set difference and store the results in a key (SDIFFSTORE)"
do
#
@r
.
sadd
"set"
,
'a'
it
"should be able to do set union and store the results in a key"
do
@r
.
sadd
"set"
,
'b'
@r
.
set_add
"set"
,
'key1'
@r
.
sadd
"set2"
,
'b'
@r
.
set_add
"set"
,
'key2'
@r
.
sadd
"set2"
,
'c'
@r
.
set_add
"set2"
,
'key2'
@r
.
sdiffstore
(
'newone'
,
'set'
,
'set2'
)
@r
.
set_add
"set2"
,
'key3'
@r
.
smembers
(
'newone'
).
should
==
[
'a'
]
@r
.
set_union_store
(
'newone'
,
'set'
,
'set2'
).
should
==
'OK'
end
@r
.
set_members
(
'newone'
).
should
==
Set
.
new
([
'key1'
,
'key2'
,
'key3'
])
#
@r
.
delete
(
'set'
)
it
"should be able move elements from one set to another (SMOVE)"
do
@r
.
delete
(
'set2'
)
@r
.
sadd
'set1'
,
'a'
end
@r
.
sadd
'set1'
,
'b'
#
@r
.
sadd
'set2'
,
'x'
it
"should be able to do set difference"
do
@r
.
smove
(
'set1'
,
'set2'
,
'a'
).
should
be_true
@r
.
set_add
"set"
,
'a'
@r
.
sismember
(
'set2'
,
'a'
).
should
be_true
@r
.
set_add
"set"
,
'b'
@r
.
set_add
"set2"
,
'b'
@r
.
set_add
"set2"
,
'c'
@r
.
set_diff
(
'set'
,
'set2'
).
should
==
Set
.
new
([
'a'
])
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
#
it
"should be able to do set difference and store the results in a key"
do
@r
.
set_add
"set"
,
'a'
@r
.
set_add
"set"
,
'b'
@r
.
set_add
"set2"
,
'b'
@r
.
set_add
"set2"
,
'c'
@r
.
set_diff_store
(
'newone'
,
'set'
,
'set2'
)
@r
.
set_members
(
'newone'
).
should
==
Set
.
new
([
'a'
])
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
#
it
"should be able move elements from one set to another"
do
@r
.
set_add
'set1'
,
'a'
@r
.
set_add
'set1'
,
'b'
@r
.
set_add
'set2'
,
'x'
@r
.
set_move
(
'set1'
,
'set2'
,
'a'
).
should
==
true
@r
.
set_member?
(
'set2'
,
'a'
).
should
==
true
@r
.
delete
(
'set1'
)
@r
.
delete
(
'set1'
)
end
end
#
#
it
"should be able to do crazy SORT queries"
do
it
"should be able to do crazy SORT queries"
do
@r
[
'dog_1'
]
=
'louie'
@r
[
'dog_1'
]
=
'louie'
@r
.
push
_tail
'dogs'
,
1
@r
.
r
push
'dogs'
,
1
@r
[
'dog_2'
]
=
'lucy'
@r
[
'dog_2'
]
=
'lucy'
@r
.
push
_tail
'dogs'
,
2
@r
.
r
push
'dogs'
,
2
@r
[
'dog_3'
]
=
'max'
@r
[
'dog_3'
]
=
'max'
@r
.
push
_tail
'dogs'
,
3
@r
.
r
push
'dogs'
,
3
@r
[
'dog_4'
]
=
'taj'
@r
[
'dog_4'
]
=
'taj'
@r
.
push
_tail
'dogs'
,
4
@r
.
r
push
'dogs'
,
4
@r
.
sort
(
'dogs'
,
:get
=>
'dog_*'
,
:limit
=>
[
0
,
1
]).
should
==
[
'louie'
]
@r
.
sort
(
'dogs'
,
:get
=>
'dog_*'
,
:limit
=>
[
0
,
1
]).
should
==
[
'louie'
]
@r
.
sort
(
'dogs'
,
:get
=>
'dog_*'
,
:limit
=>
[
0
,
1
],
:order
=>
'desc alpha'
).
should
==
[
'taj'
]
@r
.
sort
(
'dogs'
,
:get
=>
'dog_*'
,
:limit
=>
[
0
,
1
],
:order
=>
'desc alpha'
).
should
==
[
'taj'
]
end
end
...
@@ -369,16 +342,16 @@ describe "redis" do
...
@@ -369,16 +342,16 @@ describe "redis" do
it
"should be able to handle array of :get using SORT"
do
it
"should be able to handle array of :get using SORT"
do
@r
[
'dog:1:name'
]
=
'louie'
@r
[
'dog:1:name'
]
=
'louie'
@r
[
'dog:1:breed'
]
=
'mutt'
@r
[
'dog:1:breed'
]
=
'mutt'
@r
.
push
_tail
'dogs'
,
1
@r
.
r
push
'dogs'
,
1
@r
[
'dog:2:name'
]
=
'lucy'
@r
[
'dog:2:name'
]
=
'lucy'
@r
[
'dog:2:breed'
]
=
'poodle'
@r
[
'dog:2:breed'
]
=
'poodle'
@r
.
push
_tail
'dogs'
,
2
@r
.
r
push
'dogs'
,
2
@r
[
'dog:3:name'
]
=
'max'
@r
[
'dog:3:name'
]
=
'max'
@r
[
'dog:3:breed'
]
=
'hound'
@r
[
'dog:3:breed'
]
=
'hound'
@r
.
push
_tail
'dogs'
,
3
@r
.
r
push
'dogs'
,
3
@r
[
'dog:4:name'
]
=
'taj'
@r
[
'dog:4:name'
]
=
'taj'
@r
[
'dog:4:breed'
]
=
'terrier'
@r
[
'dog:4:breed'
]
=
'terrier'
@r
.
push
_tail
'dogs'
,
4
@r
.
r
push
'dogs'
,
4
@r
.
sort
(
'dogs'
,
:get
=>
[
'dog:*:name'
,
'dog:*:breed'
],
:limit
=>
[
0
,
1
]).
should
==
[
'louie'
,
'mutt'
]
@r
.
sort
(
'dogs'
,
:get
=>
[
'dog:*:name'
,
'dog:*:breed'
],
:limit
=>
[
0
,
1
]).
should
==
[
'louie'
,
'mutt'
]
@r
.
sort
(
'dogs'
,
:get
=>
[
'dog:*:name'
,
'dog:*:breed'
],
:limit
=>
[
0
,
1
],
:order
=>
'desc alpha'
).
should
==
[
'taj'
,
'terrier'
]
@r
.
sort
(
'dogs'
,
:get
=>
[
'dog:*:name'
,
'dog:*:breed'
],
:limit
=>
[
0
,
1
],
:order
=>
'desc alpha'
).
should
==
[
'taj'
,
'terrier'
]
end
end
...
@@ -392,13 +365,13 @@ describe "redis" do
...
@@ -392,13 +365,13 @@ describe "redis" do
it
"should be able to flush the database"
do
it
"should be able to flush the database"
do
@r
[
'key1'
]
=
'keyone'
@r
[
'key1'
]
=
'keyone'
@r
[
'key2'
]
=
'keytwo'
@r
[
'key2'
]
=
'keytwo'
@r
.
keys
(
'*'
).
sort
.
should
==
[
'foo'
,
'key1'
,
'key2'
]
#foo from before
@r
.
keys
(
'*'
).
sort
.
should
==
[
'foo'
,
'key1'
,
'key2'
]
.
sort
#foo from before
@r
.
flush
_
db
@r
.
flushdb
@r
.
keys
(
'*'
).
should
==
[]
@r
.
keys
(
'*'
).
should
==
[]
end
end
#
#
it
"should be able to provide the last save time"
do
it
"should be able to provide the last save time
(LASTSAVE)
"
do
savetime
=
@r
.
last
_
save
savetime
=
@r
.
lastsave
Time
.
at
(
savetime
).
class
.
should
==
Time
Time
.
at
(
savetime
).
class
.
should
==
Time
Time
.
at
(
savetime
).
should
<=
Time
.
now
Time
.
at
(
savetime
).
should
<=
Time
.
now
end
end
...
@@ -411,13 +384,12 @@ describe "redis" do
...
@@ -411,13 +384,12 @@ describe "redis" do
end
end
it
"should bgsave"
do
it
"should bgsave"
do
lambda
{
@r
.
bgsave
}
.
should
_not
raise_error
(
RedisError
)
@r
.
bgsave
.
should
==
'OK'
end
end
it
"should handle multiple servers"
do
it
"should handle multiple servers"
do
require
'dist_redis'
require
'dist_redis'
@r
=
DistRedis
.
new
(
'localhost:6379'
,
'127.0.0.1:6379'
)
@r
=
DistRedis
.
new
(
:hosts
=>
[
'localhost:6379'
,
'127.0.0.1:6379'
],
:db
=>
15
)
@r
.
select_db
(
15
)
# use database 15 for testing so we dont accidentally step on you real data
100
.
times
do
|
idx
|
100
.
times
do
|
idx
|
@r
[
idx
]
=
"foo
#{
idx
}
"
@r
[
idx
]
=
"foo
#{
idx
}
"
...
@@ -430,16 +402,13 @@ describe "redis" do
...
@@ -430,16 +402,13 @@ describe "redis" do
it
"should be able to pipeline writes"
do
it
"should be able to pipeline writes"
do
@r
.
pipelined
do
|
pipeline
|
@r
.
pipelined
do
|
pipeline
|
pipeline
.
push
_head
"
list
"
,
"hello"
pipeline
.
l
push
'
list
'
,
"hello"
pipeline
.
push
_head
"
list
"
,
42
pipeline
.
l
push
'
list
'
,
42
end
end
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
llen
(
'list'
).
should
==
2
@r
.
pop_head
(
'list'
).
should
==
'42'
@r
.
lpop
(
'list'
).
should
==
'42'
@r
.
delete
(
'list'
)
end
end
it
"should select db on connection"
it
"should re-select db on reconnection"
end
end
client-libraries/ruby/spec/server_spec.rb
deleted
100644 → 0
View file @
d90b352d
require
File
.
dirname
(
__FILE__
)
+
'/spec_helper'
describe
"Server"
do
before
(
:each
)
do
@server
=
Server
.
new
'localhost'
,
'6379'
end
it
"should checkout active connections"
do
threads
=
[]
10
.
times
do
threads
<<
Thread
.
new
do
lambda
{
socket
=
@server
.
socket
socket
.
close
socket
.
write
(
"INFO
\r\n
"
)
socket
.
read
(
1
)
}.
should_not
raise_error
(
Exception
)
end
end
end
end
client-libraries/ruby_2/rubyredis.rb
deleted
100644 → 0
View file @
d90b352d
# RubyRedis is an alternative implementatin of Ruby client library written
# by Salvatore Sanfilippo.
#
# The aim of this library is to create an alternative client library that is
# much simpler and does not implement every command explicitly but uses
# method_missing instead.
require
'socket'
require
'set'
begin
if
(
RUBY_VERSION
>=
'1.9'
)
require
'timeout'
RedisTimer
=
Timeout
else
require
'system_timer'
RedisTimer
=
SystemTimer
end
rescue
LoadError
RedisTimer
=
nil
end
class
RedisClient
BulkCommands
=
{
"set"
=>
true
,
"setnx"
=>
true
,
"rpush"
=>
true
,
"lpush"
=>
true
,
"lset"
=>
true
,
"lrem"
=>
true
,
"sadd"
=>
true
,
"srem"
=>
true
,
"sismember"
=>
true
,
"echo"
=>
true
,
"getset"
=>
true
,
"smove"
=>
true
}
ConvertToBool
=
lambda
{
|
r
|
r
==
0
?
false
:
r
}
ReplyProcessor
=
{
"exists"
=>
ConvertToBool
,
"sismember"
=>
ConvertToBool
,
"sadd"
=>
ConvertToBool
,
"srem"
=>
ConvertToBool
,
"smove"
=>
ConvertToBool
,
"move"
=>
ConvertToBool
,
"setnx"
=>
ConvertToBool
,
"del"
=>
ConvertToBool
,
"renamenx"
=>
ConvertToBool
,
"expire"
=>
ConvertToBool
,
"keys"
=>
lambda
{
|
r
|
r
.
split
(
" "
)},
"info"
=>
lambda
{
|
r
|
info
=
{}
r
.
each_line
{
|
kv
|
k
,
v
=
kv
.
split
(
":"
,
2
).
map
{
|
x
|
x
.
chomp
}
info
[
k
.
to_sym
]
=
v
}
info
}
}
Aliases
=
{
"flush_db"
=>
"flushdb"
,
"flush_all"
=>
"flushall"
,
"last_save"
=>
"lastsave"
,
"key?"
=>
"exists"
,
"delete"
=>
"del"
,
"randkey"
=>
"randomkey"
,
"list_length"
=>
"llen"
,
"push_tail"
=>
"rpush"
,
"push_head"
=>
"lpush"
,
"pop_tail"
=>
"rpop"
,
"pop_head"
=>
"lpop"
,
"list_set"
=>
"lset"
,
"list_range"
=>
"lrange"
,
"list_trim"
=>
"ltrim"
,
"list_index"
=>
"lindex"
,
"list_rm"
=>
"lrem"
,
"set_add"
=>
"sadd"
,
"set_delete"
=>
"srem"
,
"set_count"
=>
"scard"
,
"set_member?"
=>
"sismember"
,
"set_members"
=>
"smembers"
,
"set_intersect"
=>
"sinter"
,
"set_intersect_store"
=>
"sinterstore"
,
"set_inter_store"
=>
"sinterstore"
,
"set_union"
=>
"sunion"
,
"set_union_store"
=>
"sunionstore"
,
"set_diff"
=>
"sdiff"
,
"set_diff_store"
=>
"sdiffstore"
,
"set_move"
=>
"smove"
,
"set_unless_exists"
=>
"setnx"
,
"rename_unless_exists"
=>
"renamenx"
}
def
initialize
(
opts
=
{})
@host
=
opts
[
:host
]
||
'127.0.0.1'
@port
=
opts
[
:port
]
||
6379
@db
=
opts
[
:db
]
||
0
@timeout
=
opts
[
:timeout
]
||
0
connect_to_server
end
def
to_s
"Redis Client connected to
#{
@host
}
:
#{
@port
}
against DB
#{
@db
}
"
end
def
connect_to_server
@sock
=
connect_to
(
@host
,
@port
,
@timeout
==
0
?
nil
:
@timeout
)
call_command
([
"select"
,
@db
])
if
@db
!=
0
end
def
connect_to
(
host
,
port
,
timeout
=
nil
)
# We support connect() timeout only if system_timer is availabe
# or if we are running against Ruby >= 1.9
# Timeout reading from the socket instead will be supported anyway.
if
@timeout
!=
0
and
RedisTimer
begin
sock
=
TCPSocket
.
new
(
host
,
port
,
0
)
rescue
Timeout
::
Error
@sock
=
nil
raise
Timeout
::
Error
,
"Timeout connecting to the server"
end
else
sock
=
TCPSocket
.
new
(
host
,
port
,
0
)
end
sock
.
setsockopt
Socket
::
IPPROTO_TCP
,
Socket
::
TCP_NODELAY
,
1
# If the timeout is set we set the low level socket options in order
# to make sure a blocking read will return after the specified number
# of seconds. This hack is from memcached ruby client.
if
timeout
secs
=
Integer
(
timeout
)
usecs
=
Integer
((
timeout
-
secs
)
*
1_000_000
)
optval
=
[
secs
,
usecs
].
pack
(
"l_2"
)
sock
.
setsockopt
Socket
::
SOL_SOCKET
,
Socket
::
SO_RCVTIMEO
,
optval
sock
.
setsockopt
Socket
::
SOL_SOCKET
,
Socket
::
SO_SNDTIMEO
,
optval
end
sock
end
def
method_missing
(
*
argv
)
call_command
(
argv
)
end
def
call_command
(
argv
)
# this wrapper to raw_call_command handle reconnection on socket
# error. We try to reconnect just one time, otherwise let the error
# araise.
connect_to_server
if
!
@sock
begin
raw_call_command
(
argv
)
rescue
Errno
::
ECONNRESET
@sock
.
close
connect_to_server
raw_call_command
(
argv
)
end
end
def
raw_call_command
(
argv
)
bulk
=
nil
argv
[
0
]
=
argv
[
0
].
to_s
.
downcase
argv
[
0
]
=
Aliases
[
argv
[
0
]]
if
Aliases
[
argv
[
0
]]
if
BulkCommands
[
argv
[
0
]]
and
argv
.
length
>
1
bulk
=
argv
[
-
1
].
to_s
argv
[
-
1
]
=
bulk
.
length
end
@sock
.
write
(
argv
.
join
(
" "
)
+
"
\r\n
"
)
@sock
.
write
(
bulk
+
"
\r\n
"
)
if
bulk
# Post process the reply if needed
processor
=
ReplyProcessor
[
argv
[
0
]]
processor
?
processor
.
call
(
read_reply
)
:
read_reply
end
def
select
(
*
args
)
raise
"SELECT not allowed, use the :db option when creating the object"
end
def
[]
(
key
)
get
(
key
)
end
def
[]=
(
key
,
value
)
set
(
key
,
value
)
end
def
sort
(
key
,
opts
=
{})
cmd
=
[]
cmd
<<
"SORT
#{
key
}
"
cmd
<<
"BY
#{
opts
[
:by
]
}
"
if
opts
[
:by
]
cmd
<<
"GET
#{
[
opts
[
:get
]].
flatten
*
' GET '
}
"
if
opts
[
:get
]
cmd
<<
"
#{
opts
[
:order
]
}
"
if
opts
[
:order
]
cmd
<<
"LIMIT
#{
opts
[
:limit
].
join
(
' '
)
}
"
if
opts
[
:limit
]
call_command
(
cmd
)
end
def
incr
(
key
,
increment
=
nil
)
call_command
(
increment
?
[
"incrby"
,
key
,
increment
]
:
[
"incr"
,
key
])
end
def
decr
(
key
,
decrement
=
nil
)
call_command
(
decrement
?
[
"decrby"
,
key
,
decrement
]
:
[
"decr"
,
key
])
end
def
read_reply
# We read the first byte using read() mainly because gets() is
# immune to raw socket timeouts.
begin
rtype
=
@sock
.
read
(
1
)
rescue
Errno
::
EAGAIN
# We want to make sure it reconnects on the next command after the
# timeout. Otherwise the server may reply in the meantime leaving
# the protocol in a desync status.
@sock
=
nil
raise
Errno
::
EAGAIN
,
"Timeout reading from the socket"
end
raise
Errno
::
ECONNRESET
,
"Connection lost"
if
!
rtype
line
=
@sock
.
gets
case
rtype
when
"-"
raise
"-"
+
line
.
strip
when
"+"
line
.
strip
when
":"
line
.
to_i
when
"$"
bulklen
=
line
.
to_i
return
nil
if
bulklen
==
-
1
data
=
@sock
.
read
(
bulklen
)
@sock
.
read
(
2
)
# CRLF
data
when
"*"
objects
=
line
.
to_i
return
nil
if
bulklen
==
-
1
res
=
[]
objects
.
times
{
res
<<
read_reply
}
res
else
raise
"Protocol error, got '
#{
rtype
}
' as initial reply byte"
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment