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'
require
'hash_ring'
class
DistRedis
attr_reader
:ring
def
initialize
(
*
servers
)
srvs
=
[]
servers
.
each
do
|
s
|
server
,
port
=
s
.
split
(
':'
)
srvs
<<
Redis
.
new
(
:host
=>
server
,
:port
=>
port
)
def
initialize
(
opts
=
{})
hosts
=
[]
db
=
opts
[
:db
]
||
nil
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
@ring
=
HashRing
.
new
srvs
@ring
=
HashRing
.
new
hosts
end
def
node_for_key
(
key
)
...
...
client-libraries/ruby/lib/pipeline.rb
View file @
69664139
...
...
@@ -9,18 +9,12 @@ class Redis
@commands
=
[]
end
def
execute_command
(
data
)
@commands
<<
data
write_and_read
if
@commands
.
size
>=
BUFFER_SIZE
def
call_command
(
command
)
@commands
<<
command
end
def
finish
write_and_read
end
def
write_and_read
@redis
.
execute_command
(
@commands
.
join
,
true
)
@redis
.
read_socket
def
execute
@redis
.
call_command
(
@commands
)
@commands
.
clear
end
...
...
client-libraries/ruby/lib/redis.rb
View file @
69664139
require
'socket'
require
'set'
require
File
.
join
(
File
.
dirname
(
__FILE__
),
'server'
)
require
File
.
join
(
File
.
dirname
(
__FILE__
),
'pipeline'
)
class
RedisError
<
StandardError
end
class
RedisRenameError
<
StandardError
begin
if
(
RUBY_VERSION
>=
'1.9'
)
require
'timeout'
RedisTimer
=
Timeout
else
require
'system_timer'
RedisTimer
=
SystemTimer
end
rescue
LoadError
RedisTimer
=
nil
end
class
Redis
ERR
=
"-"
.
freeze
OK
=
'OK'
.
freeze
PONG
=
'PONG'
.
freeze
SINGLE
=
'+'
.
freeze
BULK
=
'$'
.
freeze
MULTI
=
'*'
.
freeze
INT
=
':'
.
freeze
attr_reader
:server
def
initialize
(
opts
=
{})
@opts
=
{
:host
=>
'localhost'
,
:port
=>
'6379'
,
:db
=>
0
}.
merge
(
opts
)
$debug
=
@opts
[
:debug
]
@db
=
@opts
[
:db
]
@server
=
Server
.
new
(
@opts
[
:host
],
@opts
[
:port
],
(
@opts
[
:timeout
]
||
10
))
end
def
pipelined
pipeline
=
Pipeline
.
new
(
self
)
yield
pipeline
pipeline
.
finish
end
def
to_s
"
#{
host
}
:
#{
port
}
->
#{
@db
}
"
end
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
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
do
|
r
|
case
r
when
0
then
false
when
1
then
true
else
r
end
end
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
=
{}
x
=
execute_command
(
"INFO
\r\n
"
)
x
.
each_line
do
|
kv
|
k
,
v
=
kv
.
split
(
':'
,
2
)
k
,
v
=
k
.
chomp
,
v
=
v
.
chomp
r
.
each_line
{
|
kv
|
k
,
v
=
kv
.
split
(
":"
,
2
).
map
{
|
x
|
x
.
chomp
}
info
[
k
.
to_sym
]
=
v
end
}
info
end
def
keys
(
glob
)
execute_command
(
"KEYS
#{
glob
}
\r\n
"
).
split
(
' '
)
end
def
rename!
(
oldkey
,
newkey
)
execute_command
(
"RENAME
#{
oldkey
}
#{
newkey
}
\r\n
"
)
end
def
rename
(
oldkey
,
newkey
)
case
execute_command
(
"RENAMENX
#{
oldkey
}
#{
newkey
}
\r\n
"
)
when
-
1
raise
RedisRenameError
,
"source key:
#{
oldkey
}
does not exist"
when
0
raise
RedisRenameError
,
"target key:
#{
oldkey
}
already exists"
when
-
3
raise
RedisRenameError
,
"source and destination keys are the same"
when
1
true
end
end
def
key?
(
key
)
execute_command
(
"EXISTS
#{
key
}
\r\n
"
)
==
1
end
def
delete
(
key
)
execute_command
(
"DEL
#{
key
}
\r\n
"
)
==
1
end
def
[]
(
key
)
get
(
key
)
end
def
get
(
key
)
execute_command
(
"GET
#{
key
}
\r\n
"
)
end
}
}
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"
,
"type?"
=>
"type"
}
def
mget
(
*
keys
)
execute_command
(
"MGET
#{
keys
.
join
(
' '
)
}
\r\n
"
)
def
initialize
(
opts
=
{})
@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
def
incr
(
key
,
increment
=
nil
)
if
increment
execute_command
(
"INCRBY
#{
key
}
#{
increment
}
\r\n
"
)
else
execute_command
(
"INCR
#{
key
}
\r\n
"
)
end
def
to_s
"Redis Client connected to
#{
@host
}
:
#{
@port
}
against DB
#{
@db
}
"
end
def
decr
(
key
,
decrement
=
nil
)
if
decrement
execute_command
(
"DECRBY
#{
key
}
#{
decrement
}
\r\n
"
)
else
execute_command
(
"DECR
#{
key
}
\r\n
"
)
end
def
connect_to_server
@sock
=
connect_to
(
@host
,
@port
,
@timeout
==
0
?
nil
:
@timeout
)
call_command
([
"select"
,
@db
])
if
@db
!=
0
end
def
randkey
execute_command
(
"RANDOMKEY
\r\n
"
)
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
)
rescue
Timeout
::
Error
@sock
=
nil
raise
Timeout
::
Error
,
"Timeout connecting to the server"
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
i
end
sock
=
TCPSocket
.
new
(
host
,
port
)
end
sock
.
setsockopt
Socket
::
IPPROTO_TCP
,
Socket
::
TCP_NODELAY
,
1
def
type?
(
key
)
execute_command
(
"TYPE
#{
key
}
\r\n
"
)
# 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
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
"
)
sock
end
def
pop_head
(
key
)
execute
_command
(
"LPOP
#{
key
}
\r\n
"
)
def
method_missing
(
*
argv
)
call
_command
(
argv
)
end
def
pop_tail
(
key
)
execute_command
(
"RPOP
#{
key
}
\r\n
"
)
end
def
list_set
(
key
,
index
,
val
)
execute_command
(
"LSET
#{
key
}
#{
index
}
#{
value_to_wire
(
val
)
}
\r\n
"
)
==
OK
end
def
list_range
(
key
,
start
,
ending
)
execute_command
(
"LRANGE
#{
key
}
#{
start
}
#{
ending
}
\r\n
"
)
def
call_command
(
argv
)
puts
argv
.
inspect
if
$debug
# 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
def
list_trim
(
key
,
start
,
ending
)
execute_command
(
"LTRIM
#{
key
}
#{
start
}
#{
ending
}
\r\n
"
)
end
def
list_index
(
key
,
index
)
execute_command
(
"LINDEX
#{
key
}
#{
index
}
\r\n
"
)
end
def
raw_call_command
(
argvp
)
pipeline
=
argvp
[
0
].
is_a?
(
Array
)
def
list_rm
(
key
,
count
,
val
)
case
num
=
execute_command
(
"LREM
#{
key
}
#{
count
}
#{
value_to_wire
(
val
)
}
\r\n
"
)
when
-
1
raise
RedisError
,
"key:
#{
key
}
does not exist"
when
-
2
raise
RedisError
,
"key:
#{
key
}
does not hold a list value"
unless
pipeline
argvv
=
[
argvp
]
else
num
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
argvv
=
argvp
end
def
set_count
(
key
)
case
i
=
execute_command
(
"SCARD
#{
key
}
\r\n
"
)
when
-
2
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
else
i
end
end
command
=
''
def
set_member?
(
key
,
member
)
case
execute_command
(
"SISMEMBER
#{
key
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
when
1
true
when
0
false
when
-
2
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
argvv
.
each
do
|
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
command
<<
argv
.
join
(
' '
)
+
"
\r\n
"
command
<<
bulk
+
"
\r\n
"
if
bulk
end
def
set_members
(
key
)
Set
.
new
(
execute_command
(
"SMEMBERS
#{
key
}
\r\n
"
))
end
def
set_intersect
(
*
keys
)
Set
.
new
(
execute_command
(
"SINTER
#{
keys
.
join
(
' '
)
}
\r\n
"
))
end
@sock
.
write
(
command
)
def
set_inter_store
(
destkey
,
*
keys
)
execute_command
(
"SINTERSTORE
#{
destkey
}
#{
keys
.
join
(
' '
)
}
\r\n
"
)
results
=
argvv
.
map
do
|
argv
|
processor
=
ReplyProcessor
[
argv
[
0
]]
processor
?
processor
.
call
(
read_reply
)
:
read_reply
end
def
set_union
(
*
keys
)
Set
.
new
(
execute_command
(
"SUNION
#{
keys
.
join
(
' '
)
}
\r\n
"
))
return
pipeline
?
results
:
results
[
0
]
end
def
se
t_union_store
(
destkey
,
*
key
s
)
execute_command
(
"SUNIONSTORE
#{
destkey
}
#{
keys
.
join
(
' '
)
}
\r\n
"
)
def
se
lect
(
*
arg
s
)
raise
"SELECT not allowed, use the :db option when creating the object"
end
def
set_diff
(
*
key
s
)
Set
.
new
(
execute_command
(
"SDIFF
#{
keys
.
join
(
' '
)
}
\r\n
"
)
)
def
[]
(
key
)
get
(
key
)
end
def
set_diff_store
(
destkey
,
*
keys
)
execute_command
(
"SDIFFSTORE
#{
destkey
}
#{
keys
.
join
(
' '
)
}
\r\n
"
)
def
[]=
(
key
,
value
)
set
(
key
,
value
)
end
def
set_move
(
srckey
,
destkey
,
member
)
execute_command
(
"SMOVE
#{
srckey
}
#{
destkey
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
==
1
def
set
(
key
,
value
,
expiry
=
nil
)
call_command
([
:set
,
key
,
value
])
expire
(
key
,
expiry
)
unless
expiry
.
nil?
end
def
sort
(
key
,
opts
=
{})
cmd
=
"SORT
#{
key
}
"
cmd
<<
" BY
#{
opts
[
:by
]
}
"
if
opts
[
:by
]
cmd
<<
" GET
#{
[
opts
[
:get
]].
flatten
*
' GET '
}
"
if
opts
[
:get
]
cmd
<<
" INCR
#{
opts
[
:incr
]
}
"
if
opts
[
:incr
]
cmd
<<
" DEL
#{
opts
[
:del
]
}
"
if
opts
[
:del
]
cmd
<<
" DECR
#{
opts
[
:decr
]
}
"
if
opts
[
:decr
]
cmd
<<
"
#{
opts
[
:order
]
}
"
if
opts
[
:order
]
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
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
bulk_reply
begin
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
def
incr
(
key
,
increment
=
nil
)
call_command
(
increment
?
[
"incrby"
,
key
,
increment
]
:
[
"incr"
,
key
])
end
def
write
(
data
)
puts
"writing:
#{
data
}
"
if
$debug
@socket
.
write
(
data
)
def
decr
(
key
,
decrement
=
nil
)
call_command
(
decrement
?
[
"decrby"
,
key
,
decrement
]
:
[
"decr"
,
key
])
end
def
read
(
length
=
0
)
length
=
read_proto
unless
length
>
0
res
=
@socket
.
read
(
length
)
puts
"read is
#{
res
.
inspect
}
"
if
$debug
res
# Ruby defines a now deprecated type method so we need to override it here
# since it will never hit method_missing
def
type
(
key
)
call_command
([
'type'
,
key
])
end
def
multi_bulk
res
=
read_proto
puts
"mb res is
#{
res
.
inspect
}
"
if
$debug
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
def
quit
call_command
([
'quit'
])
rescue
Errno
::
ECONNRESET
end
def
get_reply
begin
r
=
read
(
1
)
raise
RedisError
if
(
r
==
"
\r
"
||
r
==
"
\n
"
)
rescue
RedisError
retry
end
r
def
pipelined
(
&
block
)
pipeline
=
Pipeline
.
new
self
yield
pipeline
pipeline
.
execute
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
res
=
read_proto
if
res
.
index
(
'-'
)
==
0
raise
RedisError
,
res
else
true
end
rescue
RedisError
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"
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
def
get_response
rtype
=
get_reply
puts
"reply_type is
#{
rtype
.
inspect
}
"
if
$debug
raise
Errno
::
ECONNRESET
,
"Connection lost"
if
!
rtype
line
=
@sock
.
gets
case
rtype
when
SINGLE
single_line
when
BULK
bulk_reply
when
MULTI
multi_bulk
when
INT
integer_reply
when
ERR
raise
RedisError
,
single_line
else
raise
RedisError
,
"Unknown response.."
end
end
def
integer_reply
Integer
(
read_proto
)
end
def
single_line
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
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
value_size
=
value_str
.
size
raise
"Protocol error, got '
#{
rtype
}
' as initial reply byte"
end
"
#{
value_size
}
\r\n
#{
value_str
}
"
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 @@
Gem
::
Specification
.
new
do
|
s
|
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
.
authors
=
[
"Ezra Zygmuntowicz"
,
"Taylor Weibley"
,
"Matthew Clark"
]
...
...
client-libraries/ruby/spec/redis_spec.rb
View file @
69664139
...
...
@@ -22,7 +22,7 @@ describe "redis" do
end
after
(
:each
)
do
@r
.
keys
(
'*'
).
each
{
|
k
|
@r
.
del
ete
k
}
@r
.
keys
(
'*'
).
each
{
|
k
|
@r
.
del
k
}
end
after
(
:all
)
do
...
...
@@ -30,7 +30,7 @@ describe "redis" do
end
it
'should be able to PING'
do
@r
.
ping
.
should
==
true
@r
.
ping
.
should
==
'PONG'
end
it
"should be able to GET a key"
do
...
...
@@ -62,22 +62,22 @@ describe "redis" do
@r
[
'foo'
].
should
==
nil
end
it
"should be able to SETNX
(set_unless_exists)
"
do
it
"should be able to SETNX"
do
@r
[
'foo'
]
=
'nik'
@r
[
'foo'
].
should
==
'nik'
@r
.
set
_unless_exists
'foo'
,
'bar'
@r
.
set
nx
'foo'
,
'bar'
@r
[
'foo'
].
should
==
'nik'
end
#
it
"should be able to INCR
(increment)
a key"
do
@r
.
del
ete
(
'counter'
)
it
"should be able to INCR a key"
do
@r
.
del
(
'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
.
del
ete
(
'counter'
)
it
"should be able to DECR a key"
do
@r
.
del
(
'counter'
)
@r
.
incr
(
'counter'
).
should
==
1
@r
.
incr
(
'counter'
).
should
==
2
@r
.
incr
(
'counter'
).
should
==
3
...
...
@@ -85,24 +85,24 @@ describe "redis" do
@r
.
decr
(
'counter'
,
2
).
should
==
0
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
end
#
it
"should be able to RENAME a key"
do
@r
.
del
ete
'foo'
@r
.
del
ete
'bar'
@r
.
del
'foo'
@r
.
del
'bar'
@r
[
'foo'
]
=
'hi'
@r
.
rename
!
'foo'
,
'bar'
@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
.
del
ete
'foo'
@r
.
del
ete
'bar'
it
"should be able to RENAMENX a key"
do
@r
.
del
'foo'
@r
.
del
'bar'
@r
[
'foo'
]
=
'hi'
@r
[
'bar'
]
=
'ohai'
lambda
{
@r
.
rename
'foo'
,
'bar'
}.
should
raise_error
(
RedisRenameError
)
@r
.
rename
nx
'foo'
,
'bar'
@r
[
'bar'
].
should
==
'ohai'
end
#
...
...
@@ -117,251 +117,224 @@ describe "redis" do
#
it
"should be able to EXPIRE a key"
do
@r
[
'foo'
]
=
'bar'
@r
.
expire
(
'foo'
,
1
)
@r
.
expire
'foo'
,
1
@r
[
'foo'
].
should
==
"bar"
sleep
2
@r
[
'foo'
].
should
==
nil
end
#
it
"should be able to EXISTS
(check if key exists)
"
do
it
"should be able to EXISTS"
do
@r
[
'foo'
]
=
'nik'
@r
.
key?
(
'foo'
).
should
be_true
@r
.
del
ete
'foo'
@r
.
key?
(
'foo'
).
should
be_false
@r
.
exists
(
'foo'
).
should
be_true
@r
.
del
'foo'
@r
.
exists
(
'foo'
).
should
be_false
end
#
it
"should be able to KEYS(glob for keys)"
do
@r
.
keys
(
"f*"
).
each
do
|
key
|
@r
.
delete
key
end
it
"should be able to KEYS"
do
@r
.
keys
(
"f*"
).
each
{
|
key
|
@r
.
del
key
}
@r
[
'f'
]
=
'nik'
@r
[
'fo'
]
=
'nak'
@r
[
'foo'
]
=
'qux'
@r
.
keys
(
"f*"
).
sort
.
should
==
[
'f'
,
'fo'
,
'foo'
].
sort
end
#
#
BTM - TODO
it
"should be able to check the TYPE of a key"
do
@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"
,
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
.
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
.
type?
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
pop_tail
(
'list'
).
should
==
'goodbye'
@r
.
delete
(
'list'
)
end
#
it
"should be able to pop the head of a list"
do
@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'
)
end
#
it
"should be able to get the length of a list"
do
@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"
,
'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"
,
'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'
)
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
.
type?
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
list_index
(
'list'
,
1
).
should
==
'goodbye'
@r
.
delete
(
'list'
)
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
.
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'
)
end
#
it
"should be able to remove values from a list LREM"
do
@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'
)
end
#
it
"should be able add members to 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
.
set_members
(
'set'
).
sort
.
should
==
[
'key1'
,
'key2'
].
sort
@r
.
delete
(
'set'
)
end
#
it
"should be able delete members to 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
.
set_members
(
'set'
).
should
==
Set
.
new
([
'key1'
,
'key2'
])
@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
@r
.
set_add
"set"
,
'key1'
@r
.
set_add
"set"
,
'key2'
@r
.
type?
(
'set'
).
should
==
"set"
@r
.
set_count
(
'set'
).
should
==
2
@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
@r
.
set_add
"set"
,
'key1'
@r
.
set_add
"set"
,
'key2'
@r
.
set_add
"set2"
,
'key2'
@r
.
set_inter_store
(
'newone'
,
'set'
,
'set2'
).
should
==
'OK'
@r
.
set_members
(
'newone'
).
should
==
Set
.
new
([
'key2'
])
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
#
it
"should be able to do set union"
do
@r
.
set_add
"set"
,
'key1'
@r
.
set_add
"set"
,
'key2'
@r
.
set_add
"set2"
,
'key2'
@r
.
set_add
"set2"
,
'key3'
@r
.
set_union
(
'set'
,
'set2'
).
should
==
Set
.
new
([
'key1'
,
'key2'
,
'key3'
])
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
#
it
"should be able to do set union and store the results in a key"
do
@r
.
set_add
"set"
,
'key1'
@r
.
set_add
"set"
,
'key2'
@r
.
set_add
"set2"
,
'key2'
@r
.
set_add
"set2"
,
'key3'
@r
.
set_union_store
(
'newone'
,
'set'
,
'set2'
).
should
==
'OK'
@r
.
set_members
(
'newone'
).
should
==
Set
.
new
([
'key1'
,
'key2'
,
'key3'
])
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
#
it
"should be able to do set difference"
do
@r
.
set_add
"set"
,
'a'
@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
.
type
(
'foo'
).
should
==
"string"
@r
.
del
'foo'
@r
.
type
(
'foo'
).
should
==
"none"
end
#
it
"should be able to push to the head of a list (LPUSH)"
do
@r
.
lpush
"list"
,
'hello'
@r
.
lpush
"list"
,
42
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
2
@r
.
lpop
(
'list'
).
should
==
'42'
end
#
it
"should be able to push to the tail of a list (RPUSH)"
do
@r
.
rpush
"list"
,
'hello'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
1
end
#
it
"should be able to pop the tail of a list (RPOP)"
do
@r
.
rpush
"list"
,
'hello'
@r
.
rpush
"list"
,
'goodbye'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
2
@r
.
rpop
(
'list'
).
should
==
'goodbye'
end
#
it
"should be able to pop the head of a list (LPOP)"
do
@r
.
rpush
"list"
,
'hello'
@r
.
rpush
"list"
,
'goodbye'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
2
@r
.
lpop
(
'list'
).
should
==
'hello'
end
#
it
"should be able to get the length of a list (LLEN)"
do
@r
.
rpush
"list"
,
'hello'
@r
.
rpush
"list"
,
'goodbye'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
2
end
#
it
"should be able to get a range of values from a list (LRANGE)"
do
@r
.
rpush
"list"
,
'hello'
@r
.
rpush
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'1'
@r
.
rpush
"list"
,
'2'
@r
.
rpush
"list"
,
'3'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
5
@r
.
lrange
(
'list'
,
2
,
-
1
).
should
==
[
'1'
,
'2'
,
'3'
]
end
#
it
"should be able to trim a list (LTRIM)"
do
@r
.
rpush
"list"
,
'hello'
@r
.
rpush
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'1'
@r
.
rpush
"list"
,
'2'
@r
.
rpush
"list"
,
'3'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
5
@r
.
ltrim
'list'
,
0
,
1
@r
.
llen
(
'list'
).
should
==
2
@r
.
lrange
(
'list'
,
0
,
-
1
).
should
==
[
'hello'
,
'goodbye'
]
end
#
it
"should be able to get a value by indexing into a list (LINDEX)"
do
@r
.
rpush
"list"
,
'hello'
@r
.
rpush
"list"
,
'goodbye'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
2
@r
.
lindex
(
'list'
,
1
).
should
==
'goodbye'
end
#
it
"should be able to set a value by indexing into a list (LSET)"
do
@r
.
rpush
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
2
@r
.
lset
(
'list'
,
1
,
'goodbye'
).
should
==
'OK'
@r
.
lindex
(
'list'
,
1
).
should
==
'goodbye'
end
#
it
"should be able to remove values from a list (LREM)"
do
@r
.
rpush
"list"
,
'hello'
@r
.
rpush
"list"
,
'goodbye'
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
2
@r
.
lrem
(
'list'
,
1
,
'hello'
).
should
==
1
@r
.
lrange
(
'list'
,
0
,
-
1
).
should
==
[
'goodbye'
]
end
#
it
"should be able add members to a set (SADD)"
do
@r
.
sadd
"set"
,
'key1'
@r
.
sadd
"set"
,
'key2'
@r
.
type
(
'set'
).
should
==
"set"
@r
.
scard
(
'set'
).
should
==
2
@r
.
smembers
(
'set'
).
sort
.
should
==
[
'key1'
,
'key2'
].
sort
end
#
it
"should be able delete members to a set (SREM)"
do
@r
.
sadd
"set"
,
'key1'
@r
.
sadd
"set"
,
'key2'
@r
.
type
(
'set'
).
should
==
"set"
@r
.
scard
(
'set'
).
should
==
2
@r
.
smembers
(
'set'
).
sort
.
should
==
[
'key1'
,
'key2'
].
sort
@r
.
srem
(
'set'
,
'key1'
)
@r
.
scard
(
'set'
).
should
==
1
@r
.
smembers
(
'set'
).
should
==
[
'key2'
]
end
#
it
"should be able count the members of a set (SCARD)"
do
@r
.
sadd
"set"
,
'key1'
@r
.
sadd
"set"
,
'key2'
@r
.
type
(
'set'
).
should
==
"set"
@r
.
scard
(
'set'
).
should
==
2
end
#
it
"should be able test for set membership (SISMEMBER)"
do
@r
.
sadd
"set"
,
'key1'
@r
.
sadd
"set"
,
'key2'
@r
.
type
(
'set'
).
should
==
"set"
@r
.
scard
(
'set'
).
should
==
2
@r
.
sismember
(
'set'
,
'key1'
).
should
be_true
@r
.
sismember
(
'set'
,
'key2'
).
should
be_true
@r
.
sismember
(
'set'
,
'notthere'
).
should
be_false
end
#
it
"should be able to do set intersection (SINTER)"
do
@r
.
sadd
"set"
,
'key1'
@r
.
sadd
"set"
,
'key2'
@r
.
sadd
"set2"
,
'key2'
@r
.
sinter
(
'set'
,
'set2'
).
should
==
[
'key2'
]
end
#
it
"should be able to do set intersection and store the results in a key (SINTERSTORE)"
do
@r
.
sadd
"set"
,
'key1'
@r
.
sadd
"set"
,
'key2'
@r
.
sadd
"set2"
,
'key2'
@r
.
sinterstore
(
'newone'
,
'set'
,
'set2'
).
should
==
1
@r
.
smembers
(
'newone'
).
should
==
[
'key2'
]
end
#
it
"should be able to do set union (SUNION)"
do
@r
.
sadd
"set"
,
'key1'
@r
.
sadd
"set"
,
'key2'
@r
.
sadd
"set2"
,
'key2'
@r
.
sadd
"set2"
,
'key3'
@r
.
sunion
(
'set'
,
'set2'
).
sort
.
should
==
[
'key1'
,
'key2'
,
'key3'
].
sort
end
#
it
"should be able to do set union and store the results in a key (SUNIONSTORE)"
do
@r
.
sadd
"set"
,
'key1'
@r
.
sadd
"set"
,
'key2'
@r
.
sadd
"set2"
,
'key2'
@r
.
sadd
"set2"
,
'key3'
@r
.
sunionstore
(
'newone'
,
'set'
,
'set2'
).
should
==
3
@r
.
smembers
(
'newone'
).
sort
.
should
==
[
'key1'
,
'key2'
,
'key3'
].
sort
end
#
it
"should be able to do set difference (SDIFF)"
do
@r
.
sadd
"set"
,
'a'
@r
.
sadd
"set"
,
'b'
@r
.
sadd
"set2"
,
'b'
@r
.
sadd
"set2"
,
'c'
@r
.
sdiff
(
'set'
,
'set2'
).
should
==
[
'a'
]
end
#
it
"should be able to do set difference and store the results in a key (SDIFFSTORE)"
do
@r
.
sadd
"set"
,
'a'
@r
.
sadd
"set"
,
'b'
@r
.
sadd
"set2"
,
'b'
@r
.
sadd
"set2"
,
'c'
@r
.
sdiffstore
(
'newone'
,
'set'
,
'set2'
)
@r
.
smembers
(
'newone'
).
should
==
[
'a'
]
end
#
it
"should be able move elements from one set to another (SMOVE)"
do
@r
.
sadd
'set1'
,
'a'
@r
.
sadd
'set1'
,
'b'
@r
.
sadd
'set2'
,
'x'
@r
.
smove
(
'set1'
,
'set2'
,
'a'
).
should
be_true
@r
.
sismember
(
'set2'
,
'a'
).
should
be_true
@r
.
delete
(
'set1'
)
end
#
it
"should be able to do crazy SORT queries"
do
@r
[
'dog_1'
]
=
'louie'
@r
.
push
_tail
'dogs'
,
1
@r
.
r
push
'dogs'
,
1
@r
[
'dog_2'
]
=
'lucy'
@r
.
push
_tail
'dogs'
,
2
@r
.
r
push
'dogs'
,
2
@r
[
'dog_3'
]
=
'max'
@r
.
push
_tail
'dogs'
,
3
@r
.
r
push
'dogs'
,
3
@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
],
:order
=>
'desc alpha'
).
should
==
[
'taj'
]
end
...
...
@@ -369,16 +342,16 @@ describe "redis" do
it
"should be able to handle array of :get using SORT"
do
@r
[
'dog:1:name'
]
=
'louie'
@r
[
'dog:1:breed'
]
=
'mutt'
@r
.
push
_tail
'dogs'
,
1
@r
.
r
push
'dogs'
,
1
@r
[
'dog:2:name'
]
=
'lucy'
@r
[
'dog:2:breed'
]
=
'poodle'
@r
.
push
_tail
'dogs'
,
2
@r
.
r
push
'dogs'
,
2
@r
[
'dog:3:name'
]
=
'max'
@r
[
'dog:3:breed'
]
=
'hound'
@r
.
push
_tail
'dogs'
,
3
@r
.
r
push
'dogs'
,
3
@r
[
'dog:4:name'
]
=
'taj'
@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
],
:order
=>
'desc alpha'
).
should
==
[
'taj'
,
'terrier'
]
end
...
...
@@ -392,13 +365,13 @@ describe "redis" do
it
"should be able to flush the database"
do
@r
[
'key1'
]
=
'keyone'
@r
[
'key2'
]
=
'keytwo'
@r
.
keys
(
'*'
).
sort
.
should
==
[
'foo'
,
'key1'
,
'key2'
]
#foo from before
@r
.
flush
_
db
@r
.
keys
(
'*'
).
sort
.
should
==
[
'foo'
,
'key1'
,
'key2'
]
.
sort
#foo from before
@r
.
flushdb
@r
.
keys
(
'*'
).
should
==
[]
end
#
it
"should be able to provide the last save time"
do
savetime
=
@r
.
last
_
save
it
"should be able to provide the last save time
(LASTSAVE)
"
do
savetime
=
@r
.
lastsave
Time
.
at
(
savetime
).
class
.
should
==
Time
Time
.
at
(
savetime
).
should
<=
Time
.
now
end
...
...
@@ -411,13 +384,12 @@ describe "redis" do
end
it
"should bgsave"
do
lambda
{
@r
.
bgsave
}
.
should
_not
raise_error
(
RedisError
)
@r
.
bgsave
.
should
==
'OK'
end
it
"should handle multiple servers"
do
require
'dist_redis'
@r
=
DistRedis
.
new
(
'localhost:6379'
,
'127.0.0.1:6379'
)
@r
.
select_db
(
15
)
# use database 15 for testing so we dont accidentally step on you real data
@r
=
DistRedis
.
new
(
:hosts
=>
[
'localhost:6379'
,
'127.0.0.1:6379'
],
:db
=>
15
)
100
.
times
do
|
idx
|
@r
[
idx
]
=
"foo
#{
idx
}
"
...
...
@@ -430,16 +402,13 @@ describe "redis" do
it
"should be able to pipeline writes"
do
@r
.
pipelined
do
|
pipeline
|
pipeline
.
push
_head
"
list
"
,
"hello"
pipeline
.
push
_head
"
list
"
,
42
pipeline
.
l
push
'
list
'
,
"hello"
pipeline
.
l
push
'
list
'
,
42
end
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
pop_head
(
'list'
).
should
==
'42'
@r
.
delete
(
'list'
)
@r
.
type
(
'list'
).
should
==
"list"
@r
.
llen
(
'list'
).
should
==
2
@r
.
lpop
(
'list'
).
should
==
'42'
end
it
"should select db on connection"
it
"should re-select db on reconnection"
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