redis 1.0.0 → 1.0.1

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