redis 2.0.0.rc2 → 2.0.0.rc3
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 +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
|