em-redis 0.2.2 → 0.2.3
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/History.txt +1 -1
- data/README.rdoc +12 -10
- data/Rakefile +11 -2
- data/lib/em-redis.rb +1 -1
- data/lib/em-redis/redis_protocol.rb +180 -88
- data/spec/live_redis_protocol_spec.rb +12 -18
- data/spec/redis_commands_spec.rb +761 -0
- data/spec/redis_protocol_spec.rb +10 -2
- data/spec/test_helper.rb +7 -0
- metadata +50 -36
- data/Manifest.txt +0 -10
- data/tasks/em-redis.rake +0 -12
@@ -3,8 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + "/test_helper.rb")
|
|
3
3
|
EM.describe EM::Protocols::Redis, "connected to an empty db" do
|
4
4
|
|
5
5
|
before do
|
6
|
-
@c = EM::Protocols::Redis.connect
|
7
|
-
@c.select "14"
|
6
|
+
@c = EM::Protocols::Redis.connect :db => 14
|
8
7
|
@c.flushdb
|
9
8
|
end
|
10
9
|
|
@@ -54,14 +53,14 @@ EM.describe EM::Protocols::Redis, "connected to an empty db" do
|
|
54
53
|
|
55
54
|
should "be able to 'lpush' to a nonexistent list" do
|
56
55
|
@c.lpush("foo", "bar") do |r|
|
57
|
-
r.should ==
|
56
|
+
r.should == 1
|
58
57
|
done
|
59
58
|
end
|
60
59
|
end
|
61
60
|
|
62
61
|
should "be able to 'rpush' to a nonexistent list" do
|
63
62
|
@c.rpush("foo", "bar") do |r|
|
64
|
-
r.should ==
|
63
|
+
r.should == 1
|
65
64
|
done
|
66
65
|
end
|
67
66
|
end
|
@@ -107,8 +106,7 @@ end
|
|
107
106
|
EM.describe EM::Protocols::Redis, "connected to a db containing some simple string-valued keys" do
|
108
107
|
|
109
108
|
before do
|
110
|
-
@c = EM::Protocols::Redis.connect
|
111
|
-
@c.select "14"
|
109
|
+
@c = EM::Protocols::Redis.connect :db => 14
|
112
110
|
@c.flushdb
|
113
111
|
@c.set "a", "b"
|
114
112
|
@c.set "x", "y"
|
@@ -206,8 +204,7 @@ end
|
|
206
204
|
EM.describe EM::Protocols::Redis, "connected to a db containing a list" do
|
207
205
|
|
208
206
|
before do
|
209
|
-
@c = EM::Protocols::Redis.connect
|
210
|
-
@c.select "14"
|
207
|
+
@c = EM::Protocols::Redis.connect :db => 14
|
211
208
|
@c.flushdb
|
212
209
|
@c.lpush "foo", "c"
|
213
210
|
@c.lpush "foo", "b"
|
@@ -225,7 +222,7 @@ EM.describe EM::Protocols::Redis, "connected to a db containing a list" do
|
|
225
222
|
|
226
223
|
should "be able to 'rpush' onto the tail of the list" do
|
227
224
|
@c.rpush "foo", "d" do |r|
|
228
|
-
r.should ==
|
225
|
+
r.should == 4
|
229
226
|
@c.rpop "foo" do |r|
|
230
227
|
r.should == "d"
|
231
228
|
done
|
@@ -235,7 +232,7 @@ EM.describe EM::Protocols::Redis, "connected to a db containing a list" do
|
|
235
232
|
|
236
233
|
should "be able to 'lpush' onto the head of the list" do
|
237
234
|
@c.lpush "foo", "d" do |r|
|
238
|
-
r.should ==
|
235
|
+
r.should == 4
|
239
236
|
@c.lpop "foo" do |r|
|
240
237
|
r.should == "d"
|
241
238
|
done
|
@@ -295,8 +292,7 @@ end
|
|
295
292
|
|
296
293
|
EM.describe EM::Protocols::Redis, "connected to a db containing two sets" do
|
297
294
|
before do
|
298
|
-
@c = EM::Protocols::Redis.connect
|
299
|
-
@c.select "14"
|
295
|
+
@c = EM::Protocols::Redis.connect :db => 14
|
300
296
|
@c.flushdb
|
301
297
|
@c.sadd "foo", "a"
|
302
298
|
@c.sadd "foo", "b"
|
@@ -402,8 +398,7 @@ end
|
|
402
398
|
|
403
399
|
EM.describe EM::Protocols::Redis, "connected to a db containing three linked lists" do
|
404
400
|
before do
|
405
|
-
@c = EM::Protocols::Redis.connect
|
406
|
-
@c.select "14"
|
401
|
+
@c = EM::Protocols::Redis.connect :db => 14
|
407
402
|
@c.flushdb
|
408
403
|
@c.rpush "foo", "a"
|
409
404
|
@c.rpush "foo", "b"
|
@@ -422,7 +417,7 @@ EM.describe EM::Protocols::Redis, "connected to a db containing three linked lis
|
|
422
417
|
|
423
418
|
should "be able to get keys selectively" do
|
424
419
|
@c.keys "a_*" do |r|
|
425
|
-
r.should == ["a_sort", "a_data"]
|
420
|
+
r.should == ["a_sort", "a_data"].sort
|
426
421
|
done
|
427
422
|
end
|
428
423
|
end
|
@@ -430,8 +425,7 @@ end
|
|
430
425
|
|
431
426
|
EM.describe EM::Protocols::Redis, "when reconnecting" do
|
432
427
|
before do
|
433
|
-
@c = EM::Protocols::Redis.connect
|
434
|
-
@c.select "14"
|
428
|
+
@c = EM::Protocols::Redis.connect :db => 14
|
435
429
|
@c.flushdb
|
436
430
|
end
|
437
431
|
|
@@ -449,4 +443,4 @@ EM.describe EM::Protocols::Redis, "when reconnecting" do
|
|
449
443
|
end
|
450
444
|
end
|
451
445
|
end
|
452
|
-
end
|
446
|
+
end
|
@@ -0,0 +1,761 @@
|
|
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
|
+
# should raise error
|
556
|
+
@r.on_error { true.should == true }
|
557
|
+
@r.zset_incr_by("i_am_not_a_zet", 23, "element") { false.should == true }
|
558
|
+
|
559
|
+
@r.delete("hackers")
|
560
|
+
@r.delete("i_am_not_a_zet") { done }
|
561
|
+
end
|
562
|
+
#
|
563
|
+
it "should provide info (INFO)" do
|
564
|
+
@r.info do |r|
|
565
|
+
[: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|
|
566
|
+
r.keys.include?(x).should == true
|
567
|
+
end
|
568
|
+
done
|
569
|
+
end
|
570
|
+
end
|
571
|
+
#
|
572
|
+
it "should be able to flush the database (FLUSHDB)" do
|
573
|
+
@r['key1'] = 'keyone'
|
574
|
+
@r['key2'] = 'keytwo'
|
575
|
+
@r.keys('*') { |r| r.sort.should == ['foo', 'key1', 'key2'].sort } #foo from before
|
576
|
+
@r.flushdb
|
577
|
+
@r.keys('*') { |r| r.should == []; done }
|
578
|
+
end
|
579
|
+
#
|
580
|
+
it "should be able to SELECT database" do
|
581
|
+
@r.select(15)
|
582
|
+
@r.get('foo') { |r| r.should == nil; done }
|
583
|
+
end
|
584
|
+
#
|
585
|
+
it "should be able to provide the last save time (LASTSAVE)" do
|
586
|
+
@r.lastsave do |savetime|
|
587
|
+
Time.at(savetime).class.should == Time
|
588
|
+
Time.at(savetime).should <= Time.now
|
589
|
+
done
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
it "should be able to MGET keys" do
|
594
|
+
@r['foo'] = 1000
|
595
|
+
@r['bar'] = 2000
|
596
|
+
@r.mget('foo', 'bar') { |r| r.should == ['1000', '2000'] }
|
597
|
+
@r.mget('foo', 'bar', 'baz') { |r| r.should == ['1000', '2000', nil] }
|
598
|
+
@r.ping { done }
|
599
|
+
end
|
600
|
+
|
601
|
+
it "should be able to mapped MGET keys" do
|
602
|
+
@r['foo'] = 1000
|
603
|
+
@r['bar'] = 2000
|
604
|
+
@r.mapped_mget('foo', 'bar') { |r| r.should == { 'foo' => '1000', 'bar' => '2000'} }
|
605
|
+
@r.mapped_mget('foo', 'baz', 'bar') { |r| r.should == { 'foo' => '1000', 'bar' => '2000'} }
|
606
|
+
@r.ping { done }
|
607
|
+
end
|
608
|
+
|
609
|
+
it "should be able to MSET values" do
|
610
|
+
@r.mset :key1 => "value1", :key2 => "value2"
|
611
|
+
@r.get('key1') { |r| r.should == "value1" }
|
612
|
+
@r.get('key2') { |r| r.should == "value2"; done }
|
613
|
+
end
|
614
|
+
|
615
|
+
it "should be able to MSETNX values" do
|
616
|
+
@r.msetnx :keynx1 => "valuenx1", :keynx2 => "valuenx2"
|
617
|
+
@r.mget('keynx1', 'keynx2') { |r| r.should == ["valuenx1", "valuenx2"] }
|
618
|
+
|
619
|
+
@r["keynx1"] = "value1"
|
620
|
+
@r["keynx2"] = "value2"
|
621
|
+
@r.msetnx :keynx1 => "valuenx1", :keynx2 => "valuenx2"
|
622
|
+
@r.mget('keynx1', 'keynx2') { |r| r.should == ["value1", "value2"]; done }
|
623
|
+
end
|
624
|
+
|
625
|
+
it "should bgsave" do
|
626
|
+
@r.bgsave do |r|
|
627
|
+
['OK', 'Background saving started'].include?(r).should == true
|
628
|
+
done
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
it "should be able to ECHO" do
|
633
|
+
@r.echo("message in a bottle\n") { |r| r.should == "message in a bottle\n"; done }
|
634
|
+
end
|
635
|
+
|
636
|
+
# Tests are disabled due uncatchable exceptions. We should use on_error callback,
|
637
|
+
# intead of raising exceptions in random places.
|
638
|
+
#
|
639
|
+
# it "should raise error when invoke MONITOR" do
|
640
|
+
# # lambda { @r.monitor }.should.raise
|
641
|
+
# done
|
642
|
+
# end
|
643
|
+
#
|
644
|
+
# it "should raise error when invoke SYNC" do
|
645
|
+
# # lambda { @r.sync }.should.raise
|
646
|
+
# done
|
647
|
+
# end
|
648
|
+
|
649
|
+
it "should run MULTI without a block" do
|
650
|
+
@r.multi
|
651
|
+
@r.get("key1") { |r| r.should == "QUEUED" }
|
652
|
+
@r.discard { done }
|
653
|
+
end
|
654
|
+
|
655
|
+
it "should run MULTI/EXEC with a block" do
|
656
|
+
@r.multi do
|
657
|
+
@r.set "key1", "value1"
|
658
|
+
end
|
659
|
+
|
660
|
+
@r.get("key1") { |r| r.should == "value1" }
|
661
|
+
|
662
|
+
begin
|
663
|
+
@r.multi do
|
664
|
+
@r.set "key2", "value2"
|
665
|
+
raise "Some error"
|
666
|
+
@r.set "key3", "value3"
|
667
|
+
end
|
668
|
+
rescue
|
669
|
+
end
|
670
|
+
|
671
|
+
@r.get("key2") { |r| r.should == nil }
|
672
|
+
@r.get("key3") { |r| r.should == nil; done}
|
673
|
+
end
|
674
|
+
|
675
|
+
it "should yield the Redis object when using #multi with a block" do
|
676
|
+
@r.multi do |multi|
|
677
|
+
multi.set "key1", "value1"
|
678
|
+
end
|
679
|
+
|
680
|
+
@r.get("key1") { |r| r.should == "value1"; done }
|
681
|
+
end
|
682
|
+
|
683
|
+
it "can set and get hash values" do
|
684
|
+
@r.hset("rush", "signals", "1982") { |r| r.should == true }
|
685
|
+
@r.hexists("rush", "signals") { |r| r.should == true }
|
686
|
+
@r.hget("rush", "signals") { |r| r.should == "1982"; done }
|
687
|
+
end
|
688
|
+
|
689
|
+
it "can delete hash values" do
|
690
|
+
@r.hset("rush", "YYZ", "1981")
|
691
|
+
@r.hdel("rush", "YYZ") { |r| r.should == true }
|
692
|
+
@r.hexists("rush", "YYZ") { |r| r.should == false; done }
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
# Yup, bacon can't handle nested describe blocks properly
|
697
|
+
EM.describe EM::Protocols::Redis, "with some hash values" do
|
698
|
+
default_timeout 1
|
699
|
+
|
700
|
+
before do
|
701
|
+
@r = EM::Protocols::Redis.connect :db => 14
|
702
|
+
@r.flushdb
|
703
|
+
@r['foo'] = 'bar'
|
704
|
+
@r.hset("rush", "permanent waves", "1980")
|
705
|
+
@r.hset("rush", "moving pictures", "1981")
|
706
|
+
@r.hset("rush", "signals", "1982")
|
707
|
+
end
|
708
|
+
|
709
|
+
after { @r.close_connection }
|
710
|
+
|
711
|
+
it "can get the length of the hash" do
|
712
|
+
@r.hlen("rush") { |r| r.should == 3 }
|
713
|
+
@r.hlen("yyz") { |r| r.should == 0; done }
|
714
|
+
end
|
715
|
+
|
716
|
+
it "can get the keys and values of the hash" do
|
717
|
+
@r.hkeys("rush") { |r| r.should == ["permanent waves", "moving pictures", "signals"] }
|
718
|
+
@r.hvals("rush") { |r| r.should == %w[1980 1981 1982] }
|
719
|
+
@r.hvals("yyz") { |r| r.should == []; done }
|
720
|
+
end
|
721
|
+
|
722
|
+
it "returns a hash for HGETALL" do
|
723
|
+
@r.hgetall("rush") do |r|
|
724
|
+
r.should == {
|
725
|
+
"permanent waves" => "1980",
|
726
|
+
"moving pictures" => "1981",
|
727
|
+
"signals" => "1982"
|
728
|
+
}
|
729
|
+
end
|
730
|
+
@r.hgetall("yyz") { |r| r.should == {}; done }
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
EM.describe EM::Protocols::Redis, "with nested multi-bulk response" do
|
735
|
+
default_timeout 1
|
736
|
+
|
737
|
+
before do
|
738
|
+
@r = EM::Protocols::Redis.connect :db => 14
|
739
|
+
@r.flushdb
|
740
|
+
@r.set 'user:one:id', 'id-one'
|
741
|
+
@r.set 'user:two:id', 'id-two'
|
742
|
+
@r.sadd "user:one:interests", "first-interest"
|
743
|
+
@r.sadd "user:one:interests", "second-interest"
|
744
|
+
@r.sadd "user:two:interests", "third-interest"
|
745
|
+
end
|
746
|
+
|
747
|
+
after { @r.close_connection }
|
748
|
+
|
749
|
+
it "returns array of arrays" do
|
750
|
+
@r.multi
|
751
|
+
@r.smembers "user:one:interests"
|
752
|
+
@r.smembers "user:two:interests"
|
753
|
+
@r.exec do |user_interests|
|
754
|
+
user_interests.should == [["second-interest", "first-interest"], ['third-interest']]
|
755
|
+
end
|
756
|
+
@r.mget("user:one:id", "user:two:id") do |user_ids|
|
757
|
+
user_ids.should == ['id-one', 'id-two']
|
758
|
+
done
|
759
|
+
end
|
760
|
+
end
|
761
|
+
end
|