redis 3.0.0.rc1 → 3.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.travis.yml +50 -0
  2. data/.travis/Gemfile +11 -0
  3. data/CHANGELOG.md +47 -19
  4. data/README.md +160 -149
  5. data/Rakefile +15 -50
  6. data/examples/pubsub.rb +1 -1
  7. data/examples/unicorn/config.ru +1 -1
  8. data/examples/unicorn/unicorn.rb +1 -1
  9. data/lib/redis.rb +790 -390
  10. data/lib/redis/client.rb +137 -49
  11. data/lib/redis/connection/hiredis.rb +26 -15
  12. data/lib/redis/connection/ruby.rb +170 -53
  13. data/lib/redis/connection/synchrony.rb +23 -35
  14. data/lib/redis/distributed.rb +92 -32
  15. data/lib/redis/errors.rb +4 -2
  16. data/lib/redis/pipeline.rb +17 -6
  17. data/lib/redis/version.rb +1 -1
  18. data/redis.gemspec +4 -6
  19. data/test/blocking_commands_test.rb +42 -0
  20. data/test/command_map_test.rb +18 -17
  21. data/test/commands_on_hashes_test.rb +13 -12
  22. data/test/commands_on_lists_test.rb +35 -45
  23. data/test/commands_on_sets_test.rb +55 -54
  24. data/test/commands_on_sorted_sets_test.rb +106 -105
  25. data/test/commands_on_strings_test.rb +64 -55
  26. data/test/commands_on_value_types_test.rb +66 -54
  27. data/test/connection_handling_test.rb +136 -151
  28. data/test/distributed_blocking_commands_test.rb +33 -40
  29. data/test/distributed_commands_on_hashes_test.rb +6 -7
  30. data/test/distributed_commands_on_lists_test.rb +13 -14
  31. data/test/distributed_commands_on_sets_test.rb +57 -58
  32. data/test/distributed_commands_on_sorted_sets_test.rb +11 -12
  33. data/test/distributed_commands_on_strings_test.rb +31 -32
  34. data/test/distributed_commands_on_value_types_test.rb +61 -46
  35. data/test/distributed_commands_requiring_clustering_test.rb +108 -108
  36. data/test/distributed_connection_handling_test.rb +14 -15
  37. data/test/distributed_internals_test.rb +7 -19
  38. data/test/distributed_key_tags_test.rb +36 -36
  39. data/test/distributed_persistence_control_commands_test.rb +17 -14
  40. data/test/distributed_publish_subscribe_test.rb +61 -69
  41. data/test/distributed_remote_server_control_commands_test.rb +39 -28
  42. data/test/distributed_sorting_test.rb +12 -13
  43. data/test/distributed_test.rb +40 -41
  44. data/test/distributed_transactions_test.rb +20 -21
  45. data/test/encoding_test.rb +12 -9
  46. data/test/error_replies_test.rb +42 -36
  47. data/test/helper.rb +118 -85
  48. data/test/helper_test.rb +20 -6
  49. data/test/internals_test.rb +167 -103
  50. data/test/lint/blocking_commands.rb +124 -0
  51. data/test/lint/hashes.rb +115 -93
  52. data/test/lint/lists.rb +86 -80
  53. data/test/lint/sets.rb +68 -62
  54. data/test/lint/sorted_sets.rb +200 -195
  55. data/test/lint/strings.rb +112 -94
  56. data/test/lint/value_types.rb +76 -55
  57. data/test/persistence_control_commands_test.rb +17 -12
  58. data/test/pipelining_commands_test.rb +135 -126
  59. data/test/publish_subscribe_test.rb +105 -110
  60. data/test/remote_server_control_commands_test.rb +74 -58
  61. data/test/sorting_test.rb +31 -29
  62. data/test/support/connection/hiredis.rb +1 -0
  63. data/test/support/connection/ruby.rb +1 -0
  64. data/test/support/connection/synchrony.rb +17 -0
  65. data/test/{redis_mock.rb → support/redis_mock.rb} +24 -21
  66. data/test/support/wire/synchrony.rb +24 -0
  67. data/test/support/wire/thread.rb +5 -0
  68. data/test/synchrony_driver.rb +9 -9
  69. data/test/test.conf +1 -1
  70. data/test/thread_safety_test.rb +21 -19
  71. data/test/transactions_test.rb +189 -118
  72. data/test/unknown_commands_test.rb +9 -8
  73. data/test/url_param_test.rb +46 -41
  74. metadata +28 -43
  75. data/TODO.md +0 -4
  76. data/benchmarking/thread_safety.rb +0 -38
  77. data/test/lint/internals.rb +0 -36
data/Rakefile CHANGED
@@ -24,7 +24,17 @@ task :start do
24
24
  false
25
25
  end
26
26
 
27
- system "redis-server #{REDIS_CNF}" unless redis_running
27
+ unless redis_running
28
+ unless system("which redis-server")
29
+ STDERR.puts "redis-server not in PATH"
30
+ exit 1
31
+ end
32
+
33
+ unless system("redis-server #{REDIS_CNF}")
34
+ STDERR.puts "could not start redis-server"
35
+ exit 1
36
+ end
37
+ end
28
38
  end
29
39
 
30
40
  desc "Stop the Redis server"
@@ -35,55 +45,10 @@ task :stop do
35
45
  end
36
46
  end
37
47
 
38
- desc "Run the test suite"
39
- task :test => ["test:ruby", "test:hiredis", "test:synchrony"]
40
-
41
- namespace :test do
42
- desc "Run tests against the Ruby driver"
43
- task :ruby do
44
- require "cutest"
45
-
46
- Cutest.run(Dir["./test/**/*_test.rb"])
47
- end
48
-
49
- desc "Run tests against the hiredis driver"
50
- task :hiredis do
51
- require "cutest"
52
-
53
- begin
54
- require "redis/connection/hiredis"
55
-
56
- puts
57
- puts "Running tests against hiredis v#{Hiredis::VERSION}"
58
-
59
- ENV["REDIS_CONNECTION_DRIVER"] = "hiredis"
60
- Cutest.run(Dir["./test/**/*_test.rb"])
61
- rescue LoadError
62
- puts "Skipping tests against hiredis"
63
- end
64
- end
65
-
66
- desc "Run tests against the em-synchrony driver"
67
- task :synchrony do
68
- require "cutest"
69
-
70
- # Synchrony needs 1.9
71
- next if RUBY_VERSION < "1.9"
72
-
73
- begin
74
- require "redis/connection/synchrony"
75
-
76
- puts
77
- puts "Running tests against em-synchrony"
78
-
79
- threaded_tests = ['./test/thread_safety_test.rb']
80
-
81
- ENV["REDIS_CONNECTION_DRIVER"] = "synchrony"
82
- Cutest.run(Dir['./test/**/*_test.rb'] - threaded_tests)
83
- rescue LoadError
84
- puts "Skipping tests against em-synchrony"
85
- end
86
- end
48
+ Rake::TestTask.new do |t|
49
+ t.options = "-v"
50
+ t.libs << "test"
51
+ t.test_files = FileList["test/*_test.rb"]
87
52
  end
88
53
 
89
54
  task :doc => ["doc:generate", "doc:prepare"]
@@ -11,7 +11,7 @@ Finally force the example to exit sending the 'exit' message with:
11
11
 
12
12
  EOS
13
13
 
14
- redis = Redis.connect
14
+ redis = Redis.new
15
15
 
16
16
  trap(:INT) { puts; exit }
17
17
 
@@ -1,3 +1,3 @@
1
1
  run lambda { |env|
2
- [200, {"Content-Type" => "text/plain"}, [$redis.randomkey]]
2
+ [200, {"Content-Type" => "text/plain"}, [Redis.current.randomkey]]
3
3
  }
@@ -16,5 +16,5 @@ worker_processes 3
16
16
  # worker processes.
17
17
 
18
18
  after_fork do |server, worker|
19
- $redis = Redis.connect
19
+ Redis.current.quit
20
20
  end
@@ -9,33 +9,19 @@ class Redis
9
9
 
10
10
  attr :client
11
11
 
12
+ # @deprecated The preferred way to create a new client object is using `#new`.
13
+ # This method does not actually establish a connection to Redis,
14
+ # in contrary to what you might expect.
12
15
  def self.connect(options = {})
13
- options = options.dup
14
-
15
- url = options.delete(:url) || ENV["REDIS_URL"]
16
- if url
17
- require "uri"
18
-
19
- uri = URI(url)
20
-
21
- # Require the URL to have at least a host
22
- raise ArgumentError, "invalid url" unless uri.host
23
-
24
- options[:host] ||= uri.host
25
- options[:port] ||= uri.port
26
- options[:password] ||= uri.password
27
- options[:db] ||= uri.path[1..-1].to_i
28
- end
29
-
30
16
  new(options)
31
17
  end
32
18
 
33
19
  def self.current
34
- Thread.current[:redis] ||= Redis.connect
20
+ @current ||= Redis.new
35
21
  end
36
22
 
37
23
  def self.current=(redis)
38
- Thread.current[:redis] = redis
24
+ @current = redis
39
25
  end
40
26
 
41
27
  include MonitorMixin
@@ -43,22 +29,17 @@ class Redis
43
29
  def initialize(options = {})
44
30
  @client = Client.new(options)
45
31
 
46
- if options[:thread_safe] == false
47
- @synchronizer = lambda { |&block| block.call }
48
- else
49
- @synchronizer = lambda { |&block| mon_synchronize { block.call } }
50
- super() # Monitor#initialize
51
- end
32
+ super() # Monitor#initialize
52
33
  end
53
34
 
54
35
  def synchronize
55
- @synchronizer.call { yield }
36
+ mon_synchronize { yield(@client) }
56
37
  end
57
38
 
58
39
  # Run code without the client reconnecting
59
40
  def without_reconnect(&block)
60
- synchronize do
61
- @client.without_reconnect(&block)
41
+ synchronize do |client|
42
+ client.without_reconnect(&block)
62
43
  end
63
44
  end
64
45
 
@@ -68,8 +49,8 @@ class Redis
68
49
  # `requirepass` directive in the configuration file
69
50
  # @return [String] `OK`
70
51
  def auth(password)
71
- synchronize do
72
- @client.call [:auth, password]
52
+ synchronize do |client|
53
+ client.call [:auth, password]
73
54
  end
74
55
  end
75
56
 
@@ -78,9 +59,9 @@ class Redis
78
59
  # @param [Fixnum] db zero-based index of the DB to use (0 to 15)
79
60
  # @return [String] `OK`
80
61
  def select(db)
81
- synchronize do
82
- @client.db = db
83
- @client.call [:select, db]
62
+ synchronize do |client|
63
+ client.db = db
64
+ client.call [:select, db]
84
65
  end
85
66
  end
86
67
 
@@ -89,10 +70,12 @@ class Redis
89
70
  # @param [String, Symbol] cmd e.g. "commandstats"
90
71
  # @return [Hash<String, String>]
91
72
  def info(cmd = nil)
92
- synchronize do
93
- @client.call [:info, cmd].compact do |reply|
73
+ synchronize do |client|
74
+ client.call [:info, cmd].compact do |reply|
94
75
  if reply.kind_of?(String)
95
- reply = Hash[*reply.split(/:|\r\n/).grep(/^[^#]/)]
76
+ reply = Hash[reply.split("\r\n").map do |line|
77
+ line.split(":", 2) unless line =~ /^(#|$)/
78
+ end]
96
79
 
97
80
  if cmd && cmd.to_s == "commandstats"
98
81
  # Extract nested hashes for INFO COMMANDSTATS
@@ -113,8 +96,8 @@ class Redis
113
96
  # @return [String, Hash] string reply, or hash when retrieving more than one
114
97
  # property with `CONFIG GET`
115
98
  def config(action, *args)
116
- synchronize do
117
- @client.call [:config, action, *args] do |reply|
99
+ synchronize do |client|
100
+ client.call [:config, action, *args] do |reply|
118
101
  if reply.kind_of?(Array) && action == :get
119
102
  Hash[*reply]
120
103
  else
@@ -128,8 +111,8 @@ class Redis
128
111
  #
129
112
  # @return [String] `OK`
130
113
  def flushdb
131
- synchronize do
132
- @client.call [:flushdb]
114
+ synchronize do |client|
115
+ client.call [:flushdb]
133
116
  end
134
117
  end
135
118
 
@@ -137,8 +120,8 @@ class Redis
137
120
  #
138
121
  # @return [String] `OK`
139
122
  def flushall
140
- synchronize do
141
- @client.call [:flushall]
123
+ synchronize do |client|
124
+ client.call [:flushall]
142
125
  end
143
126
  end
144
127
 
@@ -146,8 +129,8 @@ class Redis
146
129
  #
147
130
  # @return [String]
148
131
  def save
149
- synchronize do
150
- @client.call [:save]
132
+ synchronize do |client|
133
+ client.call [:save]
151
134
  end
152
135
  end
153
136
 
@@ -155,8 +138,8 @@ class Redis
155
138
  #
156
139
  # @return [String] `OK`
157
140
  def bgsave
158
- synchronize do
159
- @client.call [:bgsave]
141
+ synchronize do |client|
142
+ client.call [:bgsave]
160
143
  end
161
144
  end
162
145
 
@@ -164,8 +147,8 @@ class Redis
164
147
  #
165
148
  # @return [String] `OK`
166
149
  def bgrewriteaof
167
- synchronize do
168
- @client.call [:bgrewriteaof]
150
+ synchronize do |client|
151
+ client.call [:bgrewriteaof]
169
152
  end
170
153
  end
171
154
 
@@ -174,8 +157,8 @@ class Redis
174
157
  # @param [String] key
175
158
  # @return [String]
176
159
  def get(key)
177
- synchronize do
178
- @client.call [:get, key]
160
+ synchronize do |client|
161
+ client.call [:get, key]
179
162
  end
180
163
  end
181
164
 
@@ -187,8 +170,8 @@ class Redis
187
170
  # @param [Fixnum] offset bit offset
188
171
  # @return [Fixnum] `0` or `1`
189
172
  def getbit(key, offset)
190
- synchronize do
191
- @client.call [:getbit, key, offset]
173
+ synchronize do |client|
174
+ client.call [:getbit, key, offset]
192
175
  end
193
176
  end
194
177
 
@@ -200,8 +183,8 @@ class Redis
200
183
  # the end of the string
201
184
  # @return [Fixnum] `0` or `1`
202
185
  def getrange(key, start, stop)
203
- synchronize do
204
- @client.call [:getrange, key, start, stop]
186
+ synchronize do |client|
187
+ client.call [:getrange, key, start, stop]
205
188
  end
206
189
  end
207
190
 
@@ -212,18 +195,24 @@ class Redis
212
195
  # @return [String] the old value stored in the key, or `nil` if the key
213
196
  # did not exist
214
197
  def getset(key, value)
215
- synchronize do
216
- @client.call [:getset, key, value]
198
+ synchronize do |client|
199
+ client.call [:getset, key, value]
217
200
  end
218
201
  end
219
202
 
220
203
  # Get the values of all the given keys.
221
204
  #
205
+ # @example
206
+ # redis.mget("key1", "key1")
207
+ # # => ["v1", "v2"]
208
+ #
222
209
  # @param [Array<String>] keys
223
- # @return [Array<String>]
210
+ # @return [Array<String>] an array of values for the specified keys
211
+ #
212
+ # @see #mapped_mget
224
213
  def mget(*keys, &blk)
225
- synchronize do
226
- @client.call [:mget, *keys], &blk
214
+ synchronize do |client|
215
+ client.call [:mget, *keys], &blk
227
216
  end
228
217
  end
229
218
 
@@ -233,8 +222,8 @@ class Redis
233
222
  # @param [String] value value to append
234
223
  # @return [Fixnum] length of the string after appending
235
224
  def append(key, value)
236
- synchronize do
237
- @client.call [:append, key, value]
225
+ synchronize do |client|
226
+ client.call [:append, key, value]
238
227
  end
239
228
  end
240
229
 
@@ -244,8 +233,8 @@ class Redis
244
233
  # @return [Fixnum] the length of the value stored in the key, or 0
245
234
  # if the key does not exist
246
235
  def strlen(key)
247
- synchronize do
248
- @client.call [:strlen, key]
236
+ synchronize do |client|
237
+ client.call [:strlen, key]
249
238
  end
250
239
  end
251
240
 
@@ -254,18 +243,8 @@ class Redis
254
243
  # @param [String] key
255
244
  # @return [Hash<String, String>]
256
245
  def hgetall(key)
257
- synchronize do
258
- @client.call [:hgetall, key] do |reply|
259
- if reply.kind_of?(Array)
260
- hash = Hash.new
261
- reply.each_slice(2) do |field, value|
262
- hash[field] = value
263
- end
264
- hash
265
- else
266
- reply
267
- end
268
- end
246
+ synchronize do |client|
247
+ client.call [:hgetall, key], &_hashify
269
248
  end
270
249
  end
271
250
 
@@ -275,8 +254,8 @@ class Redis
275
254
  # @param [String] field
276
255
  # @return [String]
277
256
  def hget(key, field)
278
- synchronize do
279
- @client.call [:hget, key, field]
257
+ synchronize do |client|
258
+ client.call [:hget, key, field]
280
259
  end
281
260
  end
282
261
 
@@ -286,8 +265,8 @@ class Redis
286
265
  # @param [String, Array<String>] field
287
266
  # @return [Fixnum] the number of fields that were removed from the hash
288
267
  def hdel(key, field)
289
- synchronize do
290
- @client.call [:hdel, key, field]
268
+ synchronize do |client|
269
+ client.call [:hdel, key, field]
291
270
  end
292
271
  end
293
272
 
@@ -296,8 +275,8 @@ class Redis
296
275
  # @param [String] key
297
276
  # @return [Array<String>]
298
277
  def hkeys(key)
299
- synchronize do
300
- @client.call [:hkeys, key]
278
+ synchronize do |client|
279
+ client.call [:hkeys, key]
301
280
  end
302
281
  end
303
282
 
@@ -306,8 +285,8 @@ class Redis
306
285
  # @param [String] pattern
307
286
  # @return [Array<String>]
308
287
  def keys(pattern = "*")
309
- synchronize do
310
- @client.call [:keys, pattern] do |reply|
288
+ synchronize do |client|
289
+ client.call [:keys, pattern] do |reply|
311
290
  if reply.kind_of?(String)
312
291
  reply.split(" ")
313
292
  else
@@ -321,8 +300,8 @@ class Redis
321
300
  #
322
301
  # @return [String]
323
302
  def randomkey
324
- synchronize do
325
- @client.call [:randomkey]
303
+ synchronize do |client|
304
+ client.call [:randomkey]
326
305
  end
327
306
  end
328
307
 
@@ -331,8 +310,23 @@ class Redis
331
310
  # @param [String] value
332
311
  # @return [String]
333
312
  def echo(value)
334
- synchronize do
335
- @client.call [:echo, value]
313
+ synchronize do |client|
314
+ client.call [:echo, value]
315
+ end
316
+ end
317
+
318
+ # Return the server time.
319
+ #
320
+ # @example
321
+ # r.time # => [ 1333093196, 606806 ]
322
+ #
323
+ # @return [Array<Fixnum>] tuple of seconds since UNIX epoch and
324
+ # microseconds in the current second
325
+ def time
326
+ synchronize do |client|
327
+ client.call [:time] do |reply|
328
+ reply.map(&:to_i) if reply
329
+ end
336
330
  end
337
331
  end
338
332
 
@@ -340,8 +334,8 @@ class Redis
340
334
  #
341
335
  # @return [String] `PONG`
342
336
  def ping
343
- synchronize do
344
- @client.call [:ping]
337
+ synchronize do |client|
338
+ client.call [:ping]
345
339
  end
346
340
  end
347
341
 
@@ -349,8 +343,8 @@ class Redis
349
343
  #
350
344
  # @return [Fixnum]
351
345
  def lastsave
352
- synchronize do
353
- @client.call [:lastsave]
346
+ synchronize do |client|
347
+ client.call [:lastsave]
354
348
  end
355
349
  end
356
350
 
@@ -358,8 +352,8 @@ class Redis
358
352
  #
359
353
  # @return [Fixnum]
360
354
  def dbsize
361
- synchronize do
362
- @client.call [:dbsize]
355
+ synchronize do |client|
356
+ client.call [:dbsize]
363
357
  end
364
358
  end
365
359
 
@@ -368,8 +362,8 @@ class Redis
368
362
  # @param [String] key
369
363
  # @return [Boolean]
370
364
  def exists(key)
371
- synchronize do
372
- @client.call [:exists, key], &_boolify
365
+ synchronize do |client|
366
+ client.call [:exists, key], &_boolify
373
367
  end
374
368
  end
375
369
 
@@ -378,8 +372,8 @@ class Redis
378
372
  # @param [String] key
379
373
  # @return [Fixnum]
380
374
  def llen(key)
381
- synchronize do
382
- @client.call [:llen, key]
375
+ synchronize do |client|
376
+ client.call [:llen, key]
383
377
  end
384
378
  end
385
379
 
@@ -390,8 +384,8 @@ class Redis
390
384
  # @param [Fixnum] stop stop index
391
385
  # @return [Array<String>]
392
386
  def lrange(key, start, stop)
393
- synchronize do
394
- @client.call [:lrange, key, start, stop]
387
+ synchronize do |client|
388
+ client.call [:lrange, key, start, stop]
395
389
  end
396
390
  end
397
391
 
@@ -402,8 +396,8 @@ class Redis
402
396
  # @param [Fixnum] stop stop index
403
397
  # @return [String] `OK`
404
398
  def ltrim(key, start, stop)
405
- synchronize do
406
- @client.call [:ltrim, key, start, stop]
399
+ synchronize do |client|
400
+ client.call [:ltrim, key, start, stop]
407
401
  end
408
402
  end
409
403
 
@@ -413,8 +407,8 @@ class Redis
413
407
  # @param [Fixnum] index
414
408
  # @return [String]
415
409
  def lindex(key, index)
416
- synchronize do
417
- @client.call [:lindex, key, index]
410
+ synchronize do |client|
411
+ client.call [:lindex, key, index]
418
412
  end
419
413
  end
420
414
 
@@ -427,8 +421,8 @@ class Redis
427
421
  # @return [Fixnum] length of the list after the insert operation, or `-1`
428
422
  # when the element `pivot` was not found
429
423
  def linsert(key, where, pivot, value)
430
- synchronize do
431
- @client.call [:linsert, key, where, pivot, value]
424
+ synchronize do |client|
425
+ client.call [:linsert, key, where, pivot, value]
432
426
  end
433
427
  end
434
428
 
@@ -439,8 +433,8 @@ class Redis
439
433
  # @param [String] value
440
434
  # @return [String] `OK`
441
435
  def lset(key, index, value)
442
- synchronize do
443
- @client.call [:lset, key, index, value]
436
+ synchronize do |client|
437
+ client.call [:lset, key, index, value]
444
438
  end
445
439
  end
446
440
 
@@ -454,8 +448,8 @@ class Redis
454
448
  # @param [String] value
455
449
  # @return [Fixnum] the number of removed elements
456
450
  def lrem(key, count, value)
457
- synchronize do
458
- @client.call [:lrem, key, count, value]
451
+ synchronize do |client|
452
+ client.call [:lrem, key, count, value]
459
453
  end
460
454
  end
461
455
 
@@ -465,8 +459,8 @@ class Redis
465
459
  # @param [String] value
466
460
  # @return [Fixnum] the length of the list after the push operation
467
461
  def rpush(key, value)
468
- synchronize do
469
- @client.call [:rpush, key, value]
462
+ synchronize do |client|
463
+ client.call [:rpush, key, value]
470
464
  end
471
465
  end
472
466
 
@@ -476,8 +470,8 @@ class Redis
476
470
  # @param [String] value
477
471
  # @return [Fixnum] the length of the list after the push operation
478
472
  def rpushx(key, value)
479
- synchronize do
480
- @client.call [:rpushx, key, value]
473
+ synchronize do |client|
474
+ client.call [:rpushx, key, value]
481
475
  end
482
476
  end
483
477
 
@@ -487,8 +481,8 @@ class Redis
487
481
  # @param [String] value
488
482
  # @return [Fixnum] the length of the list after the push operation
489
483
  def lpush(key, value)
490
- synchronize do
491
- @client.call [:lpush, key, value]
484
+ synchronize do |client|
485
+ client.call [:lpush, key, value]
492
486
  end
493
487
  end
494
488
 
@@ -498,8 +492,8 @@ class Redis
498
492
  # @param [String] value
499
493
  # @return [Fixnum] the length of the list after the push operation
500
494
  def lpushx(key, value)
501
- synchronize do
502
- @client.call [:lpushx, key, value]
495
+ synchronize do |client|
496
+ client.call [:lpushx, key, value]
503
497
  end
504
498
  end
505
499
 
@@ -508,33 +502,73 @@ class Redis
508
502
  # @param [String] key
509
503
  # @return [String]
510
504
  def rpop(key)
511
- synchronize do
512
- @client.call [:rpop, key]
505
+ synchronize do |client|
506
+ client.call [:rpop, key]
507
+ end
508
+ end
509
+
510
+ def _bpop(cmd, args)
511
+ options = {}
512
+
513
+ case args.last
514
+ when Hash
515
+ options = args.pop
516
+ when Integer
517
+ # Issue deprecation notice in obnoxious mode...
518
+ options[:timeout] = args.pop
519
+ end
520
+
521
+ if args.size > 1
522
+ # Issue deprecation notice in obnoxious mode...
523
+ end
524
+
525
+ keys = args.flatten
526
+ timeout = options[:timeout] || 0
527
+
528
+ synchronize do |client|
529
+ client.call_without_timeout [cmd, keys, timeout]
513
530
  end
514
531
  end
515
532
 
516
533
  # Remove and get the first element in a list, or block until one is available.
517
534
  #
518
- # @param [Array<String>] args one or more keys to perform a blocking pop on,
519
- # followed by a `Fixnum` timeout value
520
- # @return [nil, Array<String>] tuple of list that was popped from and element
521
- # that was popped, or nil when the blocking operation timed out
535
+ # @example With timeout
536
+ # list, element = redis.blpop("list", :timeout => 5)
537
+ # # => nil on timeout
538
+ # # => ["list", "element"] on success
539
+ # @example Without timeout
540
+ # list, element = redis.blpop("list")
541
+ # # => ["list", "element"]
542
+ # @example Blocking pop on multiple lists
543
+ # list, element = redis.blpop(["list", "another_list"])
544
+ # # => ["list", "element"]
545
+ #
546
+ # @param [String, Array<String>] keys one or more keys to perform the
547
+ # blocking pop on
548
+ # @param [Hash] options
549
+ # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
550
+ #
551
+ # @return [nil, [String, String]]
552
+ # - `nil` when the operation timed out
553
+ # - tuple of the list that was popped from and element was popped otherwise
522
554
  def blpop(*args)
523
- synchronize do
524
- @client.call_without_timeout [:blpop, *args]
525
- end
555
+ _bpop(:blpop, args)
526
556
  end
527
557
 
528
558
  # Remove and get the last element in a list, or block until one is available.
529
559
  #
530
- # @param [Array<String>] args one or more keys to perform a blocking pop on,
531
- # followed by a `Fixnum` timeout value
532
- # @return [nil, Array<String>] tuple of list that was popped from and element
533
- # that was popped, or nil when the blocking operation timed out
560
+ # @param [String, Array<String>] keys one or more keys to perform the
561
+ # blocking pop on
562
+ # @param [Hash] options
563
+ # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
564
+ #
565
+ # @return [nil, [String, String]]
566
+ # - `nil` when the operation timed out
567
+ # - tuple of the list that was popped from and element was popped otherwise
568
+ #
569
+ # @see #blpop
534
570
  def brpop(*args)
535
- synchronize do
536
- @client.call_without_timeout [:brpop, *args]
537
- end
571
+ _bpop(:brpop, args)
538
572
  end
539
573
 
540
574
  # Pop a value from a list, push it to another list and return it; or block
@@ -542,11 +576,23 @@ class Redis
542
576
  #
543
577
  # @param [String] source source key
544
578
  # @param [String] destination destination key
545
- # @param [Fixnum] timeout
546
- # @return [nil, String] the element, or nil when the blocking operation timed out
547
- def brpoplpush(source, destination, timeout)
548
- synchronize do
549
- @client.call_without_timeout [:brpoplpush, source, destination, timeout]
579
+ # @param [Hash] options
580
+ # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
581
+ #
582
+ # @return [nil, String]
583
+ # - `nil` when the operation timed out
584
+ # - the element was popped and pushed otherwise
585
+ def brpoplpush(source, destination, options = {})
586
+ case options
587
+ when Integer
588
+ # Issue deprecation notice in obnoxious mode...
589
+ options = { :timeout => options }
590
+ end
591
+
592
+ timeout = options[:timeout] || 0
593
+
594
+ synchronize do |client|
595
+ client.call_without_timeout [:brpoplpush, source, destination, timeout]
550
596
  end
551
597
  end
552
598
 
@@ -556,8 +602,8 @@ class Redis
556
602
  # @param [String] destination destination key
557
603
  # @return [nil, String] the element, or nil when the source key does not exist
558
604
  def rpoplpush(source, destination)
559
- synchronize do
560
- @client.call [:rpoplpush, source, destination]
605
+ synchronize do |client|
606
+ client.call [:rpoplpush, source, destination]
561
607
  end
562
608
  end
563
609
 
@@ -566,8 +612,8 @@ class Redis
566
612
  # @param [String] key
567
613
  # @return [String]
568
614
  def lpop(key)
569
- synchronize do
570
- @client.call [:lpop, key]
615
+ synchronize do |client|
616
+ client.call [:lpop, key]
571
617
  end
572
618
  end
573
619
 
@@ -577,10 +623,10 @@ class Redis
577
623
  # @param [Fixnum] length maximum number of entries to return
578
624
  # @return [Array<String>, Fixnum, String] depends on subcommand
579
625
  def slowlog(subcommand, length=nil)
580
- synchronize do
626
+ synchronize do |client|
581
627
  args = [:slowlog, subcommand]
582
628
  args << length if length
583
- @client.call args
629
+ client.call args
584
630
  end
585
631
  end
586
632
 
@@ -589,8 +635,8 @@ class Redis
589
635
  # @param [String] key
590
636
  # @return [Array<String>]
591
637
  def smembers(key)
592
- synchronize do
593
- @client.call [:smembers, key]
638
+ synchronize do |client|
639
+ client.call [:smembers, key]
594
640
  end
595
641
  end
596
642
 
@@ -600,8 +646,8 @@ class Redis
600
646
  # @param [String] member
601
647
  # @return [Boolean]
602
648
  def sismember(key, member)
603
- synchronize do
604
- @client.call [:sismember, key, member], &_boolify
649
+ synchronize do |client|
650
+ client.call [:sismember, key, member], &_boolify
605
651
  end
606
652
  end
607
653
 
@@ -614,8 +660,8 @@ class Redis
614
660
  # array of members is specified, holding the number of members that were
615
661
  # successfully added
616
662
  def sadd(key, member)
617
- synchronize do
618
- @client.call [:sadd, key, member] do |reply|
663
+ synchronize do |client|
664
+ client.call [:sadd, key, member] do |reply|
619
665
  if member.is_a? Array
620
666
  # Variadic: return integer
621
667
  reply
@@ -636,8 +682,8 @@ class Redis
636
682
  # array of members is specified, holding the number of members that were
637
683
  # successfully removed
638
684
  def srem(key, member)
639
- synchronize do
640
- @client.call [:srem, key, member] do |reply|
685
+ synchronize do |client|
686
+ client.call [:srem, key, member] do |reply|
641
687
  if member.is_a? Array
642
688
  # Variadic: return integer
643
689
  reply
@@ -656,8 +702,8 @@ class Redis
656
702
  # @param [String] member member to move from `source` to `destination`
657
703
  # @return [Boolean]
658
704
  def smove(source, destination, member)
659
- synchronize do
660
- @client.call [:smove, source, destination, member], &_boolify
705
+ synchronize do |client|
706
+ client.call [:smove, source, destination, member], &_boolify
661
707
  end
662
708
  end
663
709
 
@@ -666,8 +712,8 @@ class Redis
666
712
  # @param [String] key
667
713
  # @return [String]
668
714
  def spop(key)
669
- synchronize do
670
- @client.call [:spop, key]
715
+ synchronize do |client|
716
+ client.call [:spop, key]
671
717
  end
672
718
  end
673
719
 
@@ -676,8 +722,8 @@ class Redis
676
722
  # @param [String] key
677
723
  # @return [Fixnum]
678
724
  def scard(key)
679
- synchronize do
680
- @client.call [:scard, key]
725
+ synchronize do |client|
726
+ client.call [:scard, key]
681
727
  end
682
728
  end
683
729
 
@@ -686,8 +732,8 @@ class Redis
686
732
  # @param [String, Array<String>] keys keys pointing to sets to intersect
687
733
  # @return [Array<String>] members in the intersection
688
734
  def sinter(*keys)
689
- synchronize do
690
- @client.call [:sinter, *keys]
735
+ synchronize do |client|
736
+ client.call [:sinter, *keys]
691
737
  end
692
738
  end
693
739
 
@@ -697,8 +743,8 @@ class Redis
697
743
  # @param [String, Array<String>] keys keys pointing to sets to intersect
698
744
  # @return [Fixnum] number of elements in the resulting set
699
745
  def sinterstore(destination, *keys)
700
- synchronize do
701
- @client.call [:sinterstore, destination, *keys]
746
+ synchronize do |client|
747
+ client.call [:sinterstore, destination, *keys]
702
748
  end
703
749
  end
704
750
 
@@ -707,8 +753,8 @@ class Redis
707
753
  # @param [String, Array<String>] keys keys pointing to sets to unify
708
754
  # @return [Array<String>] members in the union
709
755
  def sunion(*keys)
710
- synchronize do
711
- @client.call [:sunion, *keys]
756
+ synchronize do |client|
757
+ client.call [:sunion, *keys]
712
758
  end
713
759
  end
714
760
 
@@ -718,8 +764,8 @@ class Redis
718
764
  # @param [String, Array<String>] keys keys pointing to sets to unify
719
765
  # @return [Fixnum] number of elements in the resulting set
720
766
  def sunionstore(destination, *keys)
721
- synchronize do
722
- @client.call [:sunionstore, destination, *keys]
767
+ synchronize do |client|
768
+ client.call [:sunionstore, destination, *keys]
723
769
  end
724
770
  end
725
771
 
@@ -728,8 +774,8 @@ class Redis
728
774
  # @param [String, Array<String>] keys keys pointing to sets to subtract
729
775
  # @return [Array<String>] members in the difference
730
776
  def sdiff(*keys)
731
- synchronize do
732
- @client.call [:sdiff, *keys]
777
+ synchronize do |client|
778
+ client.call [:sdiff, *keys]
733
779
  end
734
780
  end
735
781
 
@@ -739,8 +785,8 @@ class Redis
739
785
  # @param [String, Array<String>] keys keys pointing to sets to subtract
740
786
  # @return [Fixnum] number of elements in the resulting set
741
787
  def sdiffstore(destination, *keys)
742
- synchronize do
743
- @client.call [:sdiffstore, destination, *keys]
788
+ synchronize do |client|
789
+ client.call [:sdiffstore, destination, *keys]
744
790
  end
745
791
  end
746
792
 
@@ -749,23 +795,23 @@ class Redis
749
795
  # @param [String] key
750
796
  # @return [String]
751
797
  def srandmember(key)
752
- synchronize do
753
- @client.call [:srandmember, key]
798
+ synchronize do |client|
799
+ client.call [:srandmember, key]
754
800
  end
755
801
  end
756
802
 
757
803
  # Add one or more members to a sorted set, or update the score for members
758
804
  # that already exist.
759
805
  #
760
- # @example Add a single `(score, member)` pair to a sorted set
806
+ # @example Add a single `[score, member]` pair to a sorted set
761
807
  # redis.zadd("zset", 32.0, "member")
762
- # @example Add an array of `(score, member)` pairs to a sorted set
808
+ # @example Add an array of `[score, member]` pairs to a sorted set
763
809
  # redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
764
810
  #
765
811
  # @param [String] key
766
- # @param [(Float, String), Array<(Float,String)>] args
767
- # - a single `(score, member)` pair
768
- # - an array of `(score, member)` pairs
812
+ # @param [[Float, String], Array<[Float, String]>] args
813
+ # - a single `[score, member]` pair
814
+ # - an array of `[score, member]` pairs
769
815
  #
770
816
  # @return [Boolean, Fixnum]
771
817
  # - `Boolean` when a single pair is specified, holding whether or not it was
@@ -773,13 +819,13 @@ class Redis
773
819
  # - `Fixnum` when an array of pairs is specified, holding the number of
774
820
  # pairs that were **added** to the sorted set
775
821
  def zadd(key, *args)
776
- synchronize do
822
+ synchronize do |client|
777
823
  if args.size == 1 && args[0].is_a?(Array)
778
824
  # Variadic: return integer
779
- @client.call [:zadd, key] + args[0]
825
+ client.call [:zadd, key] + args[0]
780
826
  elsif args.size == 2
781
827
  # Single pair: return boolean
782
- @client.call [:zadd, key, args[0], args[1]], &_boolify
828
+ client.call [:zadd, key, args[0], args[1]], &_boolify
783
829
  else
784
830
  raise ArgumentError, "wrong number of arguments"
785
831
  end
@@ -804,8 +850,8 @@ class Redis
804
850
  # - `Fixnum` when an array of pairs is specified, holding the number of
805
851
  # members that were removed to the sorted set
806
852
  def zrem(key, member)
807
- synchronize do
808
- @client.call [:zrem, key, member] do |reply|
853
+ synchronize do |client|
854
+ client.call [:zrem, key, member] do |reply|
809
855
  if member.is_a? Array
810
856
  # Variadic: return integer
811
857
  reply
@@ -823,8 +869,8 @@ class Redis
823
869
  # @param [String] member
824
870
  # @return [Fixnum]
825
871
  def zrank(key, member)
826
- synchronize do
827
- @client.call [:zrank, key, member]
872
+ synchronize do |client|
873
+ client.call [:zrank, key, member]
828
874
  end
829
875
  end
830
876
 
@@ -835,8 +881,8 @@ class Redis
835
881
  # @param [String] member
836
882
  # @return [Fixnum]
837
883
  def zrevrank(key, member)
838
- synchronize do
839
- @client.call [:zrevrank, key, member]
884
+ synchronize do |client|
885
+ client.call [:zrevrank, key, member]
840
886
  end
841
887
  end
842
888
 
@@ -851,8 +897,8 @@ class Redis
851
897
  # @param [String] member
852
898
  # @return [Float] score of the member after incrementing it
853
899
  def zincrby(key, increment, member)
854
- synchronize do
855
- @client.call [:zincrby, key, increment, member] do |reply|
900
+ synchronize do |client|
901
+ client.call [:zincrby, key, increment, member] do |reply|
856
902
  Float(reply) if reply
857
903
  end
858
904
  end
@@ -867,8 +913,8 @@ class Redis
867
913
  # @param [String] key
868
914
  # @return [Fixnum]
869
915
  def zcard(key)
870
- synchronize do
871
- @client.call [:zcard, key]
916
+ synchronize do |client|
917
+ client.call [:zcard, key]
872
918
  end
873
919
  end
874
920
 
@@ -887,17 +933,17 @@ class Redis
887
933
  # @param [Hash] options
888
934
  # - `:with_scores => true`: include scores in output
889
935
  #
890
- # @return [Array<String>, Array<(String, Float)>]
936
+ # @return [Array<String>, Array<[String, Float]>]
891
937
  # - when `:with_scores` is not specified, an array of members
892
- # - when `:with_scores` is specified, an array with `(member, score)` pairs
938
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
893
939
  def zrange(key, start, stop, options = {})
894
940
  args = []
895
941
 
896
942
  with_scores = options[:with_scores] || options[:withscores]
897
943
  args << "WITHSCORES" if with_scores
898
944
 
899
- synchronize do
900
- @client.call [:zrange, key, start, stop, *args] do |reply|
945
+ synchronize do |client|
946
+ client.call [:zrange, key, start, stop, *args] do |reply|
901
947
  if with_scores
902
948
  if reply
903
949
  reply.each_slice(2).map do |member, score|
@@ -928,8 +974,8 @@ class Redis
928
974
  with_scores = options[:with_scores] || options[:withscores]
929
975
  args << "WITHSCORES" if with_scores
930
976
 
931
- synchronize do
932
- @client.call [:zrevrange, key, start, stop, *args] do |reply|
977
+ synchronize do |client|
978
+ client.call [:zrevrange, key, start, stop, *args] do |reply|
933
979
  if with_scores
934
980
  if reply
935
981
  reply.each_slice(2).map do |member, score|
@@ -967,9 +1013,9 @@ class Redis
967
1013
  # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
968
1014
  # `count` members
969
1015
  #
970
- # @return [Array<String>, Array<(String, Float)>]
1016
+ # @return [Array<String>, Array<[String, Float]>]
971
1017
  # - when `:with_scores` is not specified, an array of members
972
- # - when `:with_scores` is specified, an array with `(member, score)` pairs
1018
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
973
1019
  def zrangebyscore(key, min, max, options = {})
974
1020
  args = []
975
1021
 
@@ -979,8 +1025,8 @@ class Redis
979
1025
  limit = options[:limit]
980
1026
  args.concat ["LIMIT", *limit] if limit
981
1027
 
982
- synchronize do
983
- @client.call [:zrangebyscore, key, min, max, *args] do |reply|
1028
+ synchronize do |client|
1029
+ client.call [:zrangebyscore, key, min, max, *args] do |reply|
984
1030
  if with_scores
985
1031
  if reply
986
1032
  reply.each_slice(2).map do |member, score|
@@ -1017,8 +1063,8 @@ class Redis
1017
1063
  limit = options[:limit]
1018
1064
  args.concat ["LIMIT", *limit] if limit
1019
1065
 
1020
- synchronize do
1021
- @client.call [:zrevrangebyscore, key, max, min, *args] do |reply|
1066
+ synchronize do |client|
1067
+ client.call [:zrevrangebyscore, key, max, min, *args] do |reply|
1022
1068
  if with_scores
1023
1069
  if reply
1024
1070
  reply.each_slice(2).map do |member, score|
@@ -1049,9 +1095,9 @@ class Redis
1049
1095
  # - inclusive maximum score is specified verbatim
1050
1096
  # - exclusive maximum score is specified by prefixing `(`
1051
1097
  # @return [Fixnum] number of members in within the specified range
1052
- def zcount(key, start, stop)
1053
- synchronize do
1054
- @client.call [:zcount, key, start, stop]
1098
+ def zcount(key, min, max)
1099
+ synchronize do |client|
1100
+ client.call [:zcount, key, min, max]
1055
1101
  end
1056
1102
  end
1057
1103
 
@@ -1073,8 +1119,8 @@ class Redis
1073
1119
  # - exclusive maximum score is specified by prefixing `(`
1074
1120
  # @return [Fixnum] number of members that were removed
1075
1121
  def zremrangebyscore(key, min, max)
1076
- synchronize do
1077
- @client.call [:zremrangebyscore, key, min, max]
1122
+ synchronize do |client|
1123
+ client.call [:zremrangebyscore, key, min, max]
1078
1124
  end
1079
1125
  end
1080
1126
 
@@ -1092,8 +1138,8 @@ class Redis
1092
1138
  # @param [Fixnum] stop stop index
1093
1139
  # @return [Fixnum] number of members that were removed
1094
1140
  def zremrangebyrank(key, start, stop)
1095
- synchronize do
1096
- @client.call [:zremrangebyrank, key, start, stop]
1141
+ synchronize do |client|
1142
+ client.call [:zremrangebyrank, key, start, stop]
1097
1143
  end
1098
1144
  end
1099
1145
 
@@ -1107,8 +1153,8 @@ class Redis
1107
1153
  # @param [String] member
1108
1154
  # @return [Float] score of the member
1109
1155
  def zscore(key, member)
1110
- synchronize do
1111
- @client.call [:zscore, key, member] do |reply|
1156
+ synchronize do |client|
1157
+ client.call [:zscore, key, member] do |reply|
1112
1158
  Float(reply) if reply
1113
1159
  end
1114
1160
  end
@@ -1129,13 +1175,16 @@ class Redis
1129
1175
  # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
1130
1176
  # @return [Fixnum] number of elements in the resulting sorted set
1131
1177
  def zinterstore(destination, keys, options = {})
1132
- command = CommandOptions.new(options) do |c|
1133
- c.splat :weights
1134
- c.value :aggregate
1135
- end
1178
+ args = []
1179
+
1180
+ weights = options[:weights]
1181
+ args.concat ["WEIGHTS", *weights] if weights
1182
+
1183
+ aggregate = options[:aggregate]
1184
+ args.concat ["AGGREGATE", aggregate] if aggregate
1136
1185
 
1137
- synchronize do
1138
- @client.call [:zinterstore, destination, keys.size, *(keys + command.to_a)]
1186
+ synchronize do |client|
1187
+ client.call [:zinterstore, destination, keys.size, *(keys + args)]
1139
1188
  end
1140
1189
  end
1141
1190
 
@@ -1153,13 +1202,16 @@ class Redis
1153
1202
  # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
1154
1203
  # @return [Fixnum] number of elements in the resulting sorted set
1155
1204
  def zunionstore(destination, keys, options = {})
1156
- command = CommandOptions.new(options) do |c|
1157
- c.splat :weights
1158
- c.value :aggregate
1159
- end
1205
+ args = []
1206
+
1207
+ weights = options[:weights]
1208
+ args.concat ["WEIGHTS", *weights] if weights
1209
+
1210
+ aggregate = options[:aggregate]
1211
+ args.concat ["AGGREGATE", aggregate] if aggregate
1160
1212
 
1161
- synchronize do
1162
- @client.call [:zunionstore, destination, keys.size, *(keys + command.to_a)]
1213
+ synchronize do |client|
1214
+ client.call [:zunionstore, destination, keys.size, *(keys + args)]
1163
1215
  end
1164
1216
  end
1165
1217
 
@@ -1183,8 +1235,8 @@ class Redis
1183
1235
  # @param [Fixnum] db
1184
1236
  # @return [Boolean] whether the key was moved or not
1185
1237
  def move(key, db)
1186
- synchronize do
1187
- @client.call [:move, key, db], &_boolify
1238
+ synchronize do |client|
1239
+ client.call [:move, key, db], &_boolify
1188
1240
  end
1189
1241
  end
1190
1242
 
@@ -1194,18 +1246,18 @@ class Redis
1194
1246
  # @param [String] value
1195
1247
  # @return [Boolean] whether the key was set or not
1196
1248
  def setnx(key, value)
1197
- synchronize do
1198
- @client.call [:setnx, key, value], &_boolify
1249
+ synchronize do |client|
1250
+ client.call [:setnx, key, value], &_boolify
1199
1251
  end
1200
1252
  end
1201
1253
 
1202
1254
  # Delete one or more keys.
1203
1255
  #
1204
1256
  # @param [String, Array<String>] keys
1205
- # @return [Fixnum] number of keys that were removed
1257
+ # @return [Fixnum] number of keys that were deleted
1206
1258
  def del(*keys)
1207
- synchronize do
1208
- @client.call [:del, *keys]
1259
+ synchronize do |client|
1260
+ client.call [:del, *keys]
1209
1261
  end
1210
1262
  end
1211
1263
 
@@ -1215,8 +1267,8 @@ class Redis
1215
1267
  # @param [String] new_name
1216
1268
  # @return [String] `OK`
1217
1269
  def rename(old_name, new_name)
1218
- synchronize do
1219
- @client.call [:rename, old_name, new_name]
1270
+ synchronize do |client|
1271
+ client.call [:rename, old_name, new_name]
1220
1272
  end
1221
1273
  end
1222
1274
 
@@ -1226,20 +1278,30 @@ class Redis
1226
1278
  # @param [String] new_name
1227
1279
  # @return [Boolean] whether the key was renamed or not
1228
1280
  def renamenx(old_name, new_name)
1229
- synchronize do
1230
- @client.call [:renamenx, old_name, new_name], &_boolify
1281
+ synchronize do |client|
1282
+ client.call [:renamenx, old_name, new_name], &_boolify
1231
1283
  end
1232
1284
  end
1233
1285
 
1234
1286
  # Set a key's time to live in seconds.
1235
1287
  #
1236
1288
  # @param [String] key
1237
- # @param [Fixnum] seconds time to live. After this timeout has expired,
1238
- # the key will automatically be deleted
1289
+ # @param [Fixnum] seconds time to live
1239
1290
  # @return [Boolean] whether the timeout was set or not
1240
1291
  def expire(key, seconds)
1241
- synchronize do
1242
- @client.call [:expire, key, seconds], &_boolify
1292
+ synchronize do |client|
1293
+ client.call [:expire, key, seconds], &_boolify
1294
+ end
1295
+ end
1296
+
1297
+ # Set a key's time to live in milliseconds.
1298
+ #
1299
+ # @param [String] key
1300
+ # @param [Fixnum] milliseconds time to live
1301
+ # @return [Boolean] whether the timeout was set or not
1302
+ def pexpire(key, milliseconds)
1303
+ synchronize do |client|
1304
+ client.call [:pexpire, key, milliseconds], &_boolify
1243
1305
  end
1244
1306
  end
1245
1307
 
@@ -1248,19 +1310,30 @@ class Redis
1248
1310
  # @param [String] key
1249
1311
  # @return [Boolean] whether the timeout was removed or not
1250
1312
  def persist(key)
1251
- synchronize do
1252
- @client.call [:persist, key], &_boolify
1313
+ synchronize do |client|
1314
+ client.call [:persist, key], &_boolify
1253
1315
  end
1254
1316
  end
1255
1317
 
1256
- # Get the time to live for a key.
1318
+ # Get the time to live (in seconds) for a key.
1257
1319
  #
1258
1320
  # @param [String] key
1259
1321
  # @return [Fixnum] remaining time to live in seconds, or -1 if the
1260
1322
  # key does not exist or does not have a timeout
1261
1323
  def ttl(key)
1262
- synchronize do
1263
- @client.call [:ttl, key]
1324
+ synchronize do |client|
1325
+ client.call [:ttl, key]
1326
+ end
1327
+ end
1328
+
1329
+ # Get the time to live (in milliseconds) for a key.
1330
+ #
1331
+ # @param [String] key
1332
+ # @return [Fixnum] remaining time to live in milliseconds, or -1 if the
1333
+ # key does not exist or does not have a timeout
1334
+ def pttl(key)
1335
+ synchronize do |client|
1336
+ client.call [:pttl, key]
1264
1337
  end
1265
1338
  end
1266
1339
 
@@ -1268,47 +1341,107 @@ class Redis
1268
1341
  #
1269
1342
  # @param [String] key
1270
1343
  # @param [Fixnum] unix_time expiry time specified as a UNIX timestamp
1271
- # (seconds since January 1, 1970). After this timeout has expired,
1272
- # the key will automatically be deleted
1273
1344
  # @return [Boolean] whether the timeout was set or not
1274
1345
  def expireat(key, unix_time)
1275
- synchronize do
1276
- @client.call [:expireat, key, unix_time], &_boolify
1346
+ synchronize do |client|
1347
+ client.call [:expireat, key, unix_time], &_boolify
1277
1348
  end
1278
1349
  end
1279
1350
 
1351
+ # Set the expiration for a key as number of milliseconds from UNIX Epoch.
1352
+ #
1353
+ # @param [String] key
1354
+ # @param [Fixnum] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
1355
+ # @return [Boolean] whether the timeout was set or not
1356
+ def pexpireat(key, ms_unix_time)
1357
+ synchronize do |client|
1358
+ client.call [:pexpireat, key, ms_unix_time], &_boolify
1359
+ end
1360
+ end
1280
1361
  # Set the string value of a hash field.
1362
+ #
1363
+ # @param [String] key
1364
+ # @param [String] field
1365
+ # @param [String] value
1366
+ # @return [Boolean] whether or not the field was **added** to the hash
1281
1367
  def hset(key, field, value)
1282
- synchronize do
1283
- @client.call [:hset, key, field, value], &_boolify
1368
+ synchronize do |client|
1369
+ client.call [:hset, key, field, value], &_boolify
1284
1370
  end
1285
1371
  end
1286
1372
 
1287
1373
  # Set the value of a hash field, only if the field does not exist.
1374
+ #
1375
+ # @param [String] key
1376
+ # @param [String] field
1377
+ # @param [String] value
1378
+ # @return [Boolean] whether or not the field was **added** to the hash
1288
1379
  def hsetnx(key, field, value)
1289
- synchronize do
1290
- @client.call [:hsetnx, key, field, value], &_boolify
1380
+ synchronize do |client|
1381
+ client.call [:hsetnx, key, field, value], &_boolify
1291
1382
  end
1292
1383
  end
1293
1384
 
1294
- # Set multiple hash fields to multiple values.
1385
+ # Set one or more hash values.
1386
+ #
1387
+ # @example
1388
+ # redis.hmset("hash", "f1", "v1", "f2", "v2")
1389
+ # # => "OK"
1390
+ #
1391
+ # @param [String] key
1392
+ # @param [Array<String>] attrs array of fields and values
1393
+ # @return `"OK"`
1394
+ #
1395
+ # @see #mapped_hmset
1295
1396
  def hmset(key, *attrs)
1296
- synchronize do
1297
- @client.call [:hmset, key, *attrs]
1397
+ synchronize do |client|
1398
+ client.call [:hmset, key, *attrs]
1298
1399
  end
1299
1400
  end
1300
1401
 
1402
+ # Set one or more hash values.
1403
+ #
1404
+ # @example
1405
+ # redis.hmset("hash", { "f1" => "v1", "f2" => "v2" })
1406
+ # # => "OK"
1407
+ #
1408
+ # @param [String] key
1409
+ # @param [Hash] hash fields mapping to values
1410
+ # @return `"OK"`
1411
+ #
1412
+ # @see #hmset
1301
1413
  def mapped_hmset(key, hash)
1302
1414
  hmset(key, *hash.to_a.flatten)
1303
1415
  end
1304
1416
 
1305
1417
  # Get the values of all the given hash fields.
1418
+ #
1419
+ # @example
1420
+ # redis.hmget("hash", "f1", "f2")
1421
+ # # => ["v1", "v2"]
1422
+ #
1423
+ # @param [String] key
1424
+ # @param [Array<String>] fields array of fields
1425
+ # @return [Array<String>] an array of values for the specified fields
1426
+ #
1427
+ # @see #mapped_hmget
1306
1428
  def hmget(key, *fields, &blk)
1307
- synchronize do
1308
- @client.call [:hmget, key, *fields], &blk
1429
+ synchronize do |client|
1430
+ client.call [:hmget, key, *fields], &blk
1309
1431
  end
1310
1432
  end
1311
1433
 
1434
+ # Get the values of all the given hash fields.
1435
+ #
1436
+ # @example
1437
+ # redis.hmget("hash", "f1", "f2")
1438
+ # # => { "f1" => "v1", "f2" => "v2" }
1439
+ #
1440
+ # @param [String] key
1441
+ # @param [Array<String>] fields array of fields
1442
+ # @return [Hash] a hash mapping the specified fields to their values
1443
+ #
1444
+ # @see #hmget
1312
1445
  def mapped_hmget(key, *fields)
1313
1446
  hmget(key, *fields) do |reply|
1314
1447
  if reply.kind_of?(Array)
@@ -1324,118 +1457,224 @@ class Redis
1324
1457
  end
1325
1458
 
1326
1459
  # Get the number of fields in a hash.
1460
+ #
1461
+ # @param [String] key
1462
+ # @return [Fixnum] number of fields in the hash
1327
1463
  def hlen(key)
1328
- synchronize do
1329
- @client.call [:hlen, key]
1464
+ synchronize do |client|
1465
+ client.call [:hlen, key]
1330
1466
  end
1331
1467
  end
1332
1468
 
1333
1469
  # Get all the values in a hash.
1470
+ #
1471
+ # @param [String] key
1472
+ # @return [Array<String>]
1334
1473
  def hvals(key)
1335
- synchronize do
1336
- @client.call [:hvals, key]
1474
+ synchronize do |client|
1475
+ client.call [:hvals, key]
1337
1476
  end
1338
1477
  end
1339
1478
 
1340
- # Increment the integer value of a hash field by the given number.
1479
+ # Increment the integer value of a hash field by the given integer number.
1480
+ #
1481
+ # @param [String] key
1482
+ # @param [String] field
1483
+ # @param [Fixnum] increment
1484
+ # @return [Fixnum] value of the field after incrementing it
1341
1485
  def hincrby(key, field, increment)
1342
- synchronize do
1343
- @client.call [:hincrby, key, field, increment]
1486
+ synchronize do |client|
1487
+ client.call [:hincrby, key, field, increment]
1344
1488
  end
1345
1489
  end
1346
1490
 
1347
- # Discard all commands issued after MULTI.
1348
- def discard
1349
- synchronize do
1350
- @client.call [:discard]
1491
+ # Increment the numeric value of a hash field by the given float number.
1492
+ #
1493
+ # @param [String] key
1494
+ # @param [String] field
1495
+ # @param [Float] increment
1496
+ # @return [Float] value of the field after incrementing it
1497
+ def hincrbyfloat(key, field, increment)
1498
+ synchronize do |client|
1499
+ client.call [:hincrbyfloat, key, field, increment] do |reply|
1500
+ Float(reply) if reply
1501
+ end
1351
1502
  end
1352
1503
  end
1353
1504
 
1354
1505
  # Determine if a hash field exists.
1506
+ #
1507
+ # @param [String] key
1508
+ # @param [String] field
1509
+ # @return [Boolean] whether or not the field exists in the hash
1355
1510
  def hexists(key, field)
1356
- synchronize do
1357
- @client.call [:hexists, key, field], &_boolify
1511
+ synchronize do |client|
1512
+ client.call [:hexists, key, field], &_boolify
1358
1513
  end
1359
1514
  end
1360
1515
 
1361
1516
  # Listen for all requests received by the server in real time.
1517
+ #
1518
+ # There is no way to interrupt this command.
1519
+ #
1520
+ # @yield a block to be called for every line of output
1521
+ # @yieldparam [String] line timestamp and command that was executed
1362
1522
  def monitor(&block)
1363
- synchronize do
1364
- @client.call_loop([:monitor], &block)
1523
+ synchronize do |client|
1524
+ client.call_loop([:monitor], &block)
1365
1525
  end
1366
1526
  end
1367
1527
 
1368
1528
  def debug(*args)
1369
- synchronize do
1370
- @client.call [:debug, *args]
1529
+ synchronize do |client|
1530
+ client.call [:debug, *args]
1371
1531
  end
1372
1532
  end
1373
1533
 
1374
1534
  def object(*args)
1375
- synchronize do
1376
- @client.call [:object, *args]
1535
+ synchronize do |client|
1536
+ client.call [:object, *args]
1377
1537
  end
1378
1538
  end
1379
1539
 
1380
1540
  # Internal command used for replication.
1381
1541
  def sync
1382
- synchronize do
1383
- @client.call [:sync]
1542
+ synchronize do |client|
1543
+ client.call [:sync]
1384
1544
  end
1385
1545
  end
1386
1546
 
1387
1547
  # Set the string value of a key.
1548
+ #
1549
+ # @param [String] key
1550
+ # @param [String] value
1551
+ # @return `"OK"`
1388
1552
  def set(key, value)
1389
- synchronize do
1390
- @client.call [:set, key, value]
1553
+ synchronize do |client|
1554
+ client.call [:set, key, value]
1391
1555
  end
1392
1556
  end
1393
1557
 
1394
1558
  alias :[]= :set
1395
1559
 
1396
1560
  # Sets or clears the bit at offset in the string value stored at key.
1561
+ #
1562
+ # @param [String] key
1563
+ # @param [Fixnum] offset bit offset
1564
+ # @param [Fixnum] value bit value `0` or `1`
1565
+ # @return [Fixnum] the original bit value stored at `offset`
1397
1566
  def setbit(key, offset, value)
1398
- synchronize do
1399
- @client.call [:setbit, key, offset, value]
1567
+ synchronize do |client|
1568
+ client.call [:setbit, key, offset, value]
1400
1569
  end
1401
1570
  end
1402
1571
 
1403
- # Set the value and expiration of a key.
1572
+ # Set the time to live in seconds of a key.
1573
+ #
1574
+ # @param [String] key
1575
+ # @param [Fixnum] ttl
1576
+ # @param [String] value
1577
+ # @return `"OK"`
1404
1578
  def setex(key, ttl, value)
1405
- synchronize do
1406
- @client.call [:setex, key, ttl, value]
1579
+ synchronize do |client|
1580
+ client.call [:setex, key, ttl, value]
1581
+ end
1582
+ end
1583
+
1584
+ # Set the time to live in milliseconds of a key.
1585
+ #
1586
+ # @param [String] key
1587
+ # @param [Fixnum] ttl
1588
+ # @param [String] value
1589
+ # @return `"OK"`
1590
+ def psetex(key, ttl, value)
1591
+ synchronize do |client|
1592
+ client.call [:psetex, key, ttl, value]
1407
1593
  end
1408
1594
  end
1409
1595
 
1410
1596
  # Overwrite part of a string at key starting at the specified offset.
1597
+ #
1598
+ # @param [String] key
1599
+ # @param [Fixnum] offset byte offset
1600
+ # @param [String] value
1601
+ # @return [Fixnum] length of the string after it was modified
1411
1602
  def setrange(key, offset, value)
1412
- synchronize do
1413
- @client.call [:setrange, key, offset, value]
1603
+ synchronize do |client|
1604
+ client.call [:setrange, key, offset, value]
1414
1605
  end
1415
1606
  end
1416
1607
 
1417
- # Set multiple keys to multiple values.
1608
+ # Set one or more values.
1609
+ #
1610
+ # @example
1611
+ # redis.mset("key1", "v1", "key2", "v2")
1612
+ # # => "OK"
1613
+ #
1614
+ # @param [Array<String>] args array of keys and values
1615
+ # @return `"OK"`
1616
+ #
1617
+ # @see #mapped_mset
1418
1618
  def mset(*args)
1419
- synchronize do
1420
- @client.call [:mset, *args]
1619
+ synchronize do |client|
1620
+ client.call [:mset, *args]
1421
1621
  end
1422
1622
  end
1423
1623
 
1624
+ # Set one or more values.
1625
+ #
1626
+ # @example
1627
+ # redis.mapped_mset({ "f1" => "v1", "f2" => "v2" })
1628
+ # # => "OK"
1629
+ #
1630
+ # @param [Hash] hash keys mapping to values
1631
+ # @return `"OK"`
1632
+ #
1633
+ # @see #mset
1424
1634
  def mapped_mset(hash)
1425
1635
  mset(*hash.to_a.flatten)
1426
1636
  end
1427
1637
 
1428
- # Set multiple keys to multiple values, only if none of the keys exist.
1638
+ # Set one or more values, only if none of the keys exist.
1639
+ #
1640
+ # @example
1641
+ # redis.msetnx("key1", "v1", "key2", "v2")
1642
+ # # => true
1643
+ #
1644
+ # @param [Array<String>] args array of keys and values
1645
+ # @return [Boolean] whether or not all values were set
1646
+ #
1647
+ # @see #mapped_msetnx
1429
1648
  def msetnx(*args)
1430
- synchronize do
1431
- @client.call [:msetnx, *args]
1649
+ synchronize do |client|
1650
+ client.call [:msetnx, *args], &_boolify
1432
1651
  end
1433
1652
  end
1434
1653
 
1654
+ # Set one or more values, only if none of the keys exist.
1655
+ #
1656
+ # @example
1657
+ # redis.msetnx({ "key1" => "v1", "key2" => "v2" })
1658
+ # # => true
1659
+ #
1660
+ # @param [Hash] hash keys mapping to values
1661
+ # @return [Boolean] whether or not all values were set
1662
+ #
1663
+ # @see #msetnx
1435
1664
  def mapped_msetnx(hash)
1436
1665
  msetnx(*hash.to_a.flatten)
1437
1666
  end
1438
1667
 
1668
+ # Get the values of all the given keys.
1669
+ #
1670
+ # @example
1671
+ # redis.mapped_mget("key1", "key1")
1672
+ # # => { "key1" => "v1", "key2" => "v2" }
1673
+ #
1674
+ # @param [Array<String>] keys array of keys
1675
+ # @return [Hash] a hash mapping the specified keys to their values
1676
+ #
1677
+ # @see #mget
1439
1678
  def mapped_mget(*keys)
1440
1679
  mget(*keys) do |reply|
1441
1680
  if reply.kind_of?(Array)
@@ -1451,45 +1690,133 @@ class Redis
1451
1690
  end
1452
1691
 
1453
1692
  # Sort the elements in a list, set or sorted set.
1693
+ #
1694
+ # @example Retrieve the first 2 elements from an alphabetically sorted "list"
1695
+ # redis.sort("list", :order => "alpha", :limit => [0, 2])
1696
+ # # => ["a", "b"]
1697
+ # @example Store an alphabetically descending list in "target"
1698
+ # redis.sort("list", :order => "desc alpha", :store => "target")
1699
+ # # => 26
1700
+ #
1701
+ # @param [String] key
1702
+ # @param [Hash] options
1703
+ # - `:by => String`: use external key to sort elements by
1704
+ # - `:limit => [offset, count]`: skip `offset` elements, return a maximum
1705
+ # of `count` elements
1706
+ # - `:get => [String, Array<String>]`: single key or array of keys to
1707
+ # retrieve per element in the result
1708
+ # - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
1709
+ # - `:store => String`: key to store the result at
1710
+ #
1711
+ # @return [Array<String>, Array<Array<String>>, Fixnum]
1712
+ # - when `:get` is not specified, or holds a single element, an array of elements
1713
+ # - when `:get` is specified, and holds more than one element, an array of
1714
+ # elements where every element is an array with the result for every
1715
+ # element specified in `:get`
1716
+ # - when `:store` is specified, the number of elements in the stored result
1454
1717
  def sort(key, options = {})
1455
- command = CommandOptions.new(options) do |c|
1456
- c.value :by
1457
- c.splat :limit
1458
- c.multi :get
1459
- c.words :order
1460
- c.value :store
1461
- end
1718
+ args = []
1719
+
1720
+ by = options[:by]
1721
+ args.concat ["BY", by] if by
1722
+
1723
+ limit = options[:limit]
1724
+ args.concat ["LIMIT", *limit] if limit
1462
1725
 
1463
- synchronize do
1464
- @client.call [:sort, key, *command.to_a]
1726
+ get = Array(options[:get])
1727
+ args.concat ["GET"].product(get).flatten unless get.empty?
1728
+
1729
+ order = options[:order]
1730
+ args.concat order.split(" ") if order
1731
+
1732
+ store = options[:store]
1733
+ args.concat ["STORE", store] if store
1734
+
1735
+ synchronize do |client|
1736
+ client.call [:sort, key, *args] do |reply|
1737
+ if get.size > 1
1738
+ if reply
1739
+ reply.each_slice(get.size).to_a
1740
+ end
1741
+ else
1742
+ reply
1743
+ end
1744
+ end
1465
1745
  end
1466
1746
  end
1467
1747
 
1468
1748
  # Increment the integer value of a key by one.
1749
+ #
1750
+ # @example
1751
+ # redis.incr("value")
1752
+ # # => 6
1753
+ #
1754
+ # @param [String] key
1755
+ # @return [Fixnum] value after incrementing it
1469
1756
  def incr(key)
1470
- synchronize do
1471
- @client.call [:incr, key]
1757
+ synchronize do |client|
1758
+ client.call [:incr, key]
1472
1759
  end
1473
1760
  end
1474
1761
 
1475
- # Increment the integer value of a key by the given number.
1762
+ # Increment the integer value of a key by the given integer number.
1763
+ #
1764
+ # @example
1765
+ # redis.incrby("value", 5)
1766
+ # # => 10
1767
+ #
1768
+ # @param [String] key
1769
+ # @param [Fixnum] increment
1770
+ # @return [Fixnum] value after incrementing it
1476
1771
  def incrby(key, increment)
1477
- synchronize do
1478
- @client.call [:incrby, key, increment]
1772
+ synchronize do |client|
1773
+ client.call [:incrby, key, increment]
1774
+ end
1775
+ end
1776
+
1777
+ # Increment the numeric value of a key by the given float number.
1778
+ #
1779
+ # @example
1780
+ # redis.incrbyfloat("value", 1.23)
1781
+ # # => 1.23
1782
+ #
1783
+ # @param [String] key
1784
+ # @param [Float] increment
1785
+ # @return [Float] value after incrementing it
1786
+ def incrbyfloat(key, increment)
1787
+ synchronize do |client|
1788
+ client.call [:incrbyfloat, key, increment] do |reply|
1789
+ Float(reply) if reply
1790
+ end
1479
1791
  end
1480
1792
  end
1481
1793
 
1482
1794
  # Decrement the integer value of a key by one.
1795
+ #
1796
+ # @example
1797
+ # redis.decr("value")
1798
+ # # => 4
1799
+ #
1800
+ # @param [String] key
1801
+ # @return [Fixnum] value after decrementing it
1483
1802
  def decr(key)
1484
- synchronize do
1485
- @client.call [:decr, key]
1803
+ synchronize do |client|
1804
+ client.call [:decr, key]
1486
1805
  end
1487
1806
  end
1488
1807
 
1489
1808
  # Decrement the integer value of a key by the given number.
1809
+ #
1810
+ # @example
1811
+ # redis.decrby("value", 5)
1812
+ # # => 0
1813
+ #
1814
+ # @param [String] key
1815
+ # @param [Fixnum] decrement
1816
+ # @return [Fixnum] value after decrementing it
1490
1817
  def decrby(key, decrement)
1491
- synchronize do
1492
- @client.call [:decrby, key, decrement]
1818
+ synchronize do |client|
1819
+ client.call [:decrby, key, decrement]
1493
1820
  end
1494
1821
  end
1495
1822
 
@@ -1498,29 +1825,31 @@ class Redis
1498
1825
  # @param [String] key
1499
1826
  # @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
1500
1827
  def type(key)
1501
- synchronize do
1502
- @client.call [:type, key]
1828
+ synchronize do |client|
1829
+ client.call [:type, key]
1503
1830
  end
1504
1831
  end
1505
1832
 
1506
1833
  # Close the connection.
1834
+ #
1835
+ # @return [String] `OK`
1507
1836
  def quit
1508
- synchronize do
1837
+ synchronize do |client|
1509
1838
  begin
1510
- @client.call [:quit]
1839
+ client.call [:quit]
1511
1840
  rescue ConnectionError
1512
1841
  ensure
1513
- @client.disconnect
1842
+ client.disconnect
1514
1843
  end
1515
1844
  end
1516
1845
  end
1517
1846
 
1518
1847
  # Synchronously save the dataset to disk and then shut down the server.
1519
1848
  def shutdown
1520
- synchronize do
1521
- @client.without_reconnect do
1849
+ synchronize do |client|
1850
+ client.without_reconnect do
1522
1851
  begin
1523
- @client.call [:shutdown]
1852
+ client.call [:shutdown]
1524
1853
  rescue ConnectionError
1525
1854
  # This means Redis has probably exited.
1526
1855
  nil
@@ -1531,16 +1860,16 @@ class Redis
1531
1860
 
1532
1861
  # Make the server a slave of another instance, or promote it as master.
1533
1862
  def slaveof(host, port)
1534
- synchronize do
1535
- @client.call [:slaveof, host, port]
1863
+ synchronize do |client|
1864
+ client.call [:slaveof, host, port]
1536
1865
  end
1537
1866
  end
1538
1867
 
1539
1868
  def pipelined
1540
- synchronize do
1869
+ synchronize do |client|
1541
1870
  begin
1542
1871
  original, @client = @client, Pipeline.new
1543
- yield
1872
+ yield(self)
1544
1873
  original.call_pipeline(@client)
1545
1874
  ensure
1546
1875
  @client = original
@@ -1549,31 +1878,98 @@ class Redis
1549
1878
  end
1550
1879
 
1551
1880
  # Watch the given keys to determine execution of the MULTI/EXEC block.
1881
+ #
1882
+ # Using a block is optional, but is necessary for thread-safety.
1883
+ #
1884
+ # An `#unwatch` is automatically issued if an exception is raised within the
1885
+ # block that is a subclass of StandardError and is not a ConnectionError.
1886
+ #
1887
+ # @example With a block
1888
+ # redis.watch("key") do
1889
+ # if redis.get("key") == "some value"
1890
+ # redis.multi do |multi|
1891
+ # multi.set("key", "other value")
1892
+ # multi.incr("counter")
1893
+ # end
1894
+ # else
1895
+ # redis.unwatch
1896
+ # end
1897
+ # end
1898
+ # # => ["OK", 6]
1899
+ #
1900
+ # @example Without a block
1901
+ # redis.watch("key")
1902
+ # # => "OK"
1903
+ #
1904
+ # @param [String, Array<String>] keys one or more keys to watch
1905
+ # @return [Object] if using a block, returns the return value of the block
1906
+ # @return [String] if not using a block, returns `OK`
1907
+ #
1908
+ # @see #unwatch
1909
+ # @see #multi
1552
1910
  def watch(*keys)
1553
- synchronize do
1554
- @client.call [:watch, *keys]
1911
+ synchronize do |client|
1912
+ client.call [:watch, *keys]
1913
+
1914
+ if block_given?
1915
+ begin
1916
+ yield
1917
+ rescue ConnectionError
1918
+ raise
1919
+ rescue StandardError
1920
+ unwatch
1921
+ raise
1922
+ end
1923
+ end
1555
1924
  end
1556
1925
  end
1557
1926
 
1558
1927
  # Forget about all watched keys.
1928
+ #
1929
+ # @return [String] `OK`
1930
+ #
1931
+ # @see #watch
1932
+ # @see #multi
1559
1933
  def unwatch
1560
- synchronize do
1561
- @client.call [:unwatch]
1562
- end
1563
- end
1564
-
1565
- # Execute all commands issued after MULTI.
1566
- def exec
1567
- synchronize do
1568
- @client.call [:exec]
1934
+ synchronize do |client|
1935
+ client.call [:unwatch]
1569
1936
  end
1570
1937
  end
1571
1938
 
1572
1939
  # Mark the start of a transaction block.
1940
+ #
1941
+ # Passing a block is optional.
1942
+ #
1943
+ # @example With a block
1944
+ # redis.multi do |multi|
1945
+ # multi.set("key", "value")
1946
+ # multi.incr("counter")
1947
+ # end # => ["OK", 6]
1948
+ #
1949
+ # @example Without a block
1950
+ # redis.multi
1951
+ # # => "OK"
1952
+ # redis.set("key", "value")
1953
+ # # => "QUEUED"
1954
+ # redis.incr("counter")
1955
+ # # => "QUEUED"
1956
+ # redis.exec
1957
+ # # => ["OK", 6]
1958
+ #
1959
+ # @yield [multi] the commands that are called inside this block are cached
1960
+ # and written to the server upon returning from it
1961
+ # @yieldparam [Redis] multi `self`
1962
+ #
1963
+ # @return [String, Array<...>]
1964
+ # - when a block is not given, `OK`
1965
+ # - when a block is given, an array with replies
1966
+ #
1967
+ # @see #watch
1968
+ # @see #unwatch
1573
1969
  def multi
1574
- synchronize do
1970
+ synchronize do |client|
1575
1971
  if !block_given?
1576
- @client.call [:multi]
1972
+ client.call [:multi]
1577
1973
  else
1578
1974
  begin
1579
1975
  pipeline = Pipeline::Multi.new
@@ -1587,100 +1983,94 @@ class Redis
1587
1983
  end
1588
1984
  end
1589
1985
 
1986
+ # Execute all commands issued after MULTI.
1987
+ #
1988
+ # Only call this method when `#multi` was called **without** a block.
1989
+ #
1990
+ # @return [nil, Array<...>]
1991
+ # - when commands were not executed, `nil`
1992
+ # - when commands were executed, an array with their replies
1993
+ #
1994
+ # @see #multi
1995
+ # @see #discard
1996
+ def exec
1997
+ synchronize do |client|
1998
+ client.call [:exec]
1999
+ end
2000
+ end
2001
+
2002
+ # Discard all commands issued after MULTI.
2003
+ #
2004
+ # Only call this method when `#multi` was called **without** a block.
2005
+ #
2006
+ # @return `"OK"`
2007
+ #
2008
+ # @see #multi
2009
+ # @see #exec
2010
+ def discard
2011
+ synchronize do |client|
2012
+ client.call [:discard]
2013
+ end
2014
+ end
2015
+
1590
2016
  # Post a message to a channel.
1591
2017
  def publish(channel, message)
1592
- synchronize do
1593
- @client.call [:publish, channel, message]
2018
+ synchronize do |client|
2019
+ client.call [:publish, channel, message]
1594
2020
  end
1595
2021
  end
1596
2022
 
1597
2023
  def subscribed?
1598
- synchronize do
1599
- @client.kind_of? SubscribedClient
2024
+ synchronize do |client|
2025
+ client.kind_of? SubscribedClient
1600
2026
  end
1601
2027
  end
1602
2028
 
1603
2029
  # Stop listening for messages posted to the given channels.
1604
2030
  def unsubscribe(*channels)
1605
- synchronize do
2031
+ synchronize do |client|
1606
2032
  raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
1607
- @client.unsubscribe(*channels)
2033
+ client.unsubscribe(*channels)
1608
2034
  end
1609
2035
  end
1610
2036
 
1611
2037
  # Stop listening for messages posted to channels matching the given patterns.
1612
2038
  def punsubscribe(*channels)
1613
- synchronize do
2039
+ synchronize do |client|
1614
2040
  raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
1615
- @client.punsubscribe(*channels)
2041
+ client.punsubscribe(*channels)
1616
2042
  end
1617
2043
  end
1618
2044
 
1619
2045
  # Listen for messages published to the given channels.
1620
2046
  def subscribe(*channels, &block)
1621
- synchronize do
1622
- subscription(:subscribe, channels, block)
2047
+ synchronize do |client|
2048
+ _subscription(:subscribe, channels, block)
1623
2049
  end
1624
2050
  end
1625
2051
 
1626
2052
  # Listen for messages published to channels matching the given patterns.
1627
2053
  def psubscribe(*channels, &block)
1628
- synchronize do
1629
- subscription(:psubscribe, channels, block)
2054
+ synchronize do |client|
2055
+ _subscription(:psubscribe, channels, block)
1630
2056
  end
1631
2057
  end
1632
2058
 
1633
2059
  def id
1634
- synchronize do
1635
- @client.id
2060
+ synchronize do |client|
2061
+ client.id
1636
2062
  end
1637
2063
  end
1638
2064
 
1639
2065
  def inspect
1640
- synchronize do
1641
- "#<Redis client v#{Redis::VERSION} connected to #{id} (Redis v#{info["redis_version"]})>"
2066
+ synchronize do |client|
2067
+ "#<Redis client v#{Redis::VERSION} for #{client.id}>"
1642
2068
  end
1643
2069
  end
1644
2070
 
1645
2071
  def method_missing(command, *args)
1646
- synchronize do
1647
- @client.call [command, *args]
1648
- end
1649
- end
1650
-
1651
- class CommandOptions
1652
- def initialize(options)
1653
- @result = []
1654
- @options = options
1655
- yield(self)
1656
- end
1657
-
1658
- def bool(name)
1659
- insert(name) { |argument, value| [argument] }
1660
- end
1661
-
1662
- def value(name)
1663
- insert(name) { |argument, value| [argument, value] }
1664
- end
1665
-
1666
- def splat(name)
1667
- insert(name) { |argument, value| [argument, *value] }
1668
- end
1669
-
1670
- def multi(name)
1671
- insert(name) { |argument, value| [argument].product(Array(value)).flatten }
1672
- end
1673
-
1674
- def words(name)
1675
- insert(name) { |argument, value| value.split(" ") }
1676
- end
1677
-
1678
- def to_a
1679
- @result
1680
- end
1681
-
1682
- def insert(name)
1683
- @result += yield(name.to_s.upcase.gsub("_", ""), @options[name]) if @options[name]
2072
+ synchronize do |client|
2073
+ client.call [command, *args]
1684
2074
  end
1685
2075
  end
1686
2076
 
@@ -1695,7 +2085,17 @@ private
1695
2085
  }
1696
2086
  end
1697
2087
 
1698
- def subscription(method, channels, block)
2088
+ def _hashify
2089
+ lambda { |array|
2090
+ hash = Hash.new
2091
+ array.each_slice(2) do |field, value|
2092
+ hash[field] = value
2093
+ end
2094
+ hash
2095
+ }
2096
+ end
2097
+
2098
+ def _subscription(method, channels, block)
1699
2099
  return @client.call [method, *channels] if subscribed?
1700
2100
 
1701
2101
  begin