superfeedr-em-redis 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +1 -1
- data/lib/em-redis.rb +1 -1
- data/lib/em-redis/redis_protocol.rb +97 -45
- data/spec/live_redis_protocol_spec.rb +4 -4
- data/spec/redis_commands_spec.rb +85 -0
- data/spec/redis_commands_spec.rb.orig +731 -0
- data/spec/redis_protocol_spec.rb +5 -5
- data/spec/test_helper.rb +4 -0
- metadata +60 -24
- data/.gitignore +0 -2
data/Rakefile
CHANGED
@@ -18,7 +18,7 @@ Bones {
|
|
18
18
|
authors ['Jonathan Broad', 'Eugene Pimenov', 'Stephan Maka']
|
19
19
|
email 'stephan@spaceboyz.net'
|
20
20
|
url 'http://github.com/astro/em-redis'
|
21
|
-
summary 'An eventmachine-based implementation of the Redis protocol
|
21
|
+
summary 'An eventmachine-based implementation of the Redis protocol'
|
22
22
|
description summary
|
23
23
|
version EMRedis::VERSION
|
24
24
|
|
data/lib/em-redis.rb
CHANGED
@@ -34,12 +34,16 @@ module EventMachine
|
|
34
34
|
"zadd" => true,
|
35
35
|
"zincrby" => true,
|
36
36
|
"zrem" => true,
|
37
|
-
"zscore" => true
|
37
|
+
"zscore" => true,
|
38
|
+
"hget" => true,
|
39
|
+
"hdel" => true,
|
40
|
+
"hexists" => true
|
38
41
|
}
|
39
42
|
|
40
43
|
MULTI_BULK_COMMANDS = {
|
41
44
|
"mset" => true,
|
42
45
|
"msetnx" => true,
|
46
|
+
"hset" => true,
|
43
47
|
# these aliases aren't in redis gem
|
44
48
|
"multi_get" => true
|
45
49
|
}
|
@@ -60,7 +64,16 @@ module EventMachine
|
|
60
64
|
"renamenx" => BOOLEAN_PROCESSOR,
|
61
65
|
"expire" => BOOLEAN_PROCESSOR,
|
62
66
|
"select" => BOOLEAN_PROCESSOR, # not in redis gem
|
63
|
-
"
|
67
|
+
"hset" => BOOLEAN_PROCESSOR,
|
68
|
+
"hdel" => BOOLEAN_PROCESSOR,
|
69
|
+
"hexists" => BOOLEAN_PROCESSOR,
|
70
|
+
"keys" => lambda {|r|
|
71
|
+
if r.is_a?(Array)
|
72
|
+
r
|
73
|
+
else
|
74
|
+
r.split(" ")
|
75
|
+
end
|
76
|
+
},
|
64
77
|
"info" => lambda{|r|
|
65
78
|
info = {}
|
66
79
|
r.each_line {|kv|
|
@@ -68,6 +81,9 @@ module EventMachine
|
|
68
81
|
info[k.to_sym] = v
|
69
82
|
}
|
70
83
|
info
|
84
|
+
},
|
85
|
+
"hgetall" => lambda{|r|
|
86
|
+
Hash[*r]
|
71
87
|
}
|
72
88
|
}
|
73
89
|
|
@@ -218,64 +234,80 @@ module EventMachine
|
|
218
234
|
call_command(['quit'], &blk)
|
219
235
|
end
|
220
236
|
|
221
|
-
def
|
222
|
-
|
237
|
+
def exec(&blk)
|
238
|
+
call_command(['exec'], &blk)
|
223
239
|
end
|
224
|
-
alias_method :on_error, :errback
|
225
240
|
|
226
|
-
|
227
|
-
|
241
|
+
# I'm not sure autocommit is a good idea.
|
242
|
+
# For example:
|
243
|
+
# r.multi { r.set('a', 'b') { raise "kaboom" } }
|
244
|
+
# will commit "a" and will stop EM
|
245
|
+
def multi
|
246
|
+
call_command(['multi'])
|
247
|
+
if block_given?
|
248
|
+
begin
|
249
|
+
yield self
|
250
|
+
exec
|
251
|
+
rescue Exception => e
|
252
|
+
discard
|
253
|
+
raise e
|
254
|
+
end
|
255
|
+
end
|
228
256
|
end
|
229
257
|
|
230
|
-
def
|
231
|
-
|
258
|
+
def mset(*args, &blk)
|
259
|
+
hsh = args.pop if Hash === args.last
|
260
|
+
if hsh
|
261
|
+
call_command(hsh.to_a.flatten.unshift(:mset), &blk)
|
262
|
+
else
|
263
|
+
call_command(args.unshift(:mset), &blk)
|
264
|
+
end
|
232
265
|
end
|
233
266
|
|
234
|
-
def
|
235
|
-
|
236
|
-
|
237
|
-
|
267
|
+
def msetnx(*args, &blk)
|
268
|
+
hsh = args.pop if Hash === args.last
|
269
|
+
if hsh
|
270
|
+
call_command(hsh.to_a.flatten.unshift(:msetnx), &blk)
|
271
|
+
else
|
272
|
+
call_command(args.unshift(:msetnx), &blk)
|
273
|
+
end
|
238
274
|
end
|
239
275
|
|
240
|
-
def
|
241
|
-
|
276
|
+
def errback(&blk)
|
277
|
+
@error_callback = blk
|
242
278
|
end
|
279
|
+
alias_method :on_error, :errback
|
243
280
|
|
244
|
-
def
|
245
|
-
|
246
|
-
|
247
|
-
return
|
248
|
-
end
|
281
|
+
def method_missing(*argv, &blk)
|
282
|
+
call_command(argv, &blk)
|
283
|
+
end
|
249
284
|
|
250
|
-
|
251
|
-
|
252
|
-
|
285
|
+
def maybe_lock(&blk)
|
286
|
+
if !EM.reactor_thread?
|
287
|
+
EM.schedule { maybe_lock(&blk) }
|
288
|
+
elsif @connected
|
289
|
+
yield
|
290
|
+
else
|
291
|
+
callback { yield }
|
253
292
|
end
|
254
|
-
# FIXME: argvs may contain heterogenous commands, storing all
|
255
|
-
# REPLY_PROCESSORs may turn out expensive and has been omitted
|
256
|
-
# for now.
|
257
|
-
@redis_callbacks << [nil, argvs.length, blk]
|
258
293
|
end
|
259
294
|
|
260
|
-
def
|
295
|
+
def call_command(argv, &blk)
|
261
296
|
argv = argv.dup
|
262
297
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
command << "
|
272
|
-
command << v
|
298
|
+
argv[0] = argv[0].to_s.downcase
|
299
|
+
if MULTI_BULK_COMMANDS[argv[0]]
|
300
|
+
command = ""
|
301
|
+
command << "*#{argv.size}\r\n"
|
302
|
+
argv.each do |a|
|
303
|
+
a = a.to_s
|
304
|
+
command << "$#{get_size(a)}\r\n"
|
305
|
+
command << a
|
306
|
+
command << "\r\n"
|
273
307
|
end
|
274
|
-
command = command.map {|cmd| "#{cmd}\r\n"}.join
|
275
308
|
else
|
276
309
|
command = ""
|
277
310
|
bulk = nil
|
278
|
-
argv[0] = argv[0].to_s.downcase
|
279
311
|
argv[0] = ALIASES[argv[0]] if ALIASES[argv[0]]
|
280
312
|
raise "#{argv[0]} command is disabled" if DISABLED_COMMANDS[argv[0]]
|
281
313
|
if BULK_COMMANDS[argv[0]] and argv.length > 1
|
@@ -287,7 +319,27 @@ module EventMachine
|
|
287
319
|
end
|
288
320
|
|
289
321
|
@logger.debug { "*** sending: #{command}" } if @logger
|
290
|
-
|
322
|
+
maybe_lock do
|
323
|
+
@redis_callbacks << [REPLY_PROCESSOR[argv[0]], blk]
|
324
|
+
send_data command
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def call_commands(argvs, &blk)
|
329
|
+
pending = 0
|
330
|
+
results = []
|
331
|
+
check = lambda {
|
332
|
+
blk.call(results) if pending < 1
|
333
|
+
}
|
334
|
+
argvs.each do |argv|
|
335
|
+
call_command(argv) { |result|
|
336
|
+
results << result
|
337
|
+
pending -= 1
|
338
|
+
check.call
|
339
|
+
}
|
340
|
+
pending += 1
|
341
|
+
end
|
342
|
+
check.call
|
291
343
|
end
|
292
344
|
|
293
345
|
##
|
@@ -318,7 +370,7 @@ module EventMachine
|
|
318
370
|
else raise ArgumentError, 'first argument must be Hash or String'
|
319
371
|
end
|
320
372
|
when 2
|
321
|
-
options = {:host => args[
|
373
|
+
options = {:host => args[0], :port => args[1]}
|
322
374
|
else
|
323
375
|
raise ArgumentError, "wrong number of arguments (#{args.length} for 1)"
|
324
376
|
end
|
@@ -338,7 +390,6 @@ module EventMachine
|
|
338
390
|
err.code = code
|
339
391
|
raise err, "Redis server returned error code: #{code}"
|
340
392
|
end
|
341
|
-
@values = []
|
342
393
|
|
343
394
|
# These commands should be first
|
344
395
|
auth_and_select_db
|
@@ -410,10 +461,11 @@ module EventMachine
|
|
410
461
|
#e.g. *2\r\n$1\r\na\r\n$1\r\nb\r\n
|
411
462
|
when ASTERISK
|
412
463
|
multibulk_count = Integer(reply_args)
|
413
|
-
if multibulk_count == -1
|
464
|
+
if multibulk_count == -1 || multibulk_count == 0
|
414
465
|
dispatch_response([])
|
415
466
|
else
|
416
|
-
|
467
|
+
@multibulk_n = multibulk_count
|
468
|
+
@multibulk_values = []
|
417
469
|
end
|
418
470
|
# Whu?
|
419
471
|
else
|
@@ -53,14 +53,14 @@ EM.describe EM::Protocols::Redis, "connected to an empty db" do
|
|
53
53
|
|
54
54
|
should "be able to 'lpush' to a nonexistent list" do
|
55
55
|
@c.lpush("foo", "bar") do |r|
|
56
|
-
r.should ==
|
56
|
+
r.should == 1
|
57
57
|
done
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
61
|
should "be able to 'rpush' to a nonexistent list" do
|
62
62
|
@c.rpush("foo", "bar") do |r|
|
63
|
-
r.should ==
|
63
|
+
r.should == 1
|
64
64
|
done
|
65
65
|
end
|
66
66
|
end
|
@@ -222,7 +222,7 @@ EM.describe EM::Protocols::Redis, "connected to a db containing a list" do
|
|
222
222
|
|
223
223
|
should "be able to 'rpush' onto the tail of the list" do
|
224
224
|
@c.rpush "foo", "d" do |r|
|
225
|
-
r.should ==
|
225
|
+
r.should == 4
|
226
226
|
@c.rpop "foo" do |r|
|
227
227
|
r.should == "d"
|
228
228
|
done
|
@@ -232,7 +232,7 @@ EM.describe EM::Protocols::Redis, "connected to a db containing a list" do
|
|
232
232
|
|
233
233
|
should "be able to 'lpush' onto the head of the list" do
|
234
234
|
@c.lpush "foo", "d" do |r|
|
235
|
-
r.should ==
|
235
|
+
r.should == 4
|
236
236
|
@c.lpop "foo" do |r|
|
237
237
|
r.should == "d"
|
238
238
|
done
|
data/spec/redis_commands_spec.rb
CHANGED
@@ -10,6 +10,7 @@ EM.describe EM::Protocols::Redis do
|
|
10
10
|
@r['foo'] = 'bar'
|
11
11
|
end
|
12
12
|
|
13
|
+
after { @r.close_connection }
|
13
14
|
|
14
15
|
should "be able to provide a logger" do
|
15
16
|
log = StringIO.new
|
@@ -644,6 +645,90 @@ EM.describe EM::Protocols::Redis do
|
|
644
645
|
# done
|
645
646
|
# end
|
646
647
|
|
648
|
+
it "should run MULTI without a block" do
|
649
|
+
@r.multi
|
650
|
+
@r.get("key1") { |r| r.should == "QUEUED" }
|
651
|
+
@r.discard { done }
|
652
|
+
end
|
653
|
+
|
654
|
+
it "should run MULTI/EXEC with a block" do
|
655
|
+
@r.multi do
|
656
|
+
@r.set "key1", "value1"
|
657
|
+
end
|
658
|
+
|
659
|
+
@r.get("key1") { |r| r.should == "value1" }
|
660
|
+
|
661
|
+
begin
|
662
|
+
@r.multi do
|
663
|
+
@r.set "key2", "value2"
|
664
|
+
raise "Some error"
|
665
|
+
@r.set "key3", "value3"
|
666
|
+
end
|
667
|
+
rescue
|
668
|
+
end
|
669
|
+
|
670
|
+
@r.get("key2") { |r| r.should == nil }
|
671
|
+
@r.get("key3") { |r| r.should == nil; done}
|
672
|
+
end
|
673
|
+
|
674
|
+
it "should yield the Redis object when using #multi with a block" do
|
675
|
+
@r.multi do |multi|
|
676
|
+
multi.set "key1", "value1"
|
677
|
+
end
|
678
|
+
|
679
|
+
@r.get("key1") { |r| r.should == "value1"; done }
|
680
|
+
end
|
681
|
+
|
682
|
+
it "can set and get hash values" do
|
683
|
+
@r.hset("rush", "signals", "1982") { |r| r.should == true }
|
684
|
+
@r.hexists("rush", "signals") { |r| r.should == true }
|
685
|
+
@r.hget("rush", "signals") { |r| r.should == "1982"; done }
|
686
|
+
end
|
687
|
+
|
688
|
+
it "can delete hash values" do
|
689
|
+
@r.hset("rush", "YYZ", "1981")
|
690
|
+
@r.hdel("rush", "YYZ") { |r| r.should == true }
|
691
|
+
@r.hexists("rush", "YYZ") { |r| r.should == false; done }
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
# Yup, bacon can't handle nested describe blocks properly
|
696
|
+
EM.describe EM::Protocols::Redis, "with some hash values" do
|
697
|
+
default_timeout 1
|
698
|
+
|
699
|
+
before do
|
700
|
+
@r = EM::Protocols::Redis.connect :db => 14
|
701
|
+
@r.flushdb
|
702
|
+
@r['foo'] = 'bar'
|
703
|
+
@r.hset("rush", "permanent waves", "1980")
|
704
|
+
@r.hset("rush", "moving pictures", "1981")
|
705
|
+
@r.hset("rush", "signals", "1982")
|
706
|
+
end
|
707
|
+
|
708
|
+
after { @r.close_connection }
|
709
|
+
|
710
|
+
it "can get the length of the hash" do
|
711
|
+
@r.hlen("rush") { |r| r.should == 3 }
|
712
|
+
@r.hlen("yyz") { |r| r.should == 0; done }
|
713
|
+
end
|
714
|
+
|
715
|
+
it "can get the keys and values of the hash" do
|
716
|
+
@r.hkeys("rush") { |r| r.should == ["permanent waves", "moving pictures", "signals"] }
|
717
|
+
@r.hvals("rush") { |r| r.should == %w[1980 1981 1982] }
|
718
|
+
@r.hvals("yyz") { |r| r.should == []; done }
|
719
|
+
end
|
720
|
+
|
721
|
+
it "returns a hash for HGETALL" do
|
722
|
+
@r.hgetall("rush") do |r|
|
723
|
+
r.should == {
|
724
|
+
"permanent waves" => "1980",
|
725
|
+
"moving pictures" => "1981",
|
726
|
+
"signals" => "1982"
|
727
|
+
}
|
728
|
+
end
|
729
|
+
@r.hgetall("yyz") { |r| r.should == {}; done }
|
730
|
+
end
|
731
|
+
|
647
732
|
it "should work with 10 commands" do
|
648
733
|
@r.call_commands((1..10).map { |i|
|
649
734
|
['get', "foo"]
|
@@ -0,0 +1,731 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/test_helper.rb")
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
EM.describe EM::Protocols::Redis do
|
5
|
+
default_timeout 1
|
6
|
+
|
7
|
+
before do
|
8
|
+
@r = EM::Protocols::Redis.connect :db => 14
|
9
|
+
@r.flushdb
|
10
|
+
@r['foo'] = 'bar'
|
11
|
+
end
|
12
|
+
|
13
|
+
after { @r.close_connection }
|
14
|
+
|
15
|
+
should "be able to provide a logger" do
|
16
|
+
log = StringIO.new
|
17
|
+
r = EM::Protocols::Redis.connect :db => 14, :logger => Logger.new(log)
|
18
|
+
r.ping do
|
19
|
+
log.string.should.include "ping"
|
20
|
+
done
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be able to PING" do
|
25
|
+
@r.ping { |r| r.should == 'PONG'; done }
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be able to GET a key" do
|
29
|
+
@r.get('foo') { |r| r.should == 'bar'; done }
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should be able to SET a key" do
|
33
|
+
@r['foo'] = 'nik'
|
34
|
+
@r.get('foo') { |r| r.should == 'nik'; done }
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should properly handle trailing newline characters" do
|
38
|
+
@r['foo'] = "bar\n"
|
39
|
+
@r.get('foo') { |r| r.should == "bar\n"; done }
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should store and retrieve all possible characters at the beginning and the end of a string" do
|
43
|
+
(0..255).each do |char_idx|
|
44
|
+
string = "#{char_idx.chr}---#{char_idx.chr}"
|
45
|
+
@r['foo'] = string
|
46
|
+
@r.get('foo') { |r| r.should == string }
|
47
|
+
end
|
48
|
+
@r.ping { done }
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should be able to SET a key with an expiry" do
|
52
|
+
timeout(3)
|
53
|
+
|
54
|
+
@r.set('foo', 'bar', 1)
|
55
|
+
@r.get('foo') { |r| r.should == 'bar' }
|
56
|
+
EM.add_timer(2) do
|
57
|
+
@r.get('foo') { |r| r.should == nil }
|
58
|
+
@r.ping { done }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should be able to return a TTL for a key" do
|
63
|
+
@r.set('foo', 'bar', 1)
|
64
|
+
@r.ttl('foo') { |r| r.should == 1; done }
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should be able to SETNX" do
|
68
|
+
@r['foo'] = 'nik'
|
69
|
+
@r.get('foo') { |r| r.should == 'nik' }
|
70
|
+
@r.setnx 'foo', 'bar'
|
71
|
+
@r.get('foo') { |r| r.should == 'nik' }
|
72
|
+
|
73
|
+
@r.ping { done }
|
74
|
+
end
|
75
|
+
#
|
76
|
+
it "should be able to GETSET" do
|
77
|
+
@r.getset('foo', 'baz') { |r| r.should == 'bar' }
|
78
|
+
@r.get('foo') { |r| r.should == 'baz'; done }
|
79
|
+
end
|
80
|
+
#
|
81
|
+
it "should be able to INCR a key" do
|
82
|
+
@r.del('counter')
|
83
|
+
@r.incr('counter') { |r| r.should == 1 }
|
84
|
+
@r.incr('counter') { |r| r.should == 2 }
|
85
|
+
@r.incr('counter') { |r| r.should == 3 }
|
86
|
+
|
87
|
+
@r.ping { done }
|
88
|
+
end
|
89
|
+
#
|
90
|
+
it "should be able to INCRBY a key" do
|
91
|
+
@r.del('counter')
|
92
|
+
@r.incrby('counter', 1) { |r| r.should == 1 }
|
93
|
+
@r.incrby('counter', 2) { |r| r.should == 3 }
|
94
|
+
@r.incrby('counter', 3) { |r| r.should == 6 }
|
95
|
+
|
96
|
+
@r.ping { done }
|
97
|
+
end
|
98
|
+
#
|
99
|
+
it "should be able to DECR a key" do
|
100
|
+
@r.del('counter')
|
101
|
+
@r.incr('counter') { |r| r.should == 1 }
|
102
|
+
@r.incr('counter') { |r| r.should == 2 }
|
103
|
+
@r.incr('counter') { |r| r.should == 3 }
|
104
|
+
@r.decr('counter') { |r| r.should == 2 }
|
105
|
+
@r.decr('counter', 2) { |r| r.should == 0; done }
|
106
|
+
end
|
107
|
+
#
|
108
|
+
it "should be able to RANDKEY" do
|
109
|
+
@r.randkey { |r| r.should.not == nil; done }
|
110
|
+
end
|
111
|
+
#
|
112
|
+
it "should be able to RENAME a key" do
|
113
|
+
@r.del 'foo'
|
114
|
+
@r.del 'bar'
|
115
|
+
@r['foo'] = 'hi'
|
116
|
+
@r.rename 'foo', 'bar'
|
117
|
+
@r.get('bar') { |r| r.should == 'hi' ; done }
|
118
|
+
end
|
119
|
+
#
|
120
|
+
it "should be able to RENAMENX a key" do
|
121
|
+
@r.del 'foo'
|
122
|
+
@r.del 'bar'
|
123
|
+
@r['foo'] = 'hi'
|
124
|
+
@r['bar'] = 'ohai'
|
125
|
+
@r.renamenx 'foo', 'bar'
|
126
|
+
@r.get('bar') { |r| r.should == 'ohai' ; done }
|
127
|
+
end
|
128
|
+
#
|
129
|
+
it "should be able to get DBSIZE of the database" do
|
130
|
+
dbsize_without_foo, dbsize_with_foo = nil
|
131
|
+
@r.delete 'foo'
|
132
|
+
@r.dbsize { |r| dbsize_without_foo = r }
|
133
|
+
@r['foo'] = 0
|
134
|
+
@r.dbsize { |r| dbsize_with_foo = r }
|
135
|
+
|
136
|
+
@r.ping do
|
137
|
+
dbsize_with_foo.should == dbsize_without_foo + 1
|
138
|
+
done
|
139
|
+
end
|
140
|
+
end
|
141
|
+
#
|
142
|
+
it "should be able to EXPIRE a key" do
|
143
|
+
timeout(3)
|
144
|
+
|
145
|
+
@r['foo'] = 'bar'
|
146
|
+
@r.expire 'foo', 1
|
147
|
+
@r.get('foo') { |r| r.should == "bar" }
|
148
|
+
EM.add_timer(2) do
|
149
|
+
@r.get('foo') { |r| r.should == nil }
|
150
|
+
@r.ping { done }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
#
|
154
|
+
it "should be able to EXISTS" do
|
155
|
+
@r['foo'] = 'nik'
|
156
|
+
@r.exists('foo') { |r| r.should == true }
|
157
|
+
@r.del 'foo'
|
158
|
+
@r.exists('foo') { |r| r.should == false ; done }
|
159
|
+
end
|
160
|
+
#
|
161
|
+
it "should be able to KEYS" do
|
162
|
+
@r.keys("f*") { |keys| keys.each { |key| @r.del key } }
|
163
|
+
@r['f'] = 'nik'
|
164
|
+
@r['fo'] = 'nak'
|
165
|
+
@r['foo'] = 'qux'
|
166
|
+
@r.keys("f*") { |r| r.sort.should == ['f', 'fo', 'foo'].sort }
|
167
|
+
|
168
|
+
@r.ping { done }
|
169
|
+
end
|
170
|
+
#
|
171
|
+
it "should be able to return a random key (RANDOMKEY)" do
|
172
|
+
3.times do |i|
|
173
|
+
@r.randomkey do |r|
|
174
|
+
@r.exists(r) do |e|
|
175
|
+
e.should == true
|
176
|
+
done if i == 2
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
#
|
182
|
+
it "should be able to check the TYPE of a key" do
|
183
|
+
@r['foo'] = 'nik'
|
184
|
+
@r.type('foo') { |r| r.should == "string" }
|
185
|
+
@r.del 'foo'
|
186
|
+
@r.type('foo') { |r| r.should == "none" ; done }
|
187
|
+
end
|
188
|
+
#
|
189
|
+
it "should be able to push to the head of a list (LPUSH)" do
|
190
|
+
@r.lpush "list", 'hello'
|
191
|
+
@r.lpush "list", 42
|
192
|
+
@r.type('list') { |r| r.should == "list" }
|
193
|
+
@r.llen('list') { |r| r.should == 2 }
|
194
|
+
@r.lpop('list') { |r| r.should == '42'; done }
|
195
|
+
end
|
196
|
+
#
|
197
|
+
it "should be able to push to the tail of a list (RPUSH)" do
|
198
|
+
@r.rpush "list", 'hello'
|
199
|
+
@r.type('list') { |r| r.should == "list" }
|
200
|
+
@r.llen('list') { |r| r.should == 1 ; done }
|
201
|
+
end
|
202
|
+
#
|
203
|
+
it "should be able to pop the tail of a list (RPOP)" do
|
204
|
+
@r.rpush "list", 'hello'
|
205
|
+
@r.rpush"list", 'goodbye'
|
206
|
+
@r.type('list') { |r| r.should == "list" }
|
207
|
+
@r.llen('list') { |r| r.should == 2 }
|
208
|
+
@r.rpop('list') { |r| r.should == 'goodbye'; done }
|
209
|
+
end
|
210
|
+
#
|
211
|
+
it "should be able to pop the head of a list (LPOP)" do
|
212
|
+
@r.rpush "list", 'hello'
|
213
|
+
@r.rpush "list", 'goodbye'
|
214
|
+
@r.type('list') { |r| r.should == "list" }
|
215
|
+
@r.llen('list') { |r| r.should == 2 }
|
216
|
+
@r.lpop('list') { |r| r.should == 'hello'; done }
|
217
|
+
end
|
218
|
+
#
|
219
|
+
it "should be able to get the length of a list (LLEN)" do
|
220
|
+
@r.rpush "list", 'hello'
|
221
|
+
@r.rpush "list", 'goodbye'
|
222
|
+
@r.type('list') { |r| r.should == "list" }
|
223
|
+
@r.llen('list') { |r| r.should == 2 ; done }
|
224
|
+
end
|
225
|
+
#
|
226
|
+
it "should be able to get a range of values from a list (LRANGE)" do
|
227
|
+
@r.rpush "list", 'hello'
|
228
|
+
@r.rpush "list", 'goodbye'
|
229
|
+
@r.rpush "list", '1'
|
230
|
+
@r.rpush "list", '2'
|
231
|
+
@r.rpush "list", '3'
|
232
|
+
@r.type('list') { |r| r.should == "list" }
|
233
|
+
@r.llen('list') { |r| r.should == 5 }
|
234
|
+
@r.lrange('list', 2, -1) { |r| r.should == ['1', '2', '3']; done }
|
235
|
+
end
|
236
|
+
#
|
237
|
+
it "should be able to trim a list (LTRIM)" do
|
238
|
+
@r.rpush "list", 'hello'
|
239
|
+
@r.rpush "list", 'goodbye'
|
240
|
+
@r.rpush "list", '1'
|
241
|
+
@r.rpush "list", '2'
|
242
|
+
@r.rpush "list", '3'
|
243
|
+
@r.type('list') { |r| r.should == "list" }
|
244
|
+
@r.llen('list') { |r| r.should == 5 }
|
245
|
+
@r.ltrim 'list', 0, 1
|
246
|
+
@r.llen('list') { |r| r.should == 2 }
|
247
|
+
@r.lrange('list', 0, -1) { |r| r.should == ['hello', 'goodbye']; done }
|
248
|
+
end
|
249
|
+
#
|
250
|
+
it "should be able to get a value by indexing into a list (LINDEX)" do
|
251
|
+
@r.rpush "list", 'hello'
|
252
|
+
@r.rpush "list", 'goodbye'
|
253
|
+
@r.type('list') { |r| r.should == "list" }
|
254
|
+
@r.llen('list') { |r| r.should == 2 }
|
255
|
+
@r.lindex('list', 1) { |r| r.should == 'goodbye'; done }
|
256
|
+
end
|
257
|
+
#
|
258
|
+
it "should be able to set a value by indexing into a list (LSET)" do
|
259
|
+
@r.rpush "list", 'hello'
|
260
|
+
@r.rpush "list", 'hello'
|
261
|
+
@r.type('list') { |r| r.should == "list" }
|
262
|
+
@r.llen('list') { |r| r.should == 2 }
|
263
|
+
@r.lset('list', 1, 'goodbye') { |r| r.should == 'OK' }
|
264
|
+
@r.lindex('list', 1) { |r| r.should == 'goodbye'; done }
|
265
|
+
end
|
266
|
+
#
|
267
|
+
it "should be able to remove values from a list (LREM)" do
|
268
|
+
@r.rpush "list", 'hello'
|
269
|
+
@r.rpush "list", 'goodbye'
|
270
|
+
@r.type('list') { |r| r.should == "list" }
|
271
|
+
@r.llen('list') { |r| r.should == 2 }
|
272
|
+
@r.lrem('list', 1, 'hello') { |r| r.should == 1 }
|
273
|
+
@r.lrange('list', 0, -1) { |r| r.should == ['goodbye']; done }
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should be able to pop values from a list and push them onto a temp list(RPOPLPUSH)" do
|
277
|
+
@r.rpush "list", 'one'
|
278
|
+
@r.rpush "list", 'two'
|
279
|
+
@r.rpush "list", 'three'
|
280
|
+
@r.type('list') { |r| r.should == "list" }
|
281
|
+
@r.llen('list') { |r| r.should == 3 }
|
282
|
+
@r.lrange('list', 0, -1) { |r| r.should == ['one', 'two', 'three'] }
|
283
|
+
@r.lrange('tmp', 0, -1) { |r| r.should == [] }
|
284
|
+
@r.rpoplpush('list', 'tmp') { |r| r.should == 'three' }
|
285
|
+
@r.lrange('tmp', 0, -1) { |r| r.should == ['three'] }
|
286
|
+
@r.rpoplpush('list', 'tmp') { |r| r.should == 'two' }
|
287
|
+
@r.lrange('tmp', 0, -1) { |r| r.should == ['two', 'three'] }
|
288
|
+
@r.rpoplpush('list', 'tmp') { |r| r.should == 'one' }
|
289
|
+
@r.lrange('tmp', 0, -1) { |r| r.should == ['one', 'two', 'three']; done }
|
290
|
+
end
|
291
|
+
#
|
292
|
+
it "should be able add members to a set (SADD)" do
|
293
|
+
@r.sadd "set", 'key1'
|
294
|
+
@r.sadd "set", 'key2'
|
295
|
+
@r.type('set') { |r| r.should == "set" }
|
296
|
+
@r.scard('set') { |r| r.should == 2 }
|
297
|
+
@r.smembers('set') { |r| r.sort.should == ['key1', 'key2'].sort; done }
|
298
|
+
end
|
299
|
+
#
|
300
|
+
it "should be able delete members to a set (SREM)" do
|
301
|
+
@r.sadd "set", 'key1'
|
302
|
+
@r.sadd "set", 'key2'
|
303
|
+
@r.type('set') { |r| r.should == "set" }
|
304
|
+
@r.scard('set') { |r| r.should == 2 }
|
305
|
+
@r.smembers('set') { |r| r.sort.should == ['key1', 'key2'].sort }
|
306
|
+
@r.srem('set', 'key1')
|
307
|
+
@r.scard('set') { |r| r.should == 1 }
|
308
|
+
@r.smembers('set') { |r| r.should == ['key2']; done }
|
309
|
+
end
|
310
|
+
#
|
311
|
+
it "should be able to return and remove random key from set (SPOP)" do
|
312
|
+
@r.sadd "set_pop", "key1"
|
313
|
+
@r.sadd "set_pop", "key2"
|
314
|
+
@r.spop("set_pop") { |r| r.should.not == nil }
|
315
|
+
@r.scard("set_pop") { |r| r.should == 1; done }
|
316
|
+
end
|
317
|
+
#
|
318
|
+
it "should be able to return random key without delete the key from a set (SRANDMEMBER)" do
|
319
|
+
@r.sadd "set_srandmember", "key1"
|
320
|
+
@r.sadd "set_srandmember", "key2"
|
321
|
+
@r.srandmember("set_srandmember") { |r| r.should.not == nil }
|
322
|
+
@r.scard("set_srandmember") { |r| r.should == 2; done }
|
323
|
+
end
|
324
|
+
#
|
325
|
+
it "should be able count the members of a set (SCARD)" do
|
326
|
+
@r.sadd "set", 'key1'
|
327
|
+
@r.sadd "set", 'key2'
|
328
|
+
@r.type('set') { |r| r.should == "set" }
|
329
|
+
@r.scard('set') { |r| r.should == 2; done }
|
330
|
+
end
|
331
|
+
#
|
332
|
+
it "should be able test for set membership (SISMEMBER)" do
|
333
|
+
@r.sadd "set", 'key1'
|
334
|
+
@r.sadd "set", 'key2'
|
335
|
+
@r.type('set') { |r| r.should == "set" }
|
336
|
+
@r.scard('set') { |r| r.should == 2 }
|
337
|
+
@r.sismember('set', 'key1') { |r| r.should == true }
|
338
|
+
@r.sismember('set', 'key2') { |r| r.should == true }
|
339
|
+
@r.sismember('set', 'notthere') { |r| r.should == false; done }
|
340
|
+
end
|
341
|
+
#
|
342
|
+
it "should be able to do set intersection (SINTER)" do
|
343
|
+
@r.sadd "set", 'key1'
|
344
|
+
@r.sadd "set", 'key2'
|
345
|
+
@r.sadd "set2", 'key2'
|
346
|
+
@r.sinter('set', 'set2') { |r| r.should == ['key2']; done }
|
347
|
+
end
|
348
|
+
#
|
349
|
+
it "should be able to do set intersection and store the results in a key (SINTERSTORE)" do
|
350
|
+
@r.sadd "set", 'key1'
|
351
|
+
@r.sadd "set", 'key2'
|
352
|
+
@r.sadd "set2", 'key2'
|
353
|
+
@r.sinterstore('newone', 'set', 'set2') { |r| r.should == 1 }
|
354
|
+
@r.smembers('newone') { |r| r.should == ['key2']; done }
|
355
|
+
end
|
356
|
+
#
|
357
|
+
it "should be able to do set union (SUNION)" do
|
358
|
+
@r.sadd "set", 'key1'
|
359
|
+
@r.sadd "set", 'key2'
|
360
|
+
@r.sadd "set2", 'key2'
|
361
|
+
@r.sadd "set2", 'key3'
|
362
|
+
@r.sunion('set', 'set2') { |r| r.sort.should == ['key1','key2','key3'].sort; done }
|
363
|
+
end
|
364
|
+
#
|
365
|
+
it "should be able to do set union and store the results in a key (SUNIONSTORE)" do
|
366
|
+
@r.sadd "set", 'key1'
|
367
|
+
@r.sadd "set", 'key2'
|
368
|
+
@r.sadd "set2", 'key2'
|
369
|
+
@r.sadd "set2", 'key3'
|
370
|
+
@r.sunionstore('newone', 'set', 'set2') { |r| r.should == 3 }
|
371
|
+
@r.smembers('newone') { |r| r.sort.should == ['key1','key2','key3'].sort; done }
|
372
|
+
end
|
373
|
+
#
|
374
|
+
it "should be able to do set difference (SDIFF)" do
|
375
|
+
@r.sadd "set", 'a'
|
376
|
+
@r.sadd "set", 'b'
|
377
|
+
@r.sadd "set2", 'b'
|
378
|
+
@r.sadd "set2", 'c'
|
379
|
+
@r.sdiff('set', 'set2') { |r| r.should == ['a']; done }
|
380
|
+
end
|
381
|
+
#
|
382
|
+
it "should be able to do set difference and store the results in a key (SDIFFSTORE)" do
|
383
|
+
@r.sadd "set", 'a'
|
384
|
+
@r.sadd "set", 'b'
|
385
|
+
@r.sadd "set2", 'b'
|
386
|
+
@r.sadd "set2", 'c'
|
387
|
+
@r.sdiffstore('newone', 'set', 'set2')
|
388
|
+
@r.smembers('newone') { |r| r.should == ['a']; done }
|
389
|
+
end
|
390
|
+
#
|
391
|
+
it "should be able move elements from one set to another (SMOVE)" do
|
392
|
+
@r.sadd 'set1', 'a'
|
393
|
+
@r.sadd 'set1', 'b'
|
394
|
+
@r.sadd 'set2', 'x'
|
395
|
+
@r.smove('set1', 'set2', 'a') { |r| r.should == true }
|
396
|
+
@r.sismember('set2', 'a') { |r| r.should == true }
|
397
|
+
@r.delete('set1') { done }
|
398
|
+
end
|
399
|
+
#
|
400
|
+
it "should be able to do crazy SORT queries" do
|
401
|
+
# The 'Dogs' is capitialized on purpose
|
402
|
+
@r['dog_1'] = 'louie'
|
403
|
+
@r.rpush 'Dogs', 1
|
404
|
+
@r['dog_2'] = 'lucy'
|
405
|
+
@r.rpush 'Dogs', 2
|
406
|
+
@r['dog_3'] = 'max'
|
407
|
+
@r.rpush 'Dogs', 3
|
408
|
+
@r['dog_4'] = 'taj'
|
409
|
+
@r.rpush 'Dogs', 4
|
410
|
+
@r.sort('Dogs', :get => 'dog_*', :limit => [0,1]) { |r| r.should == ['louie'] }
|
411
|
+
@r.sort('Dogs', :get => 'dog_*', :limit => [0,1], :order => 'desc alpha') { |r| r.should == ['taj'] }
|
412
|
+
@r.ping { done }
|
413
|
+
end
|
414
|
+
|
415
|
+
it "should be able to handle array of :get using SORT" do
|
416
|
+
@r['dog:1:name'] = 'louie'
|
417
|
+
@r['dog:1:breed'] = 'mutt'
|
418
|
+
@r.rpush 'dogs', 1
|
419
|
+
@r['dog:2:name'] = 'lucy'
|
420
|
+
@r['dog:2:breed'] = 'poodle'
|
421
|
+
@r.rpush 'dogs', 2
|
422
|
+
@r['dog:3:name'] = 'max'
|
423
|
+
@r['dog:3:breed'] = 'hound'
|
424
|
+
@r.rpush 'dogs', 3
|
425
|
+
@r['dog:4:name'] = 'taj'
|
426
|
+
@r['dog:4:breed'] = 'terrier'
|
427
|
+
@r.rpush 'dogs', 4
|
428
|
+
@r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1]) { |r| r.should == ['louie', 'mutt'] }
|
429
|
+
@r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1], :order => 'desc alpha') { |r| r.should == ['taj', 'terrier'] }
|
430
|
+
@r.ping { done }
|
431
|
+
end
|
432
|
+
#
|
433
|
+
it "should be able count the members of a zset" do
|
434
|
+
@r.set_add "set", 'key1'
|
435
|
+
@r.set_add "set", 'key2'
|
436
|
+
@r.zset_add 'zset', 1, 'set'
|
437
|
+
@r.zset_count('zset') { |r| r.should == 1 }
|
438
|
+
@r.delete('set')
|
439
|
+
@r.delete('zset') { done }
|
440
|
+
end
|
441
|
+
#
|
442
|
+
it "should be able add members to a zset" do
|
443
|
+
@r.set_add "set", 'key1'
|
444
|
+
@r.set_add "set", 'key2'
|
445
|
+
@r.zset_add 'zset', 1, 'set'
|
446
|
+
@r.zset_range('zset', 0, 1) { |r| r.should == ['set'] }
|
447
|
+
@r.zset_count('zset') { |r| r.should == 1 }
|
448
|
+
@r.delete('set')
|
449
|
+
@r.delete('zset') { done }
|
450
|
+
end
|
451
|
+
#
|
452
|
+
it "should be able delete members to a zset" do
|
453
|
+
@r.set_add "set", 'key1'
|
454
|
+
@r.set_add "set", 'key2'
|
455
|
+
@r.type?('set') { |r| r.should == "set" }
|
456
|
+
@r.set_add "set2", 'key3'
|
457
|
+
@r.set_add "set2", 'key4'
|
458
|
+
@r.type?('set2') { |r| r.should == "set" }
|
459
|
+
@r.zset_add 'zset', 1, 'set'
|
460
|
+
@r.zset_count('zset') { |r| r.should == 1 }
|
461
|
+
@r.zset_add 'zset', 2, 'set2'
|
462
|
+
@r.zset_count('zset') { |r| r.should == 2 }
|
463
|
+
@r.zset_delete 'zset', 'set'
|
464
|
+
@r.zset_count('zset') { |r| r.should == 1 }
|
465
|
+
@r.delete('set')
|
466
|
+
@r.delete('set2')
|
467
|
+
@r.delete('zset') { done }
|
468
|
+
end
|
469
|
+
#
|
470
|
+
it "should be able to get a range of values from a zset" do
|
471
|
+
@r.set_add "set", 'key1'
|
472
|
+
@r.set_add "set", 'key2'
|
473
|
+
@r.set_add "set2", 'key3'
|
474
|
+
@r.set_add "set2", 'key4'
|
475
|
+
@r.set_add "set3", 'key1'
|
476
|
+
@r.type?('set') { |r| r.should == 'set' }
|
477
|
+
@r.type?('set2') { |r| r.should == 'set' }
|
478
|
+
@r.type?('set3') { |r| r.should == 'set' }
|
479
|
+
@r.zset_add 'zset', 1, 'set'
|
480
|
+
@r.zset_add 'zset', 2, 'set2'
|
481
|
+
@r.zset_add 'zset', 3, 'set3'
|
482
|
+
@r.zset_count('zset') { |r| r.should == 3 }
|
483
|
+
@r.zset_range('zset', 0, 3) { |r| r.should == ['set', 'set2', 'set3'] }
|
484
|
+
@r.delete('set')
|
485
|
+
@r.delete('set2')
|
486
|
+
@r.delete('set3')
|
487
|
+
@r.delete('zset') { done }
|
488
|
+
end
|
489
|
+
#
|
490
|
+
it "should be able to get a reverse range of values from a zset" do
|
491
|
+
@r.set_add "set", 'key1'
|
492
|
+
@r.set_add "set", 'key2'
|
493
|
+
@r.set_add "set2", 'key3'
|
494
|
+
@r.set_add "set2", 'key4'
|
495
|
+
@r.set_add "set3", 'key1'
|
496
|
+
@r.type?('set') { |r| r.should == 'set' }
|
497
|
+
@r.type?('set2') { |r| r.should == 'set' }
|
498
|
+
@r.type?('set3') { |r| r.should == 'set' }
|
499
|
+
@r.zset_add 'zset', 1, 'set'
|
500
|
+
@r.zset_add 'zset', 2, 'set2'
|
501
|
+
@r.zset_add 'zset', 3, 'set3'
|
502
|
+
@r.zset_count('zset') { |r| r.should == 3 }
|
503
|
+
@r.zset_reverse_range('zset', 0, 3) { |r| r.should == ['set3', 'set2', 'set'] }
|
504
|
+
@r.delete('set')
|
505
|
+
@r.delete('set2')
|
506
|
+
@r.delete('set3')
|
507
|
+
@r.delete('zset') { done }
|
508
|
+
end
|
509
|
+
#
|
510
|
+
it "should be able to get a range by score of values from a zset" do
|
511
|
+
@r.set_add "set", 'key1'
|
512
|
+
@r.set_add "set", 'key2'
|
513
|
+
@r.set_add "set2", 'key3'
|
514
|
+
@r.set_add "set2", 'key4'
|
515
|
+
@r.set_add "set3", 'key1'
|
516
|
+
@r.set_add "set4", 'key4'
|
517
|
+
@r.zset_add 'zset', 1, 'set'
|
518
|
+
@r.zset_add 'zset', 2, 'set2'
|
519
|
+
@r.zset_add 'zset', 3, 'set3'
|
520
|
+
@r.zset_add 'zset', 4, 'set4'
|
521
|
+
@r.zset_count('zset') { |r| r.should == 4 }
|
522
|
+
@r.zset_range_by_score('zset', 2, 3) { |r| r.should == ['set2', 'set3'] }
|
523
|
+
@r.delete('set')
|
524
|
+
@r.delete('set2')
|
525
|
+
@r.delete('set3')
|
526
|
+
@r.delete('set4')
|
527
|
+
@r.delete('zset') { done }
|
528
|
+
end
|
529
|
+
#
|
530
|
+
it "should be able to get a score for a specific value in a zset (ZSCORE)" do
|
531
|
+
@r.zset_add "zset", 23, "value"
|
532
|
+
@r.zset_score("zset", "value") { |r| r.should == "23" }
|
533
|
+
|
534
|
+
@r.zset_score("zset", "value2") { |r| r.should == nil }
|
535
|
+
@r.zset_score("unknown_zset", "value") { |r| r.should == nil }
|
536
|
+
|
537
|
+
@r.delete("zset") { done }
|
538
|
+
end
|
539
|
+
#
|
540
|
+
it "should be able to increment a range score of a zset (ZINCRBY)" do
|
541
|
+
# create a new zset
|
542
|
+
@r.zset_increment_by "hackers", 1965, "Yukihiro Matsumoto"
|
543
|
+
@r.zset_score("hackers", "Yukihiro Matsumoto") { |r| r.should == "1965" }
|
544
|
+
|
545
|
+
# add a new element
|
546
|
+
@r.zset_increment_by "hackers", 1912, "Alan Turing"
|
547
|
+
@r.zset_score("hackers", "Alan Turing") { |r| r.should == "1912" }
|
548
|
+
|
549
|
+
# update the score
|
550
|
+
@r.zset_increment_by "hackers", 100, "Alan Turing" # yeah, we are making Turing a bit younger
|
551
|
+
@r.zset_score("hackers", "Alan Turing") { |r| r.should == "2012" }
|
552
|
+
|
553
|
+
# attempt to update a key that's not a zset
|
554
|
+
@r["i_am_not_a_zet"] = "value"
|
555
|
+
# shouldn't raise error anymore
|
556
|
+
@r.zset_incr_by("i_am_not_a_zet", 23, "element") { |r| r.should == nil }
|
557
|
+
|
558
|
+
@r.delete("hackers")
|
559
|
+
@r.delete("i_am_not_a_zet") { done }
|
560
|
+
end
|
561
|
+
#
|
562
|
+
it "should provide info (INFO)" do
|
563
|
+
@r.info do |r|
|
564
|
+
[:last_save_time, :redis_version, :total_connections_received, :connected_clients, :total_commands_processed, :connected_slaves, :uptime_in_seconds, :used_memory, :uptime_in_days, :changes_since_last_save].each do |x|
|
565
|
+
r.keys.include?(x).should == true
|
566
|
+
end
|
567
|
+
done
|
568
|
+
end
|
569
|
+
end
|
570
|
+
#
|
571
|
+
it "should be able to flush the database (FLUSHDB)" do
|
572
|
+
@r['key1'] = 'keyone'
|
573
|
+
@r['key2'] = 'keytwo'
|
574
|
+
@r.keys('*') { |r| r.sort.should == ['foo', 'key1', 'key2'].sort } #foo from before
|
575
|
+
@r.flushdb
|
576
|
+
@r.keys('*') { |r| r.should == []; done }
|
577
|
+
end
|
578
|
+
#
|
579
|
+
it "should be able to SELECT database" do
|
580
|
+
@r.select(15)
|
581
|
+
@r.get('foo') { |r| r.should == nil; done }
|
582
|
+
end
|
583
|
+
#
|
584
|
+
it "should be able to provide the last save time (LASTSAVE)" do
|
585
|
+
@r.lastsave do |savetime|
|
586
|
+
Time.at(savetime).class.should == Time
|
587
|
+
Time.at(savetime).should <= Time.now
|
588
|
+
done
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
it "should be able to MGET keys" do
|
593
|
+
@r['foo'] = 1000
|
594
|
+
@r['bar'] = 2000
|
595
|
+
@r.mget('foo', 'bar') { |r| r.should == ['1000', '2000'] }
|
596
|
+
@r.mget('foo', 'bar', 'baz') { |r| r.should == ['1000', '2000', nil] }
|
597
|
+
@r.ping { done }
|
598
|
+
end
|
599
|
+
|
600
|
+
it "should be able to mapped MGET keys" do
|
601
|
+
@r['foo'] = 1000
|
602
|
+
@r['bar'] = 2000
|
603
|
+
@r.mapped_mget('foo', 'bar') { |r| r.should == { 'foo' => '1000', 'bar' => '2000'} }
|
604
|
+
@r.mapped_mget('foo', 'baz', 'bar') { |r| r.should == { 'foo' => '1000', 'bar' => '2000'} }
|
605
|
+
@r.ping { done }
|
606
|
+
end
|
607
|
+
|
608
|
+
it "should be able to MSET values" do
|
609
|
+
@r.mset :key1 => "value1", :key2 => "value2"
|
610
|
+
@r.get('key1') { |r| r.should == "value1" }
|
611
|
+
@r.get('key2') { |r| r.should == "value2"; done }
|
612
|
+
end
|
613
|
+
|
614
|
+
it "should be able to MSETNX values" do
|
615
|
+
@r.msetnx :keynx1 => "valuenx1", :keynx2 => "valuenx2"
|
616
|
+
@r.mget('keynx1', 'keynx2') { |r| r.should == ["valuenx1", "valuenx2"] }
|
617
|
+
|
618
|
+
@r["keynx1"] = "value1"
|
619
|
+
@r["keynx2"] = "value2"
|
620
|
+
@r.msetnx :keynx1 => "valuenx1", :keynx2 => "valuenx2"
|
621
|
+
@r.mget('keynx1', 'keynx2') { |r| r.should == ["value1", "value2"]; done }
|
622
|
+
end
|
623
|
+
|
624
|
+
it "should bgsave" do
|
625
|
+
@r.bgsave do |r|
|
626
|
+
['OK', 'Background saving started'].include?(r).should == true
|
627
|
+
done
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
it "should be able to ECHO" do
|
632
|
+
@r.echo("message in a bottle\n") { |r| r.should == "message in a bottle\n"; done }
|
633
|
+
end
|
634
|
+
|
635
|
+
# Tests are disabled due uncatchable exceptions. We should use on_error callback,
|
636
|
+
# intead of raising exceptions in random places.
|
637
|
+
#
|
638
|
+
# it "should raise error when invoke MONITOR" do
|
639
|
+
# # lambda { @r.monitor }.should.raise
|
640
|
+
# done
|
641
|
+
# end
|
642
|
+
#
|
643
|
+
# it "should raise error when invoke SYNC" do
|
644
|
+
# # lambda { @r.sync }.should.raise
|
645
|
+
# done
|
646
|
+
# end
|
647
|
+
|
648
|
+
it "should run MULTI without a block" do
|
649
|
+
@r.multi
|
650
|
+
@r.get("key1") { |r| r.should == "QUEUED" }
|
651
|
+
@r.discard { done }
|
652
|
+
end
|
653
|
+
|
654
|
+
it "should run MULTI/EXEC with a block" do
|
655
|
+
@r.multi do
|
656
|
+
@r.set "key1", "value1"
|
657
|
+
end
|
658
|
+
|
659
|
+
@r.get("key1") { |r| r.should == "value1" }
|
660
|
+
|
661
|
+
begin
|
662
|
+
@r.multi do
|
663
|
+
@r.set "key2", "value2"
|
664
|
+
raise "Some error"
|
665
|
+
@r.set "key3", "value3"
|
666
|
+
end
|
667
|
+
rescue
|
668
|
+
end
|
669
|
+
|
670
|
+
@r.get("key2") { |r| r.should == nil }
|
671
|
+
@r.get("key3") { |r| r.should == nil; done}
|
672
|
+
end
|
673
|
+
|
674
|
+
it "should yield the Redis object when using #multi with a block" do
|
675
|
+
@r.multi do |multi|
|
676
|
+
multi.set "key1", "value1"
|
677
|
+
end
|
678
|
+
|
679
|
+
@r.get("key1") { |r| r.should == "value1"; done }
|
680
|
+
end
|
681
|
+
|
682
|
+
it "can set and get hash values" do
|
683
|
+
@r.hset("rush", "signals", "1982") { |r| r.should == true }
|
684
|
+
@r.hexists("rush", "signals") { |r| r.should == true }
|
685
|
+
@r.hget("rush", "signals") { |r| r.should == "1982"; done }
|
686
|
+
end
|
687
|
+
|
688
|
+
it "can delete hash values" do
|
689
|
+
@r.hset("rush", "YYZ", "1981")
|
690
|
+
@r.hdel("rush", "YYZ") { |r| r.should == true }
|
691
|
+
@r.hexists("rush", "YYZ") { |r| r.should == false; done }
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
# Yup, bacon can't handle nested describe blocks properly
|
696
|
+
EM.describe EM::Protocols::Redis, "with some hash values" do
|
697
|
+
default_timeout 1
|
698
|
+
|
699
|
+
before do
|
700
|
+
@r = EM::Protocols::Redis.connect :db => 14
|
701
|
+
@r.flushdb
|
702
|
+
@r['foo'] = 'bar'
|
703
|
+
@r.hset("rush", "permanent waves", "1980")
|
704
|
+
@r.hset("rush", "moving pictures", "1981")
|
705
|
+
@r.hset("rush", "signals", "1982")
|
706
|
+
end
|
707
|
+
|
708
|
+
after { @r.close_connection }
|
709
|
+
|
710
|
+
it "can get the length of the hash" do
|
711
|
+
@r.hlen("rush") { |r| r.should == 3 }
|
712
|
+
@r.hlen("yyz") { |r| r.should == 0; done }
|
713
|
+
end
|
714
|
+
|
715
|
+
it "can get the keys and values of the hash" do
|
716
|
+
@r.hkeys("rush") { |r| r.should == ["permanent waves", "moving pictures", "signals"] }
|
717
|
+
@r.hvals("rush") { |r| r.should == %w[1980 1981 1982] }
|
718
|
+
@r.hvals("yyz") { |r| r.should == []; done }
|
719
|
+
end
|
720
|
+
|
721
|
+
it "returns a hash for HGETALL" do
|
722
|
+
@r.hgetall("rush") do |r|
|
723
|
+
r.should == {
|
724
|
+
"permanent waves" => "1980",
|
725
|
+
"moving pictures" => "1981",
|
726
|
+
"signals" => "1982"
|
727
|
+
}
|
728
|
+
end
|
729
|
+
@r.hgetall("yyz") { |r| r.should == {}; done }
|
730
|
+
end
|
731
|
+
end
|
data/spec/redis_protocol_spec.rb
CHANGED
@@ -83,11 +83,11 @@ EM.describe EM::Protocols::Redis do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
should "parse an inline error response" do
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
end
|
90
|
-
|
86
|
+
@c.call_command(["blarg"]) do |resp|
|
87
|
+
resp.should == nil
|
88
|
+
done
|
89
|
+
end
|
90
|
+
@c.receive_data "-FAIL\r\n"
|
91
91
|
end
|
92
92
|
|
93
93
|
should "trigger a given error callback (specified with on_error) for inline error response instead of raising an error" do
|
data/spec/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: superfeedr-em-redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 7
|
10
|
+
version: 0.2.7
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Jonathan Broad
|
@@ -11,50 +17,74 @@ autorequire:
|
|
11
17
|
bindir: bin
|
12
18
|
cert_chain: []
|
13
19
|
|
14
|
-
date: 2010-
|
20
|
+
date: 2010-06-22 00:00:00 +02:00
|
15
21
|
default_executable:
|
16
22
|
dependencies:
|
17
23
|
- !ruby/object:Gem::Dependency
|
18
24
|
name: eventmachine
|
19
|
-
|
20
|
-
|
21
|
-
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
22
28
|
requirements:
|
23
29
|
- - ">="
|
24
30
|
- !ruby/object:Gem::Version
|
31
|
+
hash: 59
|
32
|
+
segments:
|
33
|
+
- 0
|
34
|
+
- 12
|
35
|
+
- 10
|
25
36
|
version: 0.12.10
|
26
|
-
|
37
|
+
type: :runtime
|
38
|
+
version_requirements: *id001
|
27
39
|
- !ruby/object:Gem::Dependency
|
28
40
|
name: bacon
|
29
|
-
|
30
|
-
|
31
|
-
|
41
|
+
prerelease: false
|
42
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
32
44
|
requirements:
|
33
45
|
- - ">="
|
34
46
|
- !ruby/object:Gem::Version
|
47
|
+
hash: 19
|
48
|
+
segments:
|
49
|
+
- 1
|
50
|
+
- 1
|
51
|
+
- 0
|
35
52
|
version: 1.1.0
|
36
|
-
|
53
|
+
type: :development
|
54
|
+
version_requirements: *id002
|
37
55
|
- !ruby/object:Gem::Dependency
|
38
56
|
name: em-spec
|
39
|
-
|
40
|
-
|
41
|
-
|
57
|
+
prerelease: false
|
58
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
42
60
|
requirements:
|
43
61
|
- - ">="
|
44
62
|
- !ruby/object:Gem::Version
|
63
|
+
hash: 21
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
- 2
|
67
|
+
- 1
|
45
68
|
version: 0.2.1
|
46
|
-
|
69
|
+
type: :development
|
70
|
+
version_requirements: *id003
|
47
71
|
- !ruby/object:Gem::Dependency
|
48
72
|
name: bones
|
49
|
-
|
50
|
-
|
51
|
-
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
52
76
|
requirements:
|
53
77
|
- - ">="
|
54
78
|
- !ruby/object:Gem::Version
|
79
|
+
hash: 21
|
80
|
+
segments:
|
81
|
+
- 3
|
82
|
+
- 4
|
83
|
+
- 1
|
55
84
|
version: 3.4.1
|
56
|
-
|
57
|
-
|
85
|
+
type: :development
|
86
|
+
version_requirements: *id004
|
87
|
+
description: An eventmachine-based implementation of the Redis protocol
|
58
88
|
email: stephan@spaceboyz.net
|
59
89
|
executables: []
|
60
90
|
|
@@ -64,7 +94,6 @@ extra_rdoc_files:
|
|
64
94
|
- History.txt
|
65
95
|
- README.rdoc
|
66
96
|
files:
|
67
|
-
- .gitignore
|
68
97
|
- History.txt
|
69
98
|
- README.rdoc
|
70
99
|
- Rakefile
|
@@ -73,6 +102,7 @@ files:
|
|
73
102
|
- lib/em-redis/redis_protocol.rb
|
74
103
|
- spec/live_redis_protocol_spec.rb
|
75
104
|
- spec/redis_commands_spec.rb
|
105
|
+
- spec/redis_commands_spec.rb.orig
|
76
106
|
- spec/redis_protocol_spec.rb
|
77
107
|
- spec/test_helper.rb
|
78
108
|
has_rdoc: true
|
@@ -86,23 +116,29 @@ rdoc_options:
|
|
86
116
|
require_paths:
|
87
117
|
- lib
|
88
118
|
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
89
120
|
requirements:
|
90
121
|
- - ">="
|
91
122
|
- !ruby/object:Gem::Version
|
123
|
+
hash: 3
|
124
|
+
segments:
|
125
|
+
- 0
|
92
126
|
version: "0"
|
93
|
-
version:
|
94
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
95
129
|
requirements:
|
96
130
|
- - ">="
|
97
131
|
- !ruby/object:Gem::Version
|
132
|
+
hash: 3
|
133
|
+
segments:
|
134
|
+
- 0
|
98
135
|
version: "0"
|
99
|
-
version:
|
100
136
|
requirements: []
|
101
137
|
|
102
138
|
rubyforge_project: superfeedr-em-redis
|
103
|
-
rubygems_version: 1.3.
|
139
|
+
rubygems_version: 1.3.7
|
104
140
|
signing_key:
|
105
141
|
specification_version: 3
|
106
|
-
summary: An eventmachine-based implementation of the Redis protocol
|
142
|
+
summary: An eventmachine-based implementation of the Redis protocol
|
107
143
|
test_files: []
|
108
144
|
|
data/.gitignore
DELETED