redis 2.0.0.rc2 → 2.0.0.rc3
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +84 -0
- data/lib/redis.rb +84 -27
- data/lib/redis/client.rb +20 -4
- data/lib/redis/compat.rb +21 -0
- data/lib/redis/distributed.rb +56 -14
- metadata +4 -3
data/Rakefile
CHANGED
@@ -73,3 +73,87 @@ task :gemspec do
|
|
73
73
|
file.puts spec.to_ruby
|
74
74
|
end
|
75
75
|
end
|
76
|
+
|
77
|
+
desc "Generate YARDoc"
|
78
|
+
task :yardoc do
|
79
|
+
require "yard"
|
80
|
+
|
81
|
+
opts = ["--title", "A Ruby client for Redis"]
|
82
|
+
|
83
|
+
YARD::CLI::Yardoc.run(*opts)
|
84
|
+
end
|
85
|
+
|
86
|
+
namespace :commands do
|
87
|
+
def redis_commands
|
88
|
+
$redis_commands ||= begin
|
89
|
+
require "nokogiri"
|
90
|
+
|
91
|
+
doc = Nokogiri::HTML(open("http://code.google.com/p/redis/wiki/CommandReference"))
|
92
|
+
|
93
|
+
commands = {}
|
94
|
+
|
95
|
+
doc.xpath("//ul/li").each do |node|
|
96
|
+
node.at_xpath("./a").text.split("/").each do |name|
|
97
|
+
if name =~ /^[A-Z]+$/
|
98
|
+
commands[name.downcase] = node.at_xpath("./tt").text
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
commands
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
task :doc do
|
108
|
+
source = File.read("lib/redis.rb")
|
109
|
+
|
110
|
+
redis_commands.each do |name, text|
|
111
|
+
source.sub!(/(?:^ *#.*\n)*^( *)def #{name}(\(|$)/) do
|
112
|
+
indent, extra_args = $1, $2
|
113
|
+
comment = "#{indent}# #{text.strip}"
|
114
|
+
|
115
|
+
IO.popen("par p#{2 + indent.size} 80", "r+") do |io|
|
116
|
+
io.puts comment
|
117
|
+
io.close_write
|
118
|
+
comment = io.read
|
119
|
+
end
|
120
|
+
|
121
|
+
"#{comment}#{indent}def #{name}#{extra_args}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
File.open("lib/redis.rb", "w") { |f| f.write(source) }
|
126
|
+
end
|
127
|
+
|
128
|
+
task :verify do
|
129
|
+
require "redis"
|
130
|
+
|
131
|
+
Dir["test/**/*_test.rb"].each { |f| require f }
|
132
|
+
|
133
|
+
log = StringIO.new
|
134
|
+
|
135
|
+
RedisTest::OPTIONS[:logger] = Logger.new(log)
|
136
|
+
|
137
|
+
redis = Redis.new
|
138
|
+
|
139
|
+
Test::Unit::AutoRunner.run
|
140
|
+
|
141
|
+
report = ["Command", "\033[0mDefined?\033[0m", "\033[0mTested?\033[0m"]
|
142
|
+
|
143
|
+
yes, no = "\033[1;32mYes\033[0m", "\033[1;31mNo\033[0m"
|
144
|
+
|
145
|
+
redis_commands.sort.each do |name, _|
|
146
|
+
defined, tested = redis.respond_to?(name), log.string[">> #{name.upcase}"]
|
147
|
+
|
148
|
+
next if defined && tested
|
149
|
+
|
150
|
+
report << name
|
151
|
+
report << (defined ? yes : no)
|
152
|
+
report << (tested ? yes : no)
|
153
|
+
end
|
154
|
+
|
155
|
+
IO.popen("rs 0 3", "w") do |io|
|
156
|
+
io.puts report.join("\n")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/redis.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'socket'
|
2
2
|
|
3
3
|
class Redis
|
4
|
-
VERSION = "2.0.0.
|
4
|
+
VERSION = "2.0.0.rc3"
|
5
5
|
|
6
6
|
class ProtocolError < RuntimeError
|
7
7
|
def initialize(reply_type)
|
@@ -29,7 +29,11 @@ class Redis
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def initialize(options = {})
|
32
|
-
|
32
|
+
if options[:thread_safe]
|
33
|
+
@client = Client::ThreadSafe.new(options)
|
34
|
+
else
|
35
|
+
@client = Client.new(options)
|
36
|
+
end
|
33
37
|
end
|
34
38
|
|
35
39
|
def select(db)
|
@@ -229,24 +233,37 @@ class Redis
|
|
229
233
|
@client.call(:zcard, key)
|
230
234
|
end
|
231
235
|
|
232
|
-
def zrange(key, start, stop,
|
233
|
-
|
234
|
-
|
235
|
-
else
|
236
|
-
@client.call(:zrange, key, start, stop)
|
236
|
+
def zrange(key, start, stop, options = {})
|
237
|
+
command = CommandOptions.new(options) do |c|
|
238
|
+
c.bool :with_scores
|
237
239
|
end
|
240
|
+
|
241
|
+
@client.call(:zrange, key, start, stop, *command.to_a)
|
238
242
|
end
|
239
243
|
|
240
|
-
def zrangebyscore(key, min, max)
|
241
|
-
|
244
|
+
def zrangebyscore(key, min, max, options = {})
|
245
|
+
command = CommandOptions.new(options) do |c|
|
246
|
+
c.splat :limit
|
247
|
+
c.bool :with_scores
|
248
|
+
end
|
249
|
+
|
250
|
+
@client.call(:zrangebyscore, key, min, max, *command.to_a)
|
242
251
|
end
|
243
252
|
|
244
|
-
def zrevrange(key, start, stop,
|
245
|
-
|
246
|
-
|
247
|
-
else
|
248
|
-
@client.call(:zrevrange, key, start, stop)
|
253
|
+
def zrevrange(key, start, stop, options = {})
|
254
|
+
command = CommandOptions.new(options) do |c|
|
255
|
+
c.bool :with_scores
|
249
256
|
end
|
257
|
+
|
258
|
+
@client.call(:zrevrange, key, start, stop, *command.to_a)
|
259
|
+
end
|
260
|
+
|
261
|
+
def zremrangebyscore(key, min, max)
|
262
|
+
@client.call(:zremrangebyscore, key, min, max)
|
263
|
+
end
|
264
|
+
|
265
|
+
def zremrangebyrank(key, start, stop)
|
266
|
+
@client.call(:zremrangebyscore, key, start, stop)
|
250
267
|
end
|
251
268
|
|
252
269
|
def zscore(key, member)
|
@@ -257,6 +274,14 @@ class Redis
|
|
257
274
|
_bool @client.call(:zrem, key, member)
|
258
275
|
end
|
259
276
|
|
277
|
+
def zinter(destination, keys)
|
278
|
+
@client.call(:zinter, destination, keys.size, *keys)
|
279
|
+
end
|
280
|
+
|
281
|
+
def zunion(destination, keys)
|
282
|
+
@client.call(:zunion, destination, keys.size, *keys)
|
283
|
+
end
|
284
|
+
|
260
285
|
def move(key, db)
|
261
286
|
_bool @client.call(:move, key, db)
|
262
287
|
end
|
@@ -359,20 +384,15 @@ class Redis
|
|
359
384
|
end
|
360
385
|
|
361
386
|
def sort(key, options = {})
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
end if options[:get]
|
370
|
-
|
371
|
-
cmd += options[:order].split(" ") if options[:order]
|
372
|
-
cmd += ["LIMIT", *options[:limit]] if options[:limit]
|
373
|
-
cmd += ["STORE", options[:store]] if options[:store]
|
387
|
+
command = CommandOptions.new(options) do |c|
|
388
|
+
c.value :by
|
389
|
+
c.splat :limit
|
390
|
+
c.multi :get
|
391
|
+
c.words :order
|
392
|
+
c.value :store
|
393
|
+
end
|
374
394
|
|
375
|
-
@client.call(*
|
395
|
+
@client.call(:sort, key, *command.to_a)
|
376
396
|
end
|
377
397
|
|
378
398
|
def incr(key)
|
@@ -460,6 +480,42 @@ class Redis
|
|
460
480
|
@client.call(command, *args)
|
461
481
|
end
|
462
482
|
|
483
|
+
class CommandOptions
|
484
|
+
def initialize(options)
|
485
|
+
@result = []
|
486
|
+
@options = options
|
487
|
+
yield(self)
|
488
|
+
end
|
489
|
+
|
490
|
+
def bool(name)
|
491
|
+
insert(name) { |argument, value| [argument] }
|
492
|
+
end
|
493
|
+
|
494
|
+
def value(name)
|
495
|
+
insert(name) { |argument, value| [argument, value] }
|
496
|
+
end
|
497
|
+
|
498
|
+
def splat(name)
|
499
|
+
insert(name) { |argument, value| [argument, *value] }
|
500
|
+
end
|
501
|
+
|
502
|
+
def multi(name)
|
503
|
+
insert(name) { |argument, value| [argument].product(Array(value)).flatten }
|
504
|
+
end
|
505
|
+
|
506
|
+
def words(name)
|
507
|
+
insert(name) { |argument, value| value.split(" ") }
|
508
|
+
end
|
509
|
+
|
510
|
+
def to_a
|
511
|
+
@result
|
512
|
+
end
|
513
|
+
|
514
|
+
def insert(name)
|
515
|
+
@result += yield(name.to_s.upcase.gsub("_", ""), @options[name]) if @options[name]
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
463
519
|
private
|
464
520
|
|
465
521
|
def _bool(value)
|
@@ -494,3 +550,4 @@ end
|
|
494
550
|
require 'redis/client'
|
495
551
|
require 'redis/pipeline'
|
496
552
|
require 'redis/subscribe'
|
553
|
+
require 'redis/compat'
|
data/lib/redis/client.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "thread"
|
2
|
-
|
3
1
|
class Redis
|
4
2
|
class Client
|
5
3
|
MINUS = "-".freeze
|
@@ -17,8 +15,7 @@ class Redis
|
|
17
15
|
@db = (options[:db] || 0).to_i
|
18
16
|
@timeout = (options[:timeout] || 5).to_i
|
19
17
|
@password = options[:password]
|
20
|
-
@logger
|
21
|
-
@mutex = ::Mutex.new
|
18
|
+
@logger = options[:logger]
|
22
19
|
@sock = nil
|
23
20
|
end
|
24
21
|
|
@@ -261,5 +258,24 @@ class Redis
|
|
261
258
|
end
|
262
259
|
end
|
263
260
|
end
|
261
|
+
|
262
|
+
class ThreadSafe < self
|
263
|
+
def initialize(*args)
|
264
|
+
require "monitor"
|
265
|
+
|
266
|
+
super(*args)
|
267
|
+
@mutex = ::Monitor.new
|
268
|
+
end
|
269
|
+
|
270
|
+
def synchronize(&block)
|
271
|
+
@mutex.synchronize(&block)
|
272
|
+
end
|
273
|
+
|
274
|
+
def ensure_connected(&block)
|
275
|
+
super do
|
276
|
+
synchronize(&block)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
264
280
|
end
|
265
281
|
end
|
data/lib/redis/compat.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file contains core methods that are present in
|
2
|
+
# Ruby 1.9 and not in earlier versions.
|
3
|
+
|
4
|
+
unless [].respond_to?(:product)
|
5
|
+
class Array
|
6
|
+
def product(*enums)
|
7
|
+
enums.unshift self
|
8
|
+
result = [[]]
|
9
|
+
while [] != enums
|
10
|
+
t, result = result, []
|
11
|
+
b, *enums = enums
|
12
|
+
t.each do |a|
|
13
|
+
b.each do |n|
|
14
|
+
result << a + [n]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
result
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/redis/distributed.rb
CHANGED
@@ -16,9 +16,10 @@ class Redis
|
|
16
16
|
attr_reader :ring
|
17
17
|
|
18
18
|
def initialize(urls, options = {})
|
19
|
-
@tag = options.delete(:tag) ||
|
19
|
+
@tag = options.delete(:tag) || /^\{(.+?)\}/
|
20
20
|
@default_options = options
|
21
21
|
@ring = HashRing.new urls.map { |url| Redis.connect(options.merge(:url => url)) }
|
22
|
+
@subscribed_node = nil
|
22
23
|
end
|
23
24
|
|
24
25
|
def node_for(key)
|
@@ -240,7 +241,7 @@ class Redis
|
|
240
241
|
end
|
241
242
|
|
242
243
|
def smove(source, destination, member)
|
243
|
-
ensure_same_node(:smove, source, destination
|
244
|
+
ensure_same_node(:smove, source, destination) do |node|
|
244
245
|
node.smove(source, destination, member)
|
245
246
|
end
|
246
247
|
end
|
@@ -309,16 +310,32 @@ class Redis
|
|
309
310
|
node_for(key).zincrby(key, increment, member)
|
310
311
|
end
|
311
312
|
|
312
|
-
def zrange(key, start, stop,
|
313
|
-
node_for(key).zrange(key, start, stop,
|
313
|
+
def zrange(key, start, stop, options = {})
|
314
|
+
node_for(key).zrange(key, start, stop, options)
|
314
315
|
end
|
315
316
|
|
316
|
-
def
|
317
|
-
node_for(key).
|
317
|
+
def zrank(key, member)
|
318
|
+
node_for(key).zrank(key, member)
|
318
319
|
end
|
319
320
|
|
320
|
-
def
|
321
|
-
node_for(key).
|
321
|
+
def zrevrank(key, member)
|
322
|
+
node_for(key).zrevrank(key, member)
|
323
|
+
end
|
324
|
+
|
325
|
+
def zrevrange(key, start, stop, options = {})
|
326
|
+
node_for(key).zrevrange(key, start, stop, options)
|
327
|
+
end
|
328
|
+
|
329
|
+
def zremrangebyscore(key, min, max)
|
330
|
+
node_for(key).zremrangebyscore(key, min, max)
|
331
|
+
end
|
332
|
+
|
333
|
+
def zremrangebyrank(key, start, stop)
|
334
|
+
node_for(key).zremrangebyrank(key, start, stop)
|
335
|
+
end
|
336
|
+
|
337
|
+
def zrangebyscore(key, min, max, options = {})
|
338
|
+
node_for(key).zrangebyscore(key, min, max, options)
|
322
339
|
end
|
323
340
|
|
324
341
|
def zcard(key)
|
@@ -329,6 +346,18 @@ class Redis
|
|
329
346
|
node_for(key).zscore(key, member)
|
330
347
|
end
|
331
348
|
|
349
|
+
def zinter(destination, keys)
|
350
|
+
ensure_same_node(:zinter, destination, *keys) do |node|
|
351
|
+
node.zinter(destination, keys)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def zunion(destination, keys)
|
356
|
+
ensure_same_node(:zunion, destination, *keys) do |node|
|
357
|
+
node.zunion(destination, keys)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
332
361
|
def hset(key, field, value)
|
333
362
|
node_for(key).hset(key, field, value)
|
334
363
|
end
|
@@ -386,15 +415,28 @@ class Redis
|
|
386
415
|
end
|
387
416
|
|
388
417
|
def publish(channel, message)
|
389
|
-
|
418
|
+
node_for(channel).publish(channel, message)
|
390
419
|
end
|
391
420
|
|
392
|
-
def
|
393
|
-
|
421
|
+
def subscribed?
|
422
|
+
!! @subscribed_node
|
394
423
|
end
|
395
424
|
|
396
|
-
def
|
397
|
-
raise
|
425
|
+
def unsubscribe(*channels)
|
426
|
+
raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
|
427
|
+
@subscribed_node.unsubscribe(*channels)
|
428
|
+
end
|
429
|
+
|
430
|
+
def subscribe(channel, *channels, &block)
|
431
|
+
if channels.empty?
|
432
|
+
@subscribed_node = node_for(channel)
|
433
|
+
@subscribed_node.subscribe(channel, &block)
|
434
|
+
else
|
435
|
+
ensure_same_node(:subscribe, channel, *channels) do |node|
|
436
|
+
@subscribed_node = node
|
437
|
+
node.subscribe(channel, *channels, &block)
|
438
|
+
end
|
439
|
+
end
|
398
440
|
end
|
399
441
|
|
400
442
|
def punsubscribe(*channels)
|
@@ -452,7 +494,7 @@ class Redis
|
|
452
494
|
def ensure_same_node(command, *keys)
|
453
495
|
tags = keys.map { |key| key_tag(key) }
|
454
496
|
|
455
|
-
raise CannotDistribute, command if tags.
|
497
|
+
raise CannotDistribute, command if !tags.all? || tags.uniq.size != 1
|
456
498
|
|
457
499
|
yield(node_for(keys.first))
|
458
500
|
end
|
metadata
CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
|
|
6
6
|
- 2
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 2.0.0.
|
9
|
+
- rc3
|
10
|
+
version: 2.0.0.rc3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ezra Zygmuntowicz
|
@@ -20,7 +20,7 @@ autorequire: redis
|
|
20
20
|
bindir: bin
|
21
21
|
cert_chain: []
|
22
22
|
|
23
|
-
date: 2010-
|
23
|
+
date: 2010-05-06 00:00:00 -03:00
|
24
24
|
default_executable:
|
25
25
|
dependencies: []
|
26
26
|
|
@@ -38,6 +38,7 @@ files:
|
|
38
38
|
- Rakefile
|
39
39
|
- lib/edis.rb
|
40
40
|
- lib/redis/client.rb
|
41
|
+
- lib/redis/compat.rb
|
41
42
|
- lib/redis/distributed.rb
|
42
43
|
- lib/redis/hash_ring.rb
|
43
44
|
- lib/redis/pipeline.rb
|