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
Hide 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
...
@@ -8,19 +8,13 @@ class Redis
...
@@ -8,19 +8,13 @@ class Redis
@redis
=
redis
@redis
=
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
def
finish
write_and_read
end
end
def
write_and_read
def
execute
@redis
.
execute_command
(
@commands
.
join
,
true
)
@redis
.
call_command
(
@commands
)
@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
]
@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
info
=
{}
x
=
execute_command
(
"INFO
\r\n
"
)
x
.
each_line
do
|
kv
|
k
,
v
=
kv
.
split
(
':'
,
2
)
k
,
v
=
k
.
chomp
,
v
=
v
.
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
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
)
ReplyProcessor
=
{
execute_command
(
"GET
#{
key
}
\r\n
"
)
"exists"
=>
ConvertToBool
,
end
"sismember"
=>
ConvertToBool
,
"sadd"
=>
ConvertToBool
,
def
mget
(
*
keys
)
"srem"
=>
ConvertToBool
,
execute_command
(
"MGET
#{
keys
.
join
(
' '
)
}
\r\n
"
)
"smove"
=>
ConvertToBool
,
end
"move"
=>
ConvertToBool
,
"setnx"
=>
ConvertToBool
,
def
incr
(
key
,
increment
=
nil
)
"del"
=>
ConvertToBool
,
if
increment
"renamenx"
=>
ConvertToBool
,
execute_command
(
"INCRBY
#{
key
}
#{
increment
}
\r\n
"
)
"expire"
=>
ConvertToBool
,
else
"keys"
=>
lambda
{
|
r
|
r
.
split
(
" "
)},
execute_command
(
"INCR
#{
key
}
\r\n
"
)
"info"
=>
lambda
{
|
r
|
end
info
=
{}
end
r
.
each_line
{
|
kv
|
k
,
v
=
kv
.
split
(
":"
,
2
).
map
{
|
x
|
x
.
chomp
}
def
decr
(
key
,
decrement
=
nil
)
info
[
k
.
to_sym
]
=
v
if
decrement
}
execute_command
(
"DECRBY
#{
key
}
#{
decrement
}
\r\n
"
)
info
else
}
execute_command
(
"DECR
#{
key
}
\r\n
"
)
}
end
end
Aliases
=
{
"flush_db"
=>
"flushdb"
,
def
randkey
"flush_all"
=>
"flushall"
,
execute_command
(
"RANDOMKEY
\r\n
"
)
"last_save"
=>
"lastsave"
,
end
"key?"
=>
"exists"
,
"delete"
=>
"del"
,
def
list_length
(
key
)
"randkey"
=>
"randomkey"
,
case
i
=
execute_command
(
"LLEN
#{
key
}
\r\n
"
)
"list_length"
=>
"llen"
,
when
-
2
"push_tail"
=>
"rpush"
,
raise
RedisError
,
"key:
#{
key
}
does not hold a list value"
"push_head"
=>
"lpush"
,
else
"pop_tail"
=>
"rpop"
,
i
"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
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
end
end
def
type?
(
key
)
execute_command
(
"TYPE
#{
key
}
\r\n
"
)
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
"
)
end
def
pop_head
(
key
)
execute_command
(
"LPOP
#{
key
}
\r\n
"
)
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
"
)
end
def
list_trim
(
key
,
start
,
ending
)
def
to_s
execute_command
(
"LTRIM
#{
key
}
#{
start
}
#{
ending
}
\r\n
"
)
"Redis Client connected to
#{
@host
}
:
#{
@port
}
against DB
#{
@db
}
"
end
def
list_index
(
key
,
index
)
execute_command
(
"LINDEX
#{
key
}
#{
index
}
\r\n
"
)
end
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"
else
num
end
end
end
def
set_add
(
key
,
member
)
def
connect_to_server
case
execute_command
(
"SADD
#{
key
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
@sock
=
connect_to
(
@host
,
@port
,
@timeout
==
0
?
nil
:
@timeout
)
when
1
call_command
([
"select"
,
@db
])
if
@db
!=
0
true
when
0
false
when
-
2
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
end
end
end
def
set_delete
(
key
,
member
)
def
connect_to
(
host
,
port
,
timeout
=
nil
)
case
execute_command
(
"SREM
#{
key
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
# We support connect() timeout only if system_timer is availabe
when
1
# or if we are running against Ruby >= 1.9
true
# Timeout reading from the socket instead will be supported anyway.
when
0
if
@timeout
!=
0
and
RedisTimer
false
begin
when
-
2
sock
=
TCPSocket
.
new
(
host
,
port
)
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
rescue
Timeout
::
Error
@sock
=
nil
raise
Timeout
::
Error
,
"Timeout connecting to the server"
end
else
sock
=
TCPSocket
.
new
(
host
,
port
)
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
end
end
def
set_count
(
key
)
def
method_missing
(
*
argv
)
case
i
=
execute_command
(
"SCARD
#{
key
}
\r\n
"
)
call_command
(
argv
)
when
-
2
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
else
i
end
end
end
def
set_member?
(
key
,
member
)
def
call_command
(
argv
)
case
execute_command
(
"SISMEMBER
#{
key
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
puts
argv
.
inspect
if
$debug
when
1
# this wrapper to raw_call_command handle reconnection on socket
true
# error. We try to reconnect just one time, otherwise let the error
when
0
# araise.
false
connect_to_server
if
!
@sock
when
-
2
begin
raise
RedisError
,
"key:
#{
key
}
contains a non set value"
raw_call_command
(
argv
)
rescue
Errno
::
ECONNRESET
@sock
.
close
connect_to_server
raw_call_command
(
argv
)
end
end
end
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
def
set_inter_store
(
destkey
,
*
keys
)
def
raw_call_command
(
argvp
)
execute_command
(
"SINTERSTORE
#{
destkey
}
#{
keys
.
join
(
' '
)
}
\r\n
"
)
pipeline
=
argvp
[
0
].
is_a?
(
Array
)
end
unless
pipeline
def
set_union
(
*
keys
)
argvv
=
[
argvp
]
Set
.
new
(
execute_command
(
"SUNION
#{
keys
.
join
(
' '
)
}
\r\n
"
))
else
end
argvv
=
argvp
end
def
set_union_store
(
destkey
,
*
keys
)
execute_command
(
"SUNIONSTORE
#{
destkey
}
#{
keys
.
join
(
' '
)
}
\r\n
"
)
command
=
''
end
argvv
.
each
do
|
argv
|
def
set_diff
(
*
keys
)
bulk
=
nil
Set
.
new
(
execute_command
(
"SDIFF
#{
keys
.
join
(
' '
)
}
\r\n
"
))
argv
[
0
]
=
argv
[
0
].
to_s
.
downcase
end
argv
[
0
]
=
Aliases
[
argv
[
0
]]
if
Aliases
[
argv
[
0
]]
if
BulkCommands
[
argv
[
0
]]
and
argv
.
length
>
1
def
set_diff_store
(
destkey
,
*
keys
)
bulk
=
argv
[
-
1
].
to_s
execute_command
(
"SDIFFSTORE
#{
destkey
}
#{
keys
.
join
(
' '
)
}
\r\n
"
)
argv
[
-
1
]
=
bulk
.
length
end
end
command
<<
argv
.
join
(
' '
)
+
"
\r\n
"
command
<<
bulk
+
"
\r\n
"
if
bulk
end
@sock
.
write
(
command
)
results
=
argvv
.
map
do
|
argv
|
processor
=
ReplyProcessor
[
argv
[
0
]]
processor
?
processor
.
call
(
read_reply
)
:
read_reply
end
return
pipeline
?
results
:
results
[
0
]
end
def
se
t_move
(
srckey
,
destkey
,
member
)
def
se
lect
(
*
args
)
execute_command
(
"SMOVE
#{
srckey
}
#{
destkey
}
#{
value_to_wire
(
member
)
}
\r\n
"
)
==
1
raise
"SELECT not allowed, use the :db option when creating the object"
end
end
def
sort
(
key
,
opts
=
{})
def
[]
(
key
)
cmd
=
"SORT
#{
key
}
"
get
(
key
)
cmd
<<
" BY
#{
opts
[
:by
]
}
"
if
opts
[
:by
]
end
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
def
[]=
(
key
,
value
)
execute_command
(
"DBSIZE
\r\n
"
)
set
(
key
,
value
)
end
end
def
expire
(
key
,
expiry
=
nil
)
def
set
(
key
,
value
,
expiry
=
nil
)
execute_command
(
"EXPIRE
#{
key
}
#{
expiry
}
\r\n
"
)
==
1
call_command
([
:set
,
key
,
value
])
end
expire
(
key
,
expiry
)
unless
expiry
.
nil?
end
def
set_unless_exists
(
key
,
val
)
def
sort
(
key
,
opts
=
{})
execute_command
(
"SETNX
#{
key
}
#{
value_to_wire
(
val
)
}
\r\n
"
)
==
1
cmd
=
[]
end
cmd
<<
"SORT
#{
key
}
"
cmd
<<
"BY
#{
opts
[
:by
]
}
"
if
opts
[
:by
]
def
bulk_reply
cmd
<<
"GET
#{
[
opts
[
:get
]].
flatten
*
' GET '
}
"
if
opts
[
:get
]
begin
cmd
<<
"
#{
opts
[
:order
]
}
"
if
opts
[
:order
]
x
=
read
cmd
<<
"LIMIT
#{
opts
[
:limit
].
join
(
' '
)
}
"
if
opts
[
:limit
]
puts
"bulk_reply read value is
#{
x
.
inspect
}
"
if
$debug
call_command
(
cmd
)
return
x
rescue
=>
e
puts
"error in bulk_reply
#{
e
}
"
if
$debug
nil
end
end
end
def
incr
(
key
,
increment
=
nil
)
def
write
(
data
)
call_command
(
increment
?
[
"incrby"
,
key
,
increment
]
:
[
"incr"
,
key
])
puts
"writing:
#{
data
}
"
if
$debug
@socket
.
write
(
data
)
end
def
read
(
length
=
0
)
length
=
read_proto
unless
length
>
0
res
=
@socket
.
read
(
length
)
puts
"read is
#{
res
.
inspect
}
"
if
$debug
res
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
end
list
end
def
decr
(
key
,
decrement
=
nil
)
call_command
(
decrement
?
[
"decrby"
,
key
,
decrement
]
:
[
"decr"
,
key
])
def
get_reply
begin
r
=
read
(
1
)
raise
RedisError
if
(
r
==
"
\r
"
||
r
==
"
\n
"
)
rescue
RedisError
retry
end
end
r
end
# Ruby defines a now deprecated type method so we need to override it here
# since it will never hit method_missing
def
status_code_reply
def
type
(
key
)
begin
call_command
([
'type'
,
key
])
res
=
read_proto
if
res
.
index
(
'-'
)
==
0
raise
RedisError
,
res
else
true
end
rescue
RedisError
raise
RedisError
end
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
def
get_response
def
quit
rtype
=
get_reply
call_command
([
'quit'
])
puts
"reply_type is
#{
rtype
.
inspect
}
"
if
$debug
rescue
Errno
::
ECONNRESET
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
end
puts
"single_line value is
#{
buff
[
0
..-
3
].
inspect
}
"
if
$debug
buff
[
0
..-
3
]
def
pipelined
(
&
block
)
end
pipeline
=
Pipeline
.
new
self
yield
pipeline
def
read_socket
pipeline
.
execute
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
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
read_reply
def
value_to_wire
(
value
)
# We read the first byte using read() mainly because gets() is
value_str
=
value
.
to_s
# immune to raw socket timeouts.
if
value_str
.
respond_to?
(
:bytesize
)
begin
value_size
=
value_str
.
bytesize
rtype
=
@sock
.
read
(
1
)
else
rescue
Errno
::
EAGAIN
value_size
=
value_str
.
size
# 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
"
#{
value_size
}
\r\n
#{
value_str
}
"
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
.
del
ete
'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"
do
it
"should be able to push to the tail of a list (RPUSH)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
1
@r
.
llen
(
'list'
).
should
==
1
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able to pop the tail of a list"
do
it
"should be able to pop the tail of a list (RPOP)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
push_tail
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'goodbye'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
llen
(
'list'
).
should
==
2
@r
.
pop_tail
(
'list'
).
should
==
'goodbye'
@r
.
rpop
(
'list'
).
should
==
'goodbye'
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able to pop the head of a list"
do
it
"should be able to pop the head of a list (LPOP)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
push_tail
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'goodbye'
@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
==
'hello'
@r
.
lpop
(
'list'
).
should
==
'hello'
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able to get the length of a list"
do
it
"should be able to get the length of a list (LLEN)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
push_tail
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'goodbye'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
llen
(
'list'
).
should
==
2
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able to get a range of values from a list"
do
it
"should be able to get a range of values from a list (LRANGE)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
push_tail
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'goodbye'
@r
.
push_tail
"list"
,
'1'
@r
.
rpush
"list"
,
'1'
@r
.
push_tail
"list"
,
'2'
@r
.
rpush
"list"
,
'2'
@r
.
push_tail
"list"
,
'3'
@r
.
rpush
"list"
,
'3'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
5
@r
.
llen
(
'list'
).
should
==
5
@r
.
list_range
(
'list'
,
2
,
-
1
).
should
==
[
'1'
,
'2'
,
'3'
]
@r
.
lrange
(
'list'
,
2
,
-
1
).
should
==
[
'1'
,
'2'
,
'3'
]
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able to trim a list"
do
it
"should be able to trim a list (LTRIM)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
push_tail
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'goodbye'
@r
.
push_tail
"list"
,
'1'
@r
.
rpush
"list"
,
'1'
@r
.
push_tail
"list"
,
'2'
@r
.
rpush
"list"
,
'2'
@r
.
push_tail
"list"
,
'3'
@r
.
rpush
"list"
,
'3'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
5
@r
.
llen
(
'list'
).
should
==
5
@r
.
list_trim
'list'
,
0
,
1
@r
.
ltrim
'list'
,
0
,
1
@r
.
list_length
(
'list'
).
should
==
2
@r
.
llen
(
'list'
).
should
==
2
@r
.
list_range
(
'list'
,
0
,
-
1
).
should
==
[
'hello'
,
'goodbye'
]
@r
.
lrange
(
'list'
,
0
,
-
1
).
should
==
[
'hello'
,
'goodbye'
]
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able to get a value by indexing into a list"
do
it
"should be able to get a value by indexing into a list (LINDEX)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
push_tail
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'goodbye'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
llen
(
'list'
).
should
==
2
@r
.
list_index
(
'list'
,
1
).
should
==
'goodbye'
@r
.
lindex
(
'list'
,
1
).
should
==
'goodbye'
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able to set a value by indexing into a list"
do
it
"should be able to set a value by indexing into a list (LSET)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
llen
(
'list'
).
should
==
2
@r
.
list_set
(
'list'
,
1
,
'goodbye'
).
should
be_true
@r
.
lset
(
'list'
,
1
,
'goodbye'
).
should
==
'OK'
@r
.
list_index
(
'list'
,
1
).
should
==
'goodbye'
@r
.
lindex
(
'list'
,
1
).
should
==
'goodbye'
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able to remove values from a list LREM"
do
it
"should be able to remove values from a list (LREM)"
do
@r
.
push_tail
"list"
,
'hello'
@r
.
rpush
"list"
,
'hello'
@r
.
push_tail
"list"
,
'goodbye'
@r
.
rpush
"list"
,
'goodbye'
@r
.
type?
(
'list'
).
should
==
"list"
@r
.
type
(
'list'
).
should
==
"list"
@r
.
list_length
(
'list'
).
should
==
2
@r
.
llen
(
'list'
).
should
==
2
@r
.
list_rm
(
'list'
,
1
,
'hello'
).
should
==
1
@r
.
lrem
(
'list'
,
1
,
'hello'
).
should
==
1
@r
.
list_range
(
'list'
,
0
,
-
1
).
should
==
[
'goodbye'
]
@r
.
lrange
(
'list'
,
0
,
-
1
).
should
==
[
'goodbye'
]
@r
.
delete
(
'list'
)
end
end
#
#
it
"should be able add members to a set"
do
it
"should be able add members to a set (SADD)"
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'
).
sort
.
should
==
[
'key1'
,
'key2'
].
sort
@r
.
smembers
(
'set'
).
sort
.
should
==
[
'key1'
,
'key2'
].
sort
@r
.
delete
(
'set'
)
end
end
#
#
it
"should be able delete members to a set"
do
it
"should be able delete members to a set (SREM)"
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'
])
@r
.
smembers
(
'set'
).
sort
.
should
==
[
'key1'
,
'key2'
].
sort
@r
.
set_delete
(
'set'
,
'key1'
)
@r
.
srem
(
'set'
,
'key1'
)
@r
.
set_count
(
'set'
).
should
==
1
@r
.
scard
(
'set'
).
should
==
1
@r
.
set_members
(
'set'
).
should
==
Set
.
new
([
'key2'
])
@r
.
smembers
(
'set'
).
should
==
[
'key2'
]
@r
.
delete
(
'set'
)
end
end
#
#
it
"should be able count the members of 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
.
delete
(
'set'
)
end
end
#
#
it
"should be able test for set membership"
do
it
"should be able test for set membership (SISMEMBER)"
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_member?
(
'set'
,
'key1'
).
should
be_true
@r
.
sismember
(
'set'
,
'key1'
).
should
be_true
@r
.
set_member?
(
'set'
,
'key2'
).
should
be_true
@r
.
sismember
(
'set'
,
'key2'
).
should
be_true
@r
.
set_member?
(
'set'
,
'notthere'
).
should
be_false
@r
.
sismember
(
'set'
,
'notthere'
).
should
be_false
@r
.
delete
(
'set'
)
end
end
#
#
it
"should be able to do set intersection"
do
it
"should be able to do set intersection (SINTER)"
do
@r
.
set_add
"set"
,
'key1'
@r
.
sadd
"set"
,
'key1'
@r
.
set_add
"set"
,
'key2'
@r
.
sadd
"set"
,
'key2'
@r
.
set_add
"set2"
,
'key2'
@r
.
sadd
"set2"
,
'key2'
@r
.
set_intersect
(
'set'
,
'set2'
).
should
==
Set
.
new
([
'key2'
])
@r
.
sinter
(
'set'
,
'set2'
).
should
==
[
'key2'
]
@r
.
delete
(
'set'
)
end
end
#
#
it
"should be able to do set intersection and store the results in a key"
do
it
"should be able to do set intersection and store the results in a key (SINTERSTORE)"
do
@r
.
set_add
"set"
,
'key1'
@r
.
sadd
"set"
,
'key1'
@r
.
set_add
"set"
,
'key2'
@r
.
sadd
"set"
,
'key2'
@r
.
set_add
"set2"
,
'key2'
@r
.
sadd
"set2"
,
'key2'
@r
.
set_inter_store
(
'newone'
,
'set'
,
'set2'
).
should
==
'OK'
@r
.
sinterstore
(
'newone'
,
'set'
,
'set2'
).
should
==
1
@r
.
set_members
(
'newone'
).
should
==
Set
.
new
([
'key2'
])
@r
.
smembers
(
'newone'
).
should
==
[
'key2'
]
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
end
#
#
it
"should be able to do set union"
do
it
"should be able to do set union (SUNION)"
do
@r
.
set_add
"set"
,
'key1'
@r
.
sadd
"set"
,
'key1'
@r
.
set_add
"set"
,
'key2'
@r
.
sadd
"set"
,
'key2'
@r
.
set_add
"set2"
,
'key2'
@r
.
sadd
"set2"
,
'key2'
@r
.
set_add
"set2"
,
'key3'
@r
.
sadd
"set2"
,
'key3'
@r
.
set_union
(
'set'
,
'set2'
).
should
==
Set
.
new
([
'key1'
,
'key2'
,
'key3'
])
@r
.
sunion
(
'set'
,
'set2'
).
sort
.
should
==
[
'key1'
,
'key2'
,
'key3'
].
sort
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
end
#
#
it
"should be able to do set union and store the results in a key"
do
it
"should be able to do set union and store the results in a key (SUNIONSTORE)"
do
@r
.
set_add
"set"
,
'key1'
@r
.
sadd
"set"
,
'key1'
@r
.
set_add
"set"
,
'key2'
@r
.
sadd
"set"
,
'key2'
@r
.
set_add
"set2"
,
'key2'
@r
.
sadd
"set2"
,
'key2'
@r
.
set_add
"set2"
,
'key3'
@r
.
sadd
"set2"
,
'key3'
@r
.
set_union_store
(
'newone'
,
'set'
,
'set2'
).
should
==
'OK'
@r
.
sunionstore
(
'newone'
,
'set'
,
'set2'
).
should
==
3
@r
.
set_members
(
'newone'
).
should
==
Set
.
new
([
'key1'
,
'key2'
,
'key3'
])
@r
.
smembers
(
'newone'
).
sort
.
should
==
[
'key1'
,
'key2'
,
'key3'
].
sort
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
end
#
#
it
"should be able to do set difference"
do
it
"should be able to do set difference (SDIFF)"
do
@r
.
set_add
"set"
,
'a'
@r
.
sadd
"set"
,
'a'
@r
.
set_add
"set"
,
'b'
@r
.
sadd
"set"
,
'b'
@r
.
set_add
"set2"
,
'b'
@r
.
sadd
"set2"
,
'b'
@r
.
set_add
"set2"
,
'c'
@r
.
sadd
"set2"
,
'c'
@r
.
set_diff
(
'set'
,
'set2'
).
should
==
Set
.
new
([
'a'
])
@r
.
sdiff
(
'set'
,
'set2'
).
should
==
[
'a'
]
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
end
#
#
it
"should be able to do set difference and store the results in a key"
do
it
"should be able to do set difference and store the results in a key (SDIFFSTORE)"
do
@r
.
set_add
"set"
,
'a'
@r
.
sadd
"set"
,
'a'
@r
.
set_add
"set"
,
'b'
@r
.
sadd
"set"
,
'b'
@r
.
set_add
"set2"
,
'b'
@r
.
sadd
"set2"
,
'b'
@r
.
set_add
"set2"
,
'c'
@r
.
sadd
"set2"
,
'c'
@r
.
set_diff_store
(
'newone'
,
'set'
,
'set2'
)
@r
.
sdiffstore
(
'newone'
,
'set'
,
'set2'
)
@r
.
set_members
(
'newone'
).
should
==
Set
.
new
([
'a'
])
@r
.
smembers
(
'newone'
).
should
==
[
'a'
]
@r
.
delete
(
'set'
)
@r
.
delete
(
'set2'
)
end
end
#
#
it
"should be able move elements from one set to another"
do
it
"should be able move elements from one set to another
(SMOVE)
"
do
@r
.
s
et_
add
'set1'
,
'a'
@r
.
sadd
'set1'
,
'a'
@r
.
s
et_
add
'set1'
,
'b'
@r
.
sadd
'set1'
,
'b'
@r
.
s
et_
add
'set2'
,
'x'
@r
.
sadd
'set2'
,
'x'
@r
.
s
et_
move
(
'set1'
,
'set2'
,
'a'
).
should
==
true
@r
.
smove
(
'set1'
,
'set2'
,
'a'
).
should
be_
true
@r
.
s
et_
member
?
(
'set2'
,
'a'
).
should
==
true
@r
.
s
is
member
(
'set2'
,
'a'
).
should
be_
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