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 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
@@ -1,7 +1,7 @@
1
1
  require 'socket'
2
2
 
3
3
  class Redis
4
- VERSION = "2.0.0.rc2"
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
- @client = Client.new(options)
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, with_scores = false)
233
- if with_scores
234
- @client.call(:zrange, key, start, stop, "WITHSCORES")
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
- @client.call(:zrangebyscore, key, min, max)
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, with_scores = false)
245
- if with_scores
246
- @client.call(:zrevrange, key, start, stop, "WITHSCORES")
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
- cmd = []
363
- cmd << "SORT"
364
- cmd << key
365
- cmd += ["BY", options[:by]] if options[:by]
366
-
367
- Array(options[:get]).each do |k|
368
- cmd += ["GET", k]
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(*cmd)
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'
@@ -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 = options[: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
@@ -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
@@ -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, member) do |node|
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, with_scores = false)
313
- node_for(key).zrange(key, start, stop, with_scores)
313
+ def zrange(key, start, stop, options = {})
314
+ node_for(key).zrange(key, start, stop, options)
314
315
  end
315
316
 
316
- def zrevrange(key, start, stop, with_scores = false)
317
- node_for(key).zrevrange(key, start, stop, with_scores)
317
+ def zrank(key, member)
318
+ node_for(key).zrank(key, member)
318
319
  end
319
320
 
320
- def zrangebyscore(key, min, max)
321
- node_for(key).zrangebyscore(key, min, max)
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
- raise NotImplementedError
418
+ node_for(channel).publish(channel, message)
390
419
  end
391
420
 
392
- def unsubscribe(*channels)
393
- raise NotImplementedError
421
+ def subscribed?
422
+ !! @subscribed_node
394
423
  end
395
424
 
396
- def subscribe(*channels, &block)
397
- raise NotImplementedError
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.compact.uniq.size != 1
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
- - rc2
10
- version: 2.0.0.rc2
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-04-28 00:00:00 -03:00
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