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 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