timcharper-redis 0.0.3.4
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/LICENSE +20 -0
- data/README.markdown +31 -0
- data/Rakefile +59 -0
- data/lib/dist_redis.rb +111 -0
- data/lib/hash_ring.rb +127 -0
- data/lib/pipeline.rb +31 -0
- data/lib/redis.rb +504 -0
- data/lib/server.rb +128 -0
- data/spec/redis_spec.rb +401 -0
- data/spec/spec_helper.rb +4 -0
- metadata +64 -0
data/lib/server.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
##
|
2
|
+
# This class represents a redis server instance.
|
3
|
+
|
4
|
+
class Server
|
5
|
+
|
6
|
+
##
|
7
|
+
# The amount of time to wait before attempting to re-establish a
|
8
|
+
# connection with a server that is marked dead.
|
9
|
+
|
10
|
+
RETRY_DELAY = 30.0
|
11
|
+
|
12
|
+
##
|
13
|
+
# The host the redis server is running on.
|
14
|
+
|
15
|
+
attr_reader :host
|
16
|
+
|
17
|
+
##
|
18
|
+
# The port the redis server is listening on.
|
19
|
+
|
20
|
+
attr_reader :port
|
21
|
+
|
22
|
+
##
|
23
|
+
#
|
24
|
+
|
25
|
+
attr_reader :replica
|
26
|
+
|
27
|
+
##
|
28
|
+
# The time of next retry if the connection is dead.
|
29
|
+
|
30
|
+
attr_reader :retry
|
31
|
+
|
32
|
+
##
|
33
|
+
# A text status string describing the state of the server.
|
34
|
+
|
35
|
+
attr_reader :status
|
36
|
+
|
37
|
+
##
|
38
|
+
# Create a new Redis::Server object for the redis instance
|
39
|
+
# listening on the given host and port.
|
40
|
+
|
41
|
+
def initialize(host, port = DEFAULT_PORT, timeout = 10)
|
42
|
+
raise ArgumentError, "No host specified" if host.nil? or host.empty?
|
43
|
+
raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero?
|
44
|
+
|
45
|
+
@host = host
|
46
|
+
@port = port.to_i
|
47
|
+
|
48
|
+
@sock = nil
|
49
|
+
@retry = nil
|
50
|
+
@status = 'NOT CONNECTED'
|
51
|
+
@timeout = timeout
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Return a string representation of the server object.
|
56
|
+
def inspect
|
57
|
+
"<Redis::Server: %s:%d (%s)>" % [@host, @port, @status]
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Try to connect to the redis server targeted by this object.
|
62
|
+
# Returns the connected socket object on success or nil on failure.
|
63
|
+
|
64
|
+
def socket
|
65
|
+
return @sock if @sock and not @sock.closed?
|
66
|
+
|
67
|
+
@sock = nil
|
68
|
+
|
69
|
+
# If the host was dead, don't retry for a while.
|
70
|
+
return if @retry and @retry > Time.now
|
71
|
+
|
72
|
+
# Attempt to connect if not already connected.
|
73
|
+
begin
|
74
|
+
@sock = connect_to(@host, @port, @timeout)
|
75
|
+
@sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
76
|
+
@retry = nil
|
77
|
+
@status = 'CONNECTED'
|
78
|
+
rescue Errno::EPIPE, Errno::ECONNREFUSED => e
|
79
|
+
puts "Socket died... socket: #{@sock.inspect}\n" if $debug
|
80
|
+
@sock.close
|
81
|
+
retry
|
82
|
+
rescue SocketError, SystemCallError, IOError => err
|
83
|
+
puts "Unable to open socket: #{err.class.name}, #{err.message}" if $debug
|
84
|
+
mark_dead err
|
85
|
+
end
|
86
|
+
|
87
|
+
return @sock
|
88
|
+
end
|
89
|
+
|
90
|
+
def connect_to(host, port, timeout=nil)
|
91
|
+
addrs = Socket.getaddrinfo(host, nil)
|
92
|
+
addr = addrs.detect { |ad| ad[0] == 'AF_INET' }
|
93
|
+
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
94
|
+
if timeout
|
95
|
+
secs = Integer(timeout)
|
96
|
+
usecs = Integer((timeout - secs) * 1_000_000)
|
97
|
+
optval = [secs, usecs].pack("l_2")
|
98
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
99
|
+
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
100
|
+
end
|
101
|
+
sock.connect(Socket.pack_sockaddr_in(port, addr[3]))
|
102
|
+
sock
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Close the connection to the redis server targeted by this
|
107
|
+
# object. The server is not considered dead.
|
108
|
+
|
109
|
+
def close
|
110
|
+
@sock.close if @sock && !@sock.closed?
|
111
|
+
@sock = nil
|
112
|
+
@retry = nil
|
113
|
+
@status = "NOT CONNECTED"
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# Mark the server as dead and close its socket.
|
118
|
+
def mark_dead(error)
|
119
|
+
@sock.close if @sock && !@sock.closed?
|
120
|
+
@sock = nil
|
121
|
+
@retry = Time.now #+ RETRY_DELAY
|
122
|
+
|
123
|
+
reason = "#{error.class.name}: #{error.message}"
|
124
|
+
@status = sprintf "%s:%s DEAD (%s), will retry at %s", @host, @port, reason, @retry
|
125
|
+
puts @status
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
data/spec/redis_spec.rb
ADDED
@@ -0,0 +1,401 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class Foo
|
4
|
+
attr_accessor :bar
|
5
|
+
def initialize(bar)
|
6
|
+
@bar = bar
|
7
|
+
end
|
8
|
+
|
9
|
+
def ==(other)
|
10
|
+
@bar == other.bar
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "redis" do
|
15
|
+
before(:all) do
|
16
|
+
@r = Redis.new
|
17
|
+
@r.select_db(15) # use database 15 for testing so we dont accidentally step on you real data
|
18
|
+
end
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
@r['foo'] = 'bar'
|
22
|
+
end
|
23
|
+
|
24
|
+
after(:each) do
|
25
|
+
@r.keys('*').each {|k| @r.delete k}
|
26
|
+
end
|
27
|
+
|
28
|
+
after(:all) do
|
29
|
+
@r.quit
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
it "should be able to GET a key" do
|
34
|
+
@r['foo'].should == 'bar'
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should be able to SET a key" do
|
38
|
+
@r['foo'] = 'nik'
|
39
|
+
@r['foo'].should == 'nik'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should properly handle trailing newline characters" do
|
43
|
+
@r['foo'] = "bar\n"
|
44
|
+
@r['foo'].should == "bar\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should store and retrieve all possible characters at the beginning and the end of a string" do
|
48
|
+
(0..255).each do |char_idx|
|
49
|
+
string = "#{char_idx.chr}---#{char_idx.chr}"
|
50
|
+
@r['foo'] = string
|
51
|
+
@r['foo'].should == string
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should be able to SET a key with an expiry" do
|
56
|
+
@r.set('foo', 'bar', 1)
|
57
|
+
@r['foo'].should == 'bar'
|
58
|
+
sleep 2
|
59
|
+
@r['foo'].should == nil
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should be able to SETNX(set_unless_exists)" do
|
63
|
+
@r['foo'] = 'nik'
|
64
|
+
@r['foo'].should == 'nik'
|
65
|
+
@r.set_unless_exists 'foo', 'bar'
|
66
|
+
@r['foo'].should == 'nik'
|
67
|
+
end
|
68
|
+
#
|
69
|
+
it "should be able to INCR(increment) a key" do
|
70
|
+
@r.delete('counter')
|
71
|
+
@r.incr('counter').should == 1
|
72
|
+
@r.incr('counter').should == 2
|
73
|
+
@r.incr('counter').should == 3
|
74
|
+
end
|
75
|
+
#
|
76
|
+
it "should be able to DECR(decrement) a key" do
|
77
|
+
@r.delete('counter')
|
78
|
+
@r.incr('counter').should == 1
|
79
|
+
@r.incr('counter').should == 2
|
80
|
+
@r.incr('counter').should == 3
|
81
|
+
@r.decr('counter').should == 2
|
82
|
+
@r.decr('counter', 2).should == 0
|
83
|
+
end
|
84
|
+
#
|
85
|
+
it "should be able to RANDKEY(return a random key)" do
|
86
|
+
@r.randkey.should_not be_nil
|
87
|
+
end
|
88
|
+
#
|
89
|
+
it "should be able to RENAME a key" do
|
90
|
+
@r.delete 'foo'
|
91
|
+
@r.delete 'bar'
|
92
|
+
@r['foo'] = 'hi'
|
93
|
+
@r.rename! 'foo', 'bar'
|
94
|
+
@r['bar'].should == 'hi'
|
95
|
+
end
|
96
|
+
#
|
97
|
+
it "should be able to RENAMENX(rename unless the new key already exists) a key" do
|
98
|
+
@r.delete 'foo'
|
99
|
+
@r.delete 'bar'
|
100
|
+
@r['foo'] = 'hi'
|
101
|
+
@r['bar'] = 'ohai'
|
102
|
+
lambda {@r.rename 'foo', 'bar'}.should raise_error(RedisRenameError)
|
103
|
+
@r['bar'].should == 'ohai'
|
104
|
+
end
|
105
|
+
#
|
106
|
+
it "should be able to EXPIRE a key" do
|
107
|
+
@r['foo'] = 'bar'
|
108
|
+
@r.expire('foo', 1)
|
109
|
+
@r['foo'].should == "bar"
|
110
|
+
sleep 2
|
111
|
+
@r['foo'].should == nil
|
112
|
+
end
|
113
|
+
#
|
114
|
+
it "should be able to EXISTS(check if key exists)" do
|
115
|
+
@r['foo'] = 'nik'
|
116
|
+
@r.key?('foo').should be_true
|
117
|
+
@r.delete 'foo'
|
118
|
+
@r.key?('foo').should be_false
|
119
|
+
end
|
120
|
+
#
|
121
|
+
it "should be able to KEYS(glob for keys)" do
|
122
|
+
@r.keys("f*").each do |key|
|
123
|
+
@r.delete key
|
124
|
+
end
|
125
|
+
@r['f'] = 'nik'
|
126
|
+
@r['fo'] = 'nak'
|
127
|
+
@r['foo'] = 'qux'
|
128
|
+
@r.keys("f*").sort.should == ['f','fo', 'foo'].sort
|
129
|
+
end
|
130
|
+
#
|
131
|
+
it "should be able to check the TYPE of a key" do
|
132
|
+
@r['foo'] = 'nik'
|
133
|
+
@r.type?('foo').should == "string"
|
134
|
+
@r.delete 'foo'
|
135
|
+
@r.type?('foo').should == "none"
|
136
|
+
end
|
137
|
+
#
|
138
|
+
it "should be able to push to the head of a list" do
|
139
|
+
@r.push_head "list", 'hello'
|
140
|
+
@r.push_head "list", 42
|
141
|
+
@r.type?('list').should == "list"
|
142
|
+
@r.list_length('list').should == 2
|
143
|
+
@r.pop_head('list').should == '42'
|
144
|
+
@r.delete('list')
|
145
|
+
end
|
146
|
+
#
|
147
|
+
it "should be able to push to the tail of a list" do
|
148
|
+
@r.push_tail "list", 'hello'
|
149
|
+
@r.type?('list').should == "list"
|
150
|
+
@r.list_length('list').should == 1
|
151
|
+
@r.delete('list')
|
152
|
+
end
|
153
|
+
#
|
154
|
+
it "should be able to pop the tail of a list" do
|
155
|
+
@r.push_tail "list", 'hello'
|
156
|
+
@r.push_tail "list", 'goodbye'
|
157
|
+
@r.type?('list').should == "list"
|
158
|
+
@r.list_length('list').should == 2
|
159
|
+
@r.pop_tail('list').should == 'goodbye'
|
160
|
+
@r.delete('list')
|
161
|
+
end
|
162
|
+
#
|
163
|
+
it "should be able to pop the head of a list" do
|
164
|
+
@r.push_tail "list", 'hello'
|
165
|
+
@r.push_tail "list", 'goodbye'
|
166
|
+
@r.type?('list').should == "list"
|
167
|
+
@r.list_length('list').should == 2
|
168
|
+
@r.pop_head('list').should == 'hello'
|
169
|
+
@r.delete('list')
|
170
|
+
end
|
171
|
+
#
|
172
|
+
it "should be able to get the length of a list" do
|
173
|
+
@r.push_tail "list", 'hello'
|
174
|
+
@r.push_tail "list", 'goodbye'
|
175
|
+
@r.type?('list').should == "list"
|
176
|
+
@r.list_length('list').should == 2
|
177
|
+
@r.delete('list')
|
178
|
+
end
|
179
|
+
#
|
180
|
+
it "should be able to get a range of values from a list" do
|
181
|
+
@r.push_tail "list", 'hello'
|
182
|
+
@r.push_tail "list", 'goodbye'
|
183
|
+
@r.push_tail "list", '1'
|
184
|
+
@r.push_tail "list", '2'
|
185
|
+
@r.push_tail "list", '3'
|
186
|
+
@r.type?('list').should == "list"
|
187
|
+
@r.list_length('list').should == 5
|
188
|
+
@r.list_range('list', 2, -1).should == ['1', '2', '3']
|
189
|
+
@r.delete('list')
|
190
|
+
end
|
191
|
+
#
|
192
|
+
it "should be able to trim a list" do
|
193
|
+
@r.push_tail "list", 'hello'
|
194
|
+
@r.push_tail "list", 'goodbye'
|
195
|
+
@r.push_tail "list", '1'
|
196
|
+
@r.push_tail "list", '2'
|
197
|
+
@r.push_tail "list", '3'
|
198
|
+
@r.type?('list').should == "list"
|
199
|
+
@r.list_length('list').should == 5
|
200
|
+
@r.list_trim 'list', 0, 1
|
201
|
+
@r.list_length('list').should == 2
|
202
|
+
@r.list_range('list', 0, -1).should == ['hello', 'goodbye']
|
203
|
+
@r.delete('list')
|
204
|
+
end
|
205
|
+
#
|
206
|
+
it "should be able to get a value by indexing into a list" do
|
207
|
+
@r.push_tail "list", 'hello'
|
208
|
+
@r.push_tail "list", 'goodbye'
|
209
|
+
@r.type?('list').should == "list"
|
210
|
+
@r.list_length('list').should == 2
|
211
|
+
@r.list_index('list', 1).should == 'goodbye'
|
212
|
+
@r.delete('list')
|
213
|
+
end
|
214
|
+
#
|
215
|
+
it "should be able to set a value by indexing into a list" do
|
216
|
+
@r.push_tail "list", 'hello'
|
217
|
+
@r.push_tail "list", 'hello'
|
218
|
+
@r.type?('list').should == "list"
|
219
|
+
@r.list_length('list').should == 2
|
220
|
+
@r.list_set('list', 1, 'goodbye').should be_true
|
221
|
+
@r.list_index('list', 1).should == 'goodbye'
|
222
|
+
@r.delete('list')
|
223
|
+
end
|
224
|
+
#
|
225
|
+
it "should be able to remove values from a list LREM" do
|
226
|
+
@r.push_tail "list", 'hello'
|
227
|
+
@r.push_tail "list", 'goodbye'
|
228
|
+
@r.type?('list').should == "list"
|
229
|
+
@r.list_length('list').should == 2
|
230
|
+
@r.list_rm('list', 1, 'hello').should == 1
|
231
|
+
@r.list_range('list', 0, -1).should == ['goodbye']
|
232
|
+
@r.delete('list')
|
233
|
+
end
|
234
|
+
#
|
235
|
+
it "should be able add members to a set" do
|
236
|
+
@r.set_add "set", 'key1'
|
237
|
+
@r.set_add "set", 'key2'
|
238
|
+
@r.type?('set').should == "set"
|
239
|
+
@r.set_count('set').should == 2
|
240
|
+
@r.set_members('set').sort.should == ['key1', 'key2'].sort
|
241
|
+
@r.delete('set')
|
242
|
+
end
|
243
|
+
#
|
244
|
+
it "should be able delete members to a set" do
|
245
|
+
@r.set_add "set", 'key1'
|
246
|
+
@r.set_add "set", 'key2'
|
247
|
+
@r.type?('set').should == "set"
|
248
|
+
@r.set_count('set').should == 2
|
249
|
+
@r.set_members('set').should == Set.new(['key1', 'key2'])
|
250
|
+
@r.set_delete('set', 'key1')
|
251
|
+
@r.set_count('set').should == 1
|
252
|
+
@r.set_members('set').should == Set.new(['key2'])
|
253
|
+
@r.delete('set')
|
254
|
+
end
|
255
|
+
#
|
256
|
+
it "should be able count the members of a set" do
|
257
|
+
@r.set_add "set", 'key1'
|
258
|
+
@r.set_add "set", 'key2'
|
259
|
+
@r.type?('set').should == "set"
|
260
|
+
@r.set_count('set').should == 2
|
261
|
+
@r.delete('set')
|
262
|
+
end
|
263
|
+
#
|
264
|
+
it "should be able test for set membership" do
|
265
|
+
@r.set_add "set", 'key1'
|
266
|
+
@r.set_add "set", 'key2'
|
267
|
+
@r.type?('set').should == "set"
|
268
|
+
@r.set_count('set').should == 2
|
269
|
+
@r.set_member?('set', 'key1').should be_true
|
270
|
+
@r.set_member?('set', 'key2').should be_true
|
271
|
+
@r.set_member?('set', 'notthere').should be_false
|
272
|
+
@r.delete('set')
|
273
|
+
end
|
274
|
+
#
|
275
|
+
it "should be able to do set intersection" do
|
276
|
+
@r.set_add "set", 'key1'
|
277
|
+
@r.set_add "set", 'key2'
|
278
|
+
@r.set_add "set2", 'key2'
|
279
|
+
@r.set_intersect('set', 'set2').should == Set.new(['key2'])
|
280
|
+
@r.delete('set')
|
281
|
+
end
|
282
|
+
#
|
283
|
+
it "should be able to do set intersection and store the results in a key" do
|
284
|
+
@r.set_add "set", 'key1'
|
285
|
+
@r.set_add "set", 'key2'
|
286
|
+
@r.set_add "set2", 'key2'
|
287
|
+
count = @r.set_inter_store('newone', 'set', 'set2')
|
288
|
+
count.should == 1
|
289
|
+
@r.set_members('newone').should == Set.new(['key2'])
|
290
|
+
@r.delete('set')
|
291
|
+
end
|
292
|
+
#
|
293
|
+
it "should be able to do set union" do
|
294
|
+
@r.set_add "set", 'key1'
|
295
|
+
@r.set_add "set", 'key2'
|
296
|
+
@r.set_add "set2", 'key2'
|
297
|
+
@r.set_add "set2", 'key3'
|
298
|
+
@r.set_union('set', 'set2').should == Set.new(['key1','key2','key3'])
|
299
|
+
@r.delete('set')
|
300
|
+
end
|
301
|
+
#
|
302
|
+
it "should be able to do set union and store the results in a key" do
|
303
|
+
@r.set_add "set", 'key1'
|
304
|
+
@r.set_add "set", 'key2'
|
305
|
+
@r.set_add "set2", 'key2'
|
306
|
+
@r.set_add "set2", 'key3'
|
307
|
+
count = @r.set_union_store('newone', 'set', 'set2')
|
308
|
+
count.should == 3
|
309
|
+
@r.set_members('newone').should == Set.new(['key1','key2','key3'])
|
310
|
+
@r.delete('set')
|
311
|
+
end
|
312
|
+
#
|
313
|
+
it "should be able to do set difference" do
|
314
|
+
@r.set_add "set", 'key1'
|
315
|
+
@r.set_add "set", 'key2'
|
316
|
+
@r.set_add "set2", 'key2'
|
317
|
+
@r.set_add "set2", 'key3'
|
318
|
+
@r.set_diff('set', 'set2').should == Set.new(['key1','key3'])
|
319
|
+
@r.delete('set')
|
320
|
+
end
|
321
|
+
#
|
322
|
+
it "should be able to do set difference and store the results in a key" do
|
323
|
+
@r.set_add "set", 'key1'
|
324
|
+
@r.set_add "set", 'key2'
|
325
|
+
@r.set_add "set2", 'key2'
|
326
|
+
@r.set_add "set2", 'key3'
|
327
|
+
count = @r.set_diff_store('newone', 'set', 'set2')
|
328
|
+
count.should == 3
|
329
|
+
@r.set_members('newone').should == Set.new(['key1','key3'])
|
330
|
+
@r.delete('set')
|
331
|
+
end
|
332
|
+
it "should be able to do crazy SORT queries" do
|
333
|
+
@r['dog_1'] = 'louie'
|
334
|
+
@r.push_tail 'dogs', 1
|
335
|
+
@r['dog_2'] = 'lucy'
|
336
|
+
@r.push_tail 'dogs', 2
|
337
|
+
@r['dog_3'] = 'max'
|
338
|
+
@r.push_tail 'dogs', 3
|
339
|
+
@r['dog_4'] = 'taj'
|
340
|
+
@r.push_tail 'dogs', 4
|
341
|
+
@r.sort('dogs', :get => 'dog_*', :limit => [0,1]).should == ['louie']
|
342
|
+
@r.sort('dogs', :get => 'dog_*', :limit => [0,1], :order => 'desc alpha').should == ['taj']
|
343
|
+
end
|
344
|
+
#
|
345
|
+
it "should provide info" do
|
346
|
+
[: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|
|
347
|
+
@r.info.keys.should include(x)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
#
|
351
|
+
it "should be able to flush the database" do
|
352
|
+
@r['key1'] = 'keyone'
|
353
|
+
@r['key2'] = 'keytwo'
|
354
|
+
@r.keys('*').sort.should == ['foo', 'key1', 'key2'] #foo from before
|
355
|
+
@r.flush_db
|
356
|
+
@r.keys('*').should == []
|
357
|
+
end
|
358
|
+
#
|
359
|
+
it "should be able to provide the last save time" do
|
360
|
+
savetime = @r.last_save
|
361
|
+
Time.at(savetime).class.should == Time
|
362
|
+
Time.at(savetime).should <= Time.now
|
363
|
+
end
|
364
|
+
|
365
|
+
it "should be able to MGET keys" do
|
366
|
+
@r['foo'] = 1000
|
367
|
+
@r['bar'] = 2000
|
368
|
+
@r.mget('foo', 'bar').should == ['1000', '2000']
|
369
|
+
@r.mget('foo', 'bar', 'baz').should == ['1000', '2000', nil]
|
370
|
+
end
|
371
|
+
|
372
|
+
it "should bgsave" do
|
373
|
+
lambda {@r.bgsave}.should_not raise_error(RedisError)
|
374
|
+
end
|
375
|
+
|
376
|
+
it "should handle multiple servers" do
|
377
|
+
require 'dist_redis'
|
378
|
+
@r = DistRedis.new('localhost:6379', '127.0.0.1:6379')
|
379
|
+
@r.select_db(15) # use database 15 for testing so we dont accidentally step on you real data
|
380
|
+
|
381
|
+
100.times do |idx|
|
382
|
+
@r[idx] = "foo#{idx}"
|
383
|
+
end
|
384
|
+
|
385
|
+
100.times do |idx|
|
386
|
+
@r[idx].should == "foo#{idx}"
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
it "should be able to pipeline writes" do
|
391
|
+
@r.pipelined do |pipeline|
|
392
|
+
pipeline.push_head "list", "hello"
|
393
|
+
pipeline.push_head "list", 42
|
394
|
+
end
|
395
|
+
|
396
|
+
@r.type?('list').should == "list"
|
397
|
+
@r.list_length('list').should == 2
|
398
|
+
@r.pop_head('list').should == '42'
|
399
|
+
@r.delete('list')
|
400
|
+
end
|
401
|
+
end
|