redis 3.0.0.rc1 → 3.0.0.rc2

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