redis 0.1.2 → 0.2.0
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 +3 -1
- data/lib/edis.rb +3 -0
- data/lib/redis/client.rb +414 -0
- data/lib/redis/dist_redis.rb +114 -0
- data/lib/redis/hash_ring.rb +131 -0
- data/lib/{pipeline.rb → redis/pipeline.rb} +4 -4
- data/lib/redis.rb +11 -361
- data/spec/redis_spec.rb +90 -6
- metadata +23 -12
- data/lib/dist_redis.rb +0 -149
- data/lib/hash_ring.rb +0 -135
data/lib/redis.rb
CHANGED
@@ -1,373 +1,23 @@
|
|
1
1
|
require 'socket'
|
2
|
-
|
2
|
+
|
3
|
+
module RedisRb
|
4
|
+
VERSION = "0.2.0"
|
5
|
+
end
|
3
6
|
|
4
7
|
begin
|
5
8
|
if RUBY_VERSION >= '1.9'
|
6
9
|
require 'timeout'
|
7
|
-
RedisTimer = Timeout
|
10
|
+
RedisRb::RedisTimer = Timeout
|
8
11
|
else
|
9
12
|
require 'system_timer'
|
10
|
-
RedisTimer = SystemTimer
|
13
|
+
RedisRb::RedisTimer = SystemTimer
|
11
14
|
end
|
12
15
|
rescue LoadError
|
13
|
-
RedisTimer = nil
|
16
|
+
RedisRb::RedisTimer = nil
|
14
17
|
end
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
MINUS = "-".freeze
|
19
|
-
PLUS = "+".freeze
|
20
|
-
COLON = ":".freeze
|
21
|
-
DOLLAR = "$".freeze
|
22
|
-
ASTERISK = "*".freeze
|
23
|
-
|
24
|
-
BULK_COMMANDS = {
|
25
|
-
"set" => true,
|
26
|
-
"setnx" => true,
|
27
|
-
"rpush" => true,
|
28
|
-
"lpush" => true,
|
29
|
-
"lset" => true,
|
30
|
-
"lrem" => true,
|
31
|
-
"sadd" => true,
|
32
|
-
"srem" => true,
|
33
|
-
"sismember" => true,
|
34
|
-
"echo" => true,
|
35
|
-
"getset" => true,
|
36
|
-
"smove" => true,
|
37
|
-
"zadd" => true,
|
38
|
-
"zincrby" => true,
|
39
|
-
"zrem" => true,
|
40
|
-
"zscore" => true
|
41
|
-
}
|
42
|
-
|
43
|
-
MULTI_BULK_COMMANDS = {
|
44
|
-
"mset" => true,
|
45
|
-
"msetnx" => true
|
46
|
-
}
|
47
|
-
|
48
|
-
BOOLEAN_PROCESSOR = lambda{|r| r == 1 }
|
49
|
-
|
50
|
-
REPLY_PROCESSOR = {
|
51
|
-
"exists" => BOOLEAN_PROCESSOR,
|
52
|
-
"sismember" => BOOLEAN_PROCESSOR,
|
53
|
-
"sadd" => BOOLEAN_PROCESSOR,
|
54
|
-
"srem" => BOOLEAN_PROCESSOR,
|
55
|
-
"smove" => BOOLEAN_PROCESSOR,
|
56
|
-
"zadd" => BOOLEAN_PROCESSOR,
|
57
|
-
"zrem" => BOOLEAN_PROCESSOR,
|
58
|
-
"move" => BOOLEAN_PROCESSOR,
|
59
|
-
"setnx" => BOOLEAN_PROCESSOR,
|
60
|
-
"del" => BOOLEAN_PROCESSOR,
|
61
|
-
"renamenx" => BOOLEAN_PROCESSOR,
|
62
|
-
"expire" => BOOLEAN_PROCESSOR,
|
63
|
-
"keys" => lambda{|r| r.split(" ")},
|
64
|
-
"info" => lambda{|r|
|
65
|
-
info = {}
|
66
|
-
r.each_line {|kv|
|
67
|
-
k,v = kv.split(":",2).map{|x| x.chomp}
|
68
|
-
info[k.to_sym] = v
|
69
|
-
}
|
70
|
-
info
|
71
|
-
}
|
72
|
-
}
|
73
|
-
|
74
|
-
ALIASES = {
|
75
|
-
"flush_db" => "flushdb",
|
76
|
-
"flush_all" => "flushall",
|
77
|
-
"last_save" => "lastsave",
|
78
|
-
"key?" => "exists",
|
79
|
-
"delete" => "del",
|
80
|
-
"randkey" => "randomkey",
|
81
|
-
"list_length" => "llen",
|
82
|
-
"push_tail" => "rpush",
|
83
|
-
"push_head" => "lpush",
|
84
|
-
"pop_tail" => "rpop",
|
85
|
-
"pop_head" => "lpop",
|
86
|
-
"list_set" => "lset",
|
87
|
-
"list_range" => "lrange",
|
88
|
-
"list_trim" => "ltrim",
|
89
|
-
"list_index" => "lindex",
|
90
|
-
"list_rm" => "lrem",
|
91
|
-
"set_add" => "sadd",
|
92
|
-
"set_delete" => "srem",
|
93
|
-
"set_count" => "scard",
|
94
|
-
"set_member?" => "sismember",
|
95
|
-
"set_members" => "smembers",
|
96
|
-
"set_intersect" => "sinter",
|
97
|
-
"set_intersect_store" => "sinterstore",
|
98
|
-
"set_inter_store" => "sinterstore",
|
99
|
-
"set_union" => "sunion",
|
100
|
-
"set_union_store" => "sunionstore",
|
101
|
-
"set_diff" => "sdiff",
|
102
|
-
"set_diff_store" => "sdiffstore",
|
103
|
-
"set_move" => "smove",
|
104
|
-
"set_unless_exists" => "setnx",
|
105
|
-
"rename_unless_exists" => "renamenx",
|
106
|
-
"type?" => "type",
|
107
|
-
"zset_add" => "zadd",
|
108
|
-
"zset_count" => "zcard",
|
109
|
-
"zset_range_by_score" => "zrangebyscore",
|
110
|
-
"zset_reverse_range" => "zrevrange",
|
111
|
-
"zset_range" => "zrange",
|
112
|
-
"zset_delete" => "zrem",
|
113
|
-
"zset_score" => "zscore",
|
114
|
-
"zset_incr_by" => "zincrby",
|
115
|
-
"zset_increment_by" => "zincrby"
|
116
|
-
}
|
117
|
-
|
118
|
-
DISABLED_COMMANDS = {
|
119
|
-
"monitor" => true,
|
120
|
-
"sync" => true
|
121
|
-
}
|
122
|
-
|
123
|
-
def initialize(options = {})
|
124
|
-
@host = options[:host] || '127.0.0.1'
|
125
|
-
@port = (options[:port] || 6379).to_i
|
126
|
-
@db = (options[:db] || 0).to_i
|
127
|
-
@timeout = (options[:timeout] || 5).to_i
|
128
|
-
@password = options[:password]
|
129
|
-
@logger = options[:logger]
|
130
|
-
@thread_safe = options[:thread_safe]
|
131
|
-
@mutex = Mutex.new if @thread_safe
|
132
|
-
@sock = nil
|
133
|
-
|
134
|
-
@logger.info { self.to_s } if @logger
|
135
|
-
end
|
136
|
-
|
137
|
-
def to_s
|
138
|
-
"Redis Client connected to #{server} against DB #{@db}"
|
139
|
-
end
|
140
|
-
|
141
|
-
def server
|
142
|
-
"#{@host}:#{@port}"
|
143
|
-
end
|
144
|
-
|
145
|
-
def connect_to_server
|
146
|
-
@sock = connect_to(@host, @port, @timeout == 0 ? nil : @timeout)
|
147
|
-
call_command(["auth",@password]) if @password
|
148
|
-
call_command(["select",@db]) unless @db == 0
|
149
|
-
end
|
150
|
-
|
151
|
-
def connect_to(host, port, timeout=nil)
|
152
|
-
# We support connect() timeout only if system_timer is availabe
|
153
|
-
# or if we are running against Ruby >= 1.9
|
154
|
-
# Timeout reading from the socket instead will be supported anyway.
|
155
|
-
if @timeout != 0 and RedisTimer
|
156
|
-
begin
|
157
|
-
sock = TCPSocket.new(host, port)
|
158
|
-
rescue Timeout::Error
|
159
|
-
@sock = nil
|
160
|
-
raise Timeout::Error, "Timeout connecting to the server"
|
161
|
-
end
|
162
|
-
else
|
163
|
-
sock = TCPSocket.new(host, port)
|
164
|
-
end
|
165
|
-
sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
166
|
-
|
167
|
-
# If the timeout is set we set the low level socket options in order
|
168
|
-
# to make sure a blocking read will return after the specified number
|
169
|
-
# of seconds. This hack is from memcached ruby client.
|
170
|
-
if timeout
|
171
|
-
secs = Integer(timeout)
|
172
|
-
usecs = Integer((timeout - secs) * 1_000_000)
|
173
|
-
optval = [secs, usecs].pack("l_2")
|
174
|
-
begin
|
175
|
-
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
|
176
|
-
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
|
177
|
-
rescue Exception => ex
|
178
|
-
# Solaris, for one, does not like/support socket timeouts.
|
179
|
-
@logger.info "Unable to use raw socket timeouts: #{ex.class.name}: #{ex.message}" if @logger
|
180
|
-
end
|
181
|
-
end
|
182
|
-
sock
|
183
|
-
end
|
184
|
-
|
185
|
-
def method_missing(*argv)
|
186
|
-
call_command(argv)
|
187
|
-
end
|
188
|
-
|
189
|
-
def call_command(argv)
|
190
|
-
@logger.debug { argv.inspect } if @logger
|
191
|
-
|
192
|
-
# this wrapper to raw_call_command handle reconnection on socket
|
193
|
-
# error. We try to reconnect just one time, otherwise let the error
|
194
|
-
# araise.
|
195
|
-
connect_to_server if !@sock
|
196
|
-
|
197
|
-
begin
|
198
|
-
raw_call_command(argv.dup)
|
199
|
-
rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED
|
200
|
-
@sock.close rescue nil
|
201
|
-
@sock = nil
|
202
|
-
connect_to_server
|
203
|
-
raw_call_command(argv.dup)
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
def raw_call_command(argvp)
|
208
|
-
pipeline = argvp[0].is_a?(Array)
|
209
|
-
|
210
|
-
unless pipeline
|
211
|
-
argvv = [argvp]
|
212
|
-
else
|
213
|
-
argvv = argvp
|
214
|
-
end
|
215
|
-
|
216
|
-
if MULTI_BULK_COMMANDS[argvv.flatten[0].to_s]
|
217
|
-
# TODO improve this code
|
218
|
-
argvp = argvv.flatten
|
219
|
-
values = argvp.pop.to_a.flatten
|
220
|
-
argvp = values.unshift(argvp[0])
|
221
|
-
command = ["*#{argvp.size}"]
|
222
|
-
argvp.each do |v|
|
223
|
-
v = v.to_s
|
224
|
-
command << "$#{get_size(v)}"
|
225
|
-
command << v
|
226
|
-
end
|
227
|
-
command = command.map {|cmd| "#{cmd}\r\n"}.join
|
228
|
-
else
|
229
|
-
command = ""
|
230
|
-
argvv.each do |argv|
|
231
|
-
bulk = nil
|
232
|
-
argv[0] = argv[0].to_s.downcase
|
233
|
-
argv[0] = ALIASES[argv[0]] if ALIASES[argv[0]]
|
234
|
-
raise "#{argv[0]} command is disabled" if DISABLED_COMMANDS[argv[0]]
|
235
|
-
if BULK_COMMANDS[argv[0]] and argv.length > 1
|
236
|
-
bulk = argv[-1].to_s
|
237
|
-
argv[-1] = get_size(bulk)
|
238
|
-
end
|
239
|
-
command << "#{argv.join(' ')}\r\n"
|
240
|
-
command << "#{bulk}\r\n" if bulk
|
241
|
-
end
|
242
|
-
end
|
243
|
-
results = maybe_lock { process_command(command, argvv) }
|
244
|
-
|
245
|
-
return pipeline ? results : results[0]
|
246
|
-
end
|
247
|
-
|
248
|
-
def process_command(command, argvv)
|
249
|
-
@sock.write(command)
|
250
|
-
argvv.map do |argv|
|
251
|
-
processor = REPLY_PROCESSOR[argv[0]]
|
252
|
-
processor ? processor.call(read_reply) : read_reply
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
def maybe_lock(&block)
|
257
|
-
if @thread_safe
|
258
|
-
@mutex.synchronize &block
|
259
|
-
else
|
260
|
-
block.call
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
def select(*args)
|
265
|
-
raise "SELECT not allowed, use the :db option when creating the object"
|
266
|
-
end
|
267
|
-
|
268
|
-
def [](key)
|
269
|
-
self.get(key)
|
270
|
-
end
|
271
|
-
|
272
|
-
def []=(key,value)
|
273
|
-
set(key,value)
|
274
|
-
end
|
275
|
-
|
276
|
-
def set(key, value, expiry=nil)
|
277
|
-
s = call_command([:set, key, value]) == OK
|
278
|
-
expire(key, expiry) if s && expiry
|
279
|
-
s
|
280
|
-
end
|
281
|
-
|
282
|
-
def sort(key, options = {})
|
283
|
-
cmd = ["SORT"]
|
284
|
-
cmd << key
|
285
|
-
cmd << "BY #{options[:by]}" if options[:by]
|
286
|
-
cmd << "GET #{[options[:get]].flatten * ' GET '}" if options[:get]
|
287
|
-
cmd << "#{options[:order]}" if options[:order]
|
288
|
-
cmd << "LIMIT #{options[:limit].join(' ')}" if options[:limit]
|
289
|
-
call_command(cmd)
|
290
|
-
end
|
291
|
-
|
292
|
-
def incr(key, increment = nil)
|
293
|
-
call_command(increment ? ["incrby",key,increment] : ["incr",key])
|
294
|
-
end
|
19
|
+
require 'redis/client'
|
20
|
+
require 'redis/pipeline'
|
295
21
|
|
296
|
-
|
297
|
-
|
298
|
-
end
|
299
|
-
|
300
|
-
# Similar to memcache.rb's #get_multi, returns a hash mapping
|
301
|
-
# keys to values.
|
302
|
-
def mapped_mget(*keys)
|
303
|
-
result = {}
|
304
|
-
mget(*keys).each do |value|
|
305
|
-
key = keys.shift
|
306
|
-
result.merge!(key => value) unless value.nil?
|
307
|
-
end
|
308
|
-
result
|
309
|
-
end
|
310
|
-
|
311
|
-
# Ruby defines a now deprecated type method so we need to override it here
|
312
|
-
# since it will never hit method_missing
|
313
|
-
def type(key)
|
314
|
-
call_command(['type', key])
|
315
|
-
end
|
316
|
-
|
317
|
-
def quit
|
318
|
-
call_command(['quit'])
|
319
|
-
rescue Errno::ECONNRESET
|
320
|
-
end
|
321
|
-
|
322
|
-
def pipelined(&block)
|
323
|
-
pipeline = Pipeline.new self
|
324
|
-
yield pipeline
|
325
|
-
pipeline.execute
|
326
|
-
end
|
327
|
-
|
328
|
-
def read_reply
|
329
|
-
# We read the first byte using read() mainly because gets() is
|
330
|
-
# immune to raw socket timeouts.
|
331
|
-
begin
|
332
|
-
rtype = @sock.read(1)
|
333
|
-
rescue Errno::EAGAIN
|
334
|
-
# We want to make sure it reconnects on the next command after the
|
335
|
-
# timeout. Otherwise the server may reply in the meantime leaving
|
336
|
-
# the protocol in a desync status.
|
337
|
-
@sock = nil
|
338
|
-
raise Errno::EAGAIN, "Timeout reading from the socket"
|
339
|
-
end
|
340
|
-
|
341
|
-
raise Errno::ECONNRESET,"Connection lost" if !rtype
|
342
|
-
line = @sock.gets
|
343
|
-
case rtype
|
344
|
-
when MINUS
|
345
|
-
raise MINUS + line.strip
|
346
|
-
when PLUS
|
347
|
-
line.strip
|
348
|
-
when COLON
|
349
|
-
line.to_i
|
350
|
-
when DOLLAR
|
351
|
-
bulklen = line.to_i
|
352
|
-
return nil if bulklen == -1
|
353
|
-
data = @sock.read(bulklen)
|
354
|
-
@sock.read(2) # CRLF
|
355
|
-
data
|
356
|
-
when ASTERISK
|
357
|
-
objects = line.to_i
|
358
|
-
return nil if bulklen == -1
|
359
|
-
res = []
|
360
|
-
objects.times {
|
361
|
-
res << read_reply
|
362
|
-
}
|
363
|
-
res
|
364
|
-
else
|
365
|
-
raise "Protocol error, got '#{rtype}' as initial reply byte"
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
private
|
370
|
-
def get_size(string)
|
371
|
-
string.respond_to?(:bytesize) ? string.bytesize : string.size
|
372
|
-
end
|
373
|
-
end
|
22
|
+
# For backwards compatibility
|
23
|
+
Redis = RedisRb::Client
|
data/spec/redis_spec.rb
CHANGED
@@ -430,6 +430,15 @@ describe "redis" do
|
|
430
430
|
@r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1]).should == ['louie', 'mutt']
|
431
431
|
@r.sort('dogs', :get => ['dog:*:name', 'dog:*:breed'], :limit => [0,1], :order => 'desc alpha').should == ['taj', 'terrier']
|
432
432
|
end
|
433
|
+
|
434
|
+
it "should be able to store a SORT result" do
|
435
|
+
@r.rpush 'colors', 'red'
|
436
|
+
@r.rpush 'colors', 'yellow'
|
437
|
+
@r.rpush 'colors', 'orange'
|
438
|
+
@r.rpush 'colors', 'green'
|
439
|
+
@r.sort('colors', :limit => [1,2], :order => 'desc alpha', :store => 'sorted.colors')
|
440
|
+
@r.lrange('sorted.colors', 0, -1).should == ['red', 'orange']
|
441
|
+
end
|
433
442
|
#
|
434
443
|
it "should be able count the members of a zset" do
|
435
444
|
@r.set_add "set", 'key1'
|
@@ -439,7 +448,7 @@ describe "redis" do
|
|
439
448
|
@r.delete('set')
|
440
449
|
@r.delete('zset')
|
441
450
|
end
|
442
|
-
#
|
451
|
+
#
|
443
452
|
it "should be able add members to a zset" do
|
444
453
|
@r.set_add "set", 'key1'
|
445
454
|
@r.set_add "set", 'key2'
|
@@ -449,7 +458,7 @@ describe "redis" do
|
|
449
458
|
@r.delete('set')
|
450
459
|
@r.delete('zset')
|
451
460
|
end
|
452
|
-
#
|
461
|
+
#
|
453
462
|
it "should be able delete members to a zset" do
|
454
463
|
@r.set_add "set", 'key1'
|
455
464
|
@r.set_add "set", 'key2'
|
@@ -467,7 +476,7 @@ describe "redis" do
|
|
467
476
|
@r.delete('set2')
|
468
477
|
@r.delete('zset')
|
469
478
|
end
|
470
|
-
#
|
479
|
+
#
|
471
480
|
it "should be able to get a range of values from a zset" do
|
472
481
|
@r.set_add "set", 'key1'
|
473
482
|
@r.set_add "set", 'key2'
|
@@ -487,7 +496,7 @@ describe "redis" do
|
|
487
496
|
@r.delete('set3')
|
488
497
|
@r.delete('zset')
|
489
498
|
end
|
490
|
-
#
|
499
|
+
#
|
491
500
|
it "should be able to get a reverse range of values from a zset" do
|
492
501
|
@r.set_add "set", 'key1'
|
493
502
|
@r.set_add "set", 'key2'
|
@@ -507,7 +516,7 @@ describe "redis" do
|
|
507
516
|
@r.delete('set3')
|
508
517
|
@r.delete('zset')
|
509
518
|
end
|
510
|
-
#
|
519
|
+
#
|
511
520
|
it "should be able to get a range by score of values from a zset" do
|
512
521
|
@r.set_add "set", 'key1'
|
513
522
|
@r.set_add "set", 'key2'
|
@@ -630,7 +639,7 @@ describe "redis" do
|
|
630
639
|
end
|
631
640
|
|
632
641
|
it "should handle multiple servers" do
|
633
|
-
require 'dist_redis'
|
642
|
+
require 'redis/dist_redis'
|
634
643
|
@r = DistRedis.new(:hosts=> ['localhost:6379', '127.0.0.1:6379'], :db => 15)
|
635
644
|
|
636
645
|
100.times do |idx|
|
@@ -640,6 +649,8 @@ describe "redis" do
|
|
640
649
|
100.times do |idx|
|
641
650
|
@r[idx].should == "foo#{idx}"
|
642
651
|
end
|
652
|
+
|
653
|
+
@r.keys('*').sort.first.should == "0"
|
643
654
|
end
|
644
655
|
|
645
656
|
it "should be able to pipeline writes" do
|
@@ -665,4 +676,77 @@ describe "redis" do
|
|
665
676
|
r.connect_to_server
|
666
677
|
end
|
667
678
|
|
679
|
+
it "should run MULTI without a block" do
|
680
|
+
@r.multi
|
681
|
+
@r.get("key1").should == "QUEUED"
|
682
|
+
@r.discard
|
683
|
+
end
|
684
|
+
|
685
|
+
it "should run MULTI/EXEC with a block" do
|
686
|
+
@r.multi do
|
687
|
+
@r.set "key1", "value1"
|
688
|
+
end
|
689
|
+
|
690
|
+
@r.get("key1").should == "value1"
|
691
|
+
|
692
|
+
begin
|
693
|
+
@r.multi do
|
694
|
+
@r.set "key2", "value2"
|
695
|
+
raise "Some error"
|
696
|
+
@r.set "key3", "value3"
|
697
|
+
end
|
698
|
+
rescue
|
699
|
+
end
|
700
|
+
|
701
|
+
@r.get("key2").should be_nil
|
702
|
+
@r.get("key3").should be_nil
|
703
|
+
end
|
704
|
+
|
705
|
+
it "should yield the Redis object when using #multi with a block" do
|
706
|
+
@r.multi do |multi|
|
707
|
+
multi.set "key1", "value1"
|
708
|
+
end
|
709
|
+
|
710
|
+
@r.get("key1").should == "value1"
|
711
|
+
end
|
712
|
+
|
713
|
+
it "can set and get hash values" do
|
714
|
+
@r.hset("rush", "signals", "1982").should be_true
|
715
|
+
@r.hexists("rush", "signals").should be_true
|
716
|
+
@r.hget("rush", "signals").should == "1982"
|
717
|
+
end
|
718
|
+
|
719
|
+
it "can delete hash values" do
|
720
|
+
@r.hset("rush", "YYZ", "1981")
|
721
|
+
@r.hdel("rush", "YYZ").should be_true
|
722
|
+
@r.hexists("rush", "YYZ").should be_false
|
723
|
+
end
|
724
|
+
|
725
|
+
describe "with some hash values" do
|
726
|
+
before(:each) do
|
727
|
+
@r.hset("rush", "permanent waves", "1980")
|
728
|
+
@r.hset("rush", "moving pictures", "1981")
|
729
|
+
@r.hset("rush", "signals", "1982")
|
730
|
+
end
|
731
|
+
|
732
|
+
it "can get the length of the hash" do
|
733
|
+
@r.hlen("rush").should == 3
|
734
|
+
@r.hlen("yyz").should be_zero
|
735
|
+
end
|
736
|
+
|
737
|
+
it "can get the keys and values of the hash" do
|
738
|
+
@r.hkeys("rush").should == ["permanent waves", "moving pictures", "signals"]
|
739
|
+
@r.hvals("rush").should == %w[1980 1981 1982]
|
740
|
+
@r.hvals("yyz").should be_empty
|
741
|
+
end
|
742
|
+
|
743
|
+
it "returns a hash for HGETALL" do
|
744
|
+
@r.hgetall("rush").should == {
|
745
|
+
"permanent waves" => "1980",
|
746
|
+
"moving pictures" => "1981",
|
747
|
+
"signals" => "1982"
|
748
|
+
}
|
749
|
+
@r.hgetall("yyz").should == {}
|
750
|
+
end
|
751
|
+
end
|
668
752
|
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Ezra Zygmuntowicz
|
@@ -14,19 +19,21 @@ autorequire: redis
|
|
14
19
|
bindir: bin
|
15
20
|
cert_chain: []
|
16
21
|
|
17
|
-
date: 2010-
|
22
|
+
date: 2010-03-23 00:00:00 -07:00
|
18
23
|
default_executable:
|
19
24
|
dependencies:
|
20
25
|
- !ruby/object:Gem::Dependency
|
21
26
|
name: rspec
|
22
|
-
|
23
|
-
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
prerelease: false
|
28
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
29
|
requirements:
|
26
30
|
- - ">="
|
27
31
|
- !ruby/object:Gem::Version
|
32
|
+
segments:
|
33
|
+
- 0
|
28
34
|
version: "0"
|
29
|
-
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
30
37
|
description: Ruby client library for redis key value storage server
|
31
38
|
email: ez@engineyard.com
|
32
39
|
executables: []
|
@@ -39,9 +46,11 @@ files:
|
|
39
46
|
- LICENSE
|
40
47
|
- README.markdown
|
41
48
|
- Rakefile
|
42
|
-
- lib/
|
43
|
-
- lib/
|
44
|
-
- lib/
|
49
|
+
- lib/edis.rb
|
50
|
+
- lib/redis/client.rb
|
51
|
+
- lib/redis/dist_redis.rb
|
52
|
+
- lib/redis/hash_ring.rb
|
53
|
+
- lib/redis/pipeline.rb
|
45
54
|
- lib/redis/raketasks.rb
|
46
55
|
- lib/redis.rb
|
47
56
|
- tasks/redis.tasks.rb
|
@@ -60,18 +69,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
69
|
requirements:
|
61
70
|
- - ">="
|
62
71
|
- !ruby/object:Gem::Version
|
72
|
+
segments:
|
73
|
+
- 0
|
63
74
|
version: "0"
|
64
|
-
version:
|
65
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
76
|
requirements:
|
67
77
|
- - ">="
|
68
78
|
- !ruby/object:Gem::Version
|
79
|
+
segments:
|
80
|
+
- 0
|
69
81
|
version: "0"
|
70
|
-
version:
|
71
82
|
requirements: []
|
72
83
|
|
73
84
|
rubyforge_project:
|
74
|
-
rubygems_version: 1.3.
|
85
|
+
rubygems_version: 1.3.6
|
75
86
|
signing_key:
|
76
87
|
specification_version: 3
|
77
88
|
summary: Ruby client library for redis key value storage server
|