redis 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/redis.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'socket'
2
2
 
3
3
  class Redis
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.1"
5
5
 
6
6
  def self.new(*attrs)
7
7
  Client.new(*attrs)
@@ -22,3 +22,4 @@ end
22
22
 
23
23
  require 'redis/client'
24
24
  require 'redis/pipeline'
25
+ require 'redis/subscribe'
data/lib/redis/client.rb CHANGED
@@ -124,6 +124,11 @@ class Redis
124
124
  "sync" => true
125
125
  }
126
126
 
127
+ BLOCKING_COMMANDS = {
128
+ "blpop" => true,
129
+ "brpop" => true
130
+ }
131
+
127
132
  def initialize(options = {})
128
133
  @host = options[:host] || '127.0.0.1'
129
134
  @port = (options[:port] || 6379).to_i
@@ -265,29 +270,39 @@ class Redis
265
270
  end
266
271
 
267
272
  def subscribe(*classes)
268
- # Sanity check, as our API is a bit tricky. You MUST call
269
- # the top-level subscribe with a block, but you can NOT call
270
- # the nested subscribe calls with a block, as all the messages
271
- # are processed by the top level call.
272
- if @pubsub == false and !block_given?
273
- raise "You must pass a block to the top level subscribe call"
274
- elsif @pubsub == true and block_given?
275
- raise "Can't pass a block to nested subscribe calls"
276
- elsif @pubsub == true
277
- # This is a nested subscribe call without a block given.
278
- # We just need to send the subscribe command and return asap.
273
+ # Top-level `subscribe` MUST be called with a block,
274
+ # nested `subscribe` MUST NOT be called with a block
275
+ if !@pubsub && !block_given?
276
+ raise "Top-level subscribe requires a block"
277
+ elsif @pubsub == true && block_given?
278
+ raise "Nested subscribe does not take a block"
279
+ elsif @pubsub
280
+ # If we're already pubsub'ing, just subscribe us to some more classes
279
281
  call_command [:subscribe,*classes]
280
282
  return true
281
283
  end
284
+
282
285
  @pubsub = true
283
286
  call_command [:subscribe,*classes]
284
- while true
285
- r = read_reply
286
- msg = {:type => r[0], :class => r[1], :data => r[2]}
287
- yield(msg)
288
- break if msg[:type] == "unsubscribe" and r[2] == 0
287
+ sub = Subscription.new
288
+ yield(sub)
289
+ begin
290
+ while true
291
+ type, *reply = read_reply # type, [class,data]
292
+ case type
293
+ when 'subscribe','unsubscribe'
294
+ sub.send(type) && sub.send(type).call(reply[0])
295
+ when 'message'
296
+ sub.send(type) && sub.send(type).call(reply[0],reply[1])
297
+ end
298
+ break if type == 'unsubscribe' && reply[1] == 0
299
+ end
300
+ rescue RuntimeError
301
+ call_command [:unsubscribe]
302
+ raise
303
+ ensure
304
+ @pubsub = false
289
305
  end
290
- @pubsub = false
291
306
  end
292
307
 
293
308
  def unsubscribe(*classes)
@@ -295,8 +310,6 @@ class Redis
295
310
  return true
296
311
  end
297
312
 
298
- protected
299
-
300
313
  def call_command(argv)
301
314
  log(argv.inspect, :debug)
302
315
 
@@ -339,16 +352,7 @@ class Redis
339
352
  # to make sure a blocking read will return after the specified number
340
353
  # of seconds. This hack is from memcached ruby client.
341
354
  if timeout
342
- secs = Integer(timeout)
343
- usecs = Integer((timeout - secs) * 1_000_000)
344
- optval = [secs, usecs].pack("l_2")
345
- begin
346
- sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
347
- sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
348
- rescue Exception => ex
349
- # Solaris, for one, does not like/support socket timeouts.
350
- log("Unable to use raw socket timeouts: #{ex.class.name}: #{ex.message}")
351
- end
355
+ set_socket_timeout(sock, timeout)
352
356
  end
353
357
  sock
354
358
  end
@@ -406,7 +410,15 @@ class Redis
406
410
  return true
407
411
  end
408
412
  # The normal command execution is reading and processing the reply.
409
- results = maybe_lock { process_command(command, argvv) }
413
+ results = maybe_lock do
414
+ begin
415
+ set_socket_timeout(@sock, 0) if requires_timeout_reset?(argvv[0][0].to_s)
416
+ process_command(command, argvv)
417
+ ensure
418
+ set_socket_timeout(@sock, @timeout) if requires_timeout_reset?(argvv[0][0].to_s)
419
+ end
420
+ end
421
+
410
422
  return pipeline ? results : results[0]
411
423
  end
412
424
 
@@ -478,5 +490,23 @@ class Redis
478
490
  def deprecated(old, new, trace = caller[0])
479
491
  $stderr.puts "\nRedis: The method #{old} is deprecated. Use #{new} instead (in #{trace})"
480
492
  end
493
+
494
+ def requires_timeout_reset?(command)
495
+ BLOCKING_COMMANDS[command] && @timeout
496
+ end
497
+
498
+ def set_socket_timeout(sock, timeout)
499
+ secs = Integer(timeout)
500
+ usecs = Integer((timeout - secs) * 1_000_000)
501
+ optval = [secs, usecs].pack("l_2")
502
+ begin
503
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
504
+ sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
505
+ rescue Exception => ex
506
+ # Solaris, for one, does not like/support socket timeouts.
507
+ log("Unable to use raw socket timeouts: #{ex.class.name}: #{ex.message}")
508
+ end
509
+ end
510
+
481
511
  end
482
512
  end
@@ -0,0 +1,14 @@
1
+ class Subscription
2
+ def subscribe(&block)
3
+ if block_given? then @subscribe = block else @subscribe end
4
+ end
5
+
6
+ def unsubscribe(&block)
7
+ if block_given? then @unsubscribe = block else @unsubscribe end
8
+ end
9
+
10
+ def message(&block)
11
+ if block_given? then @message = block else @message end
12
+ end
13
+
14
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 0
8
- - 0
9
- version: 1.0.0
8
+ - 1
9
+ version: 1.0.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ezra Zygmuntowicz
@@ -52,6 +52,7 @@ files:
52
52
  - lib/redis/hash_ring.rb
53
53
  - lib/redis/pipeline.rb
54
54
  - lib/redis/raketasks.rb
55
+ - lib/redis/subscribe.rb
55
56
  - lib/redis.rb
56
57
  - tasks/redis.tasks.rb
57
58
  has_rdoc: true