redis 4.1.4 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "connection/registry"
3
4
 
4
5
  # If a connection driver was required before this file, the array
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Redis
3
4
  module Connection
4
5
  module CommandHelper
5
-
6
6
  COMMAND_DELIMITER = "\r\n"
7
7
 
8
8
  def build_command(args)
@@ -29,7 +29,7 @@ class Redis
29
29
  command.join(COMMAND_DELIMITER)
30
30
  end
31
31
 
32
- protected
32
+ protected
33
33
 
34
34
  def encode(string)
35
35
  string.force_encoding(Encoding.default_external)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "registry"
3
4
  require_relative "../errors"
4
5
  require "hiredis/connection"
@@ -7,7 +8,6 @@ require "timeout"
7
8
  class Redis
8
9
  module Connection
9
10
  class Hiredis
10
-
11
11
  def self.connect(config)
12
12
  connection = ::Hiredis::Connection.new
13
13
  connect_timeout = (config.fetch(:connect_timeout, 0) * 1_000_000).to_i
@@ -32,7 +32,7 @@ class Redis
32
32
  end
33
33
 
34
34
  def connected?
35
- @connection && @connection.connected?
35
+ @connection&.connected?
36
36
  end
37
37
 
38
38
  def timeout=(timeout)
@@ -58,7 +58,7 @@ class Redis
58
58
  rescue Errno::EAGAIN
59
59
  raise TimeoutError
60
60
  rescue RuntimeError => err
61
- raise ProtocolError.new(err.message)
61
+ raise ProtocolError, err.message
62
62
  end
63
63
  end
64
64
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class Redis
3
4
  module Connection
4
-
5
5
  # Store a list of loaded connection drivers in the Connection module.
6
6
  # Redis::Client uses the last required driver by default, and will be aware
7
7
  # of the loaded connection drivers if the user chooses to override the
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "registry"
3
4
  require_relative "command_helper"
4
5
  require_relative "../errors"
@@ -14,8 +15,7 @@ end
14
15
  class Redis
15
16
  module Connection
16
17
  module SocketMixin
17
-
18
- CRLF = "\r\n".freeze
18
+ CRLF = "\r\n"
19
19
 
20
20
  def initialize(*args)
21
21
  super(*args)
@@ -25,46 +25,32 @@ class Redis
25
25
  end
26
26
 
27
27
  def timeout=(timeout)
28
- if timeout && timeout > 0
29
- @timeout = timeout
30
- else
31
- @timeout = nil
32
- end
28
+ @timeout = (timeout if timeout && timeout > 0)
33
29
  end
34
30
 
35
31
  def write_timeout=(timeout)
36
- if timeout && timeout > 0
37
- @write_timeout = timeout
38
- else
39
- @write_timeout = nil
40
- end
32
+ @write_timeout = (timeout if timeout && timeout > 0)
41
33
  end
42
34
 
43
35
  def read(nbytes)
44
36
  result = @buffer.slice!(0, nbytes)
45
37
 
46
- while result.bytesize < nbytes
47
- result << _read_from_socket(nbytes - result.bytesize)
48
- end
38
+ result << _read_from_socket(nbytes - result.bytesize) while result.bytesize < nbytes
49
39
 
50
40
  result
51
41
  end
52
42
 
53
43
  def gets
54
- crlf = nil
55
-
56
- while (crlf = @buffer.index(CRLF)) == nil
57
- @buffer << _read_from_socket(16384)
44
+ while (crlf = @buffer.index(CRLF)).nil?
45
+ @buffer << _read_from_socket(16_384)
58
46
  end
59
47
 
60
48
  @buffer.slice!(0, crlf + CRLF.bytesize)
61
49
  end
62
50
 
63
51
  def _read_from_socket(nbytes)
64
-
65
52
  begin
66
53
  read_nonblock(nbytes)
67
-
68
54
  rescue IO::WaitReadable
69
55
  if IO.select([self], nil, nil, @timeout)
70
56
  retry
@@ -78,7 +64,6 @@ class Redis
78
64
  raise Redis::TimeoutError
79
65
  end
80
66
  end
81
-
82
67
  rescue EOFError
83
68
  raise Errno::ECONNRESET
84
69
  end
@@ -86,7 +71,6 @@ class Redis
86
71
  def _write_to_socket(data)
87
72
  begin
88
73
  write_nonblock(data)
89
-
90
74
  rescue IO::WaitWritable
91
75
  if IO.select(nil, [self], nil, @write_timeout)
92
76
  retry
@@ -100,7 +84,6 @@ class Redis
100
84
  raise Redis::TimeoutError
101
85
  end
102
86
  end
103
-
104
87
  rescue EOFError
105
88
  raise Errno::ECONNRESET
106
89
  end
@@ -115,6 +98,7 @@ class Redis
115
98
 
116
99
  total_count += count
117
100
  return total_count if total_count >= length
101
+
118
102
  data = data.byteslice(count..-1)
119
103
  end
120
104
  end
@@ -125,7 +109,6 @@ class Redis
125
109
  require "timeout"
126
110
 
127
111
  class TCPSocket < ::TCPSocket
128
-
129
112
  include SocketMixin
130
113
 
131
114
  def self.connect(host, port, timeout)
@@ -141,7 +124,6 @@ class Redis
141
124
  if defined?(::UNIXSocket)
142
125
 
143
126
  class UNIXSocket < ::UNIXSocket
144
-
145
127
  include SocketMixin
146
128
 
147
129
  def self.connect(path, timeout)
@@ -159,7 +141,6 @@ class Redis
159
141
 
160
142
  def _read_from_socket(nbytes)
161
143
  readpartial(nbytes)
162
-
163
144
  rescue EOFError
164
145
  raise Errno::ECONNRESET
165
146
  end
@@ -170,19 +151,16 @@ class Redis
170
151
  else
171
152
 
172
153
  class TCPSocket < ::Socket
173
-
174
154
  include SocketMixin
175
155
 
176
- def self.connect_addrinfo(ai, port, timeout)
177
- sock = new(::Socket.const_get(ai[0]), Socket::SOCK_STREAM, 0)
178
- sockaddr = ::Socket.pack_sockaddr_in(port, ai[3])
156
+ def self.connect_addrinfo(addrinfo, port, timeout)
157
+ sock = new(::Socket.const_get(addrinfo[0]), Socket::SOCK_STREAM, 0)
158
+ sockaddr = ::Socket.pack_sockaddr_in(port, addrinfo[3])
179
159
 
180
160
  begin
181
161
  sock.connect_nonblock(sockaddr)
182
162
  rescue Errno::EINPROGRESS
183
- if IO.select(nil, [sock], nil, timeout) == nil
184
- raise TimeoutError
185
- end
163
+ raise TimeoutError if IO.select(nil, [sock], nil, timeout).nil?
186
164
 
187
165
  begin
188
166
  sock.connect_nonblock(sockaddr)
@@ -221,14 +199,13 @@ class Redis
221
199
  return connect_addrinfo(ai, port, timeout)
222
200
  rescue SystemCallError
223
201
  # Raise if this was our last attempt.
224
- raise if addrinfo.length == i+1
202
+ raise if addrinfo.length == i + 1
225
203
  end
226
204
  end
227
205
  end
228
206
  end
229
207
 
230
208
  class UNIXSocket < ::Socket
231
-
232
209
  include SocketMixin
233
210
 
234
211
  def self.connect(path, timeout)
@@ -238,9 +215,7 @@ class Redis
238
215
  begin
239
216
  sock.connect_nonblock(sockaddr)
240
217
  rescue Errno::EINPROGRESS
241
- if IO.select(nil, [sock], nil, timeout) == nil
242
- raise TimeoutError
243
- end
218
+ raise TimeoutError if IO.select(nil, [sock], nil, timeout).nil?
244
219
 
245
220
  begin
246
221
  sock.connect_nonblock(sockaddr)
@@ -263,7 +238,9 @@ class Redis
263
238
  tcp_sock = TCPSocket.connect(host, port, timeout)
264
239
 
265
240
  ctx = OpenSSL::SSL::SSLContext.new
266
- ctx.set_params(ssl_params) if ssl_params && !ssl_params.empty?
241
+
242
+ # The provided parameters are merged into OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
243
+ ctx.set_params(ssl_params || {})
267
244
 
268
245
  ssl_sock = new(tcp_sock, ctx)
269
246
  ssl_sock.hostname = host
@@ -290,7 +267,10 @@ class Redis
290
267
  end
291
268
  end
292
269
 
293
- unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE || (ctx.respond_to?(:verify_hostname) && !ctx.verify_hostname)
270
+ unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE || (
271
+ ctx.respond_to?(:verify_hostname) &&
272
+ !ctx.verify_hostname
273
+ )
294
274
  ssl_sock.post_connection_check(host)
295
275
  end
296
276
 
@@ -302,15 +282,16 @@ class Redis
302
282
  class Ruby
303
283
  include Redis::Connection::CommandHelper
304
284
 
305
- MINUS = "-".freeze
306
- PLUS = "+".freeze
307
- COLON = ":".freeze
308
- DOLLAR = "$".freeze
309
- ASTERISK = "*".freeze
285
+ MINUS = "-"
286
+ PLUS = "+"
287
+ COLON = ":"
288
+ DOLLAR = "$"
289
+ ASTERISK = "*"
310
290
 
311
291
  def self.connect(config)
312
292
  if config[:scheme] == "unix"
313
293
  raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
294
+
314
295
  sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
315
296
  elsif config[:scheme] == "rediss" || config[:ssl]
316
297
  sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
@@ -326,7 +307,7 @@ class Redis
326
307
  instance
327
308
  end
328
309
 
329
- if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| Socket.const_defined? c}
310
+ if %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? { |c| Socket.const_defined? c }
330
311
  def set_tcp_keepalive(keepalive)
331
312
  return unless keepalive.is_a?(Hash)
332
313
 
@@ -338,14 +319,13 @@ class Redis
338
319
 
339
320
  def get_tcp_keepalive
340
321
  {
341
- :time => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
342
- :intvl => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
343
- :probes => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int,
322
+ time: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
323
+ intvl: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
324
+ probes: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int
344
325
  }
345
326
  end
346
327
  else
347
- def set_tcp_keepalive(keepalive)
348
- end
328
+ def set_tcp_keepalive(keepalive); end
349
329
 
350
330
  def get_tcp_keepalive
351
331
  {
@@ -354,13 +334,12 @@ class Redis
354
334
  end
355
335
 
356
336
  # disables Nagle's Algorithm, prevents multiple round trips with MULTI
357
- if [:IPPROTO_TCP, :TCP_NODELAY].all?{|c| Socket.const_defined? c}
337
+ if %i[IPPROTO_TCP TCP_NODELAY].all? { |c| Socket.const_defined? c }
358
338
  def set_tcp_nodelay
359
339
  @sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
360
340
  end
361
341
  else
362
- def set_tcp_nodelay
363
- end
342
+ def set_tcp_nodelay; end
364
343
  end
365
344
 
366
345
  def initialize(sock)
@@ -368,7 +347,7 @@ class Redis
368
347
  end
369
348
 
370
349
  def connected?
371
- !! @sock
350
+ !!@sock
372
351
  end
373
352
 
374
353
  def disconnect
@@ -379,9 +358,7 @@ class Redis
379
358
  end
380
359
 
381
360
  def timeout=(timeout)
382
- if @sock.respond_to?(:timeout=)
383
- @sock.timeout = timeout
384
- end
361
+ @sock.timeout = timeout if @sock.respond_to?(:timeout=)
385
362
  end
386
363
 
387
364
  def write_timeout=(timeout)
@@ -396,7 +373,6 @@ class Redis
396
373
  line = @sock.gets
397
374
  reply_type = line.slice!(0, 1)
398
375
  format_reply(reply_type, line)
399
-
400
376
  rescue Errno::EAGAIN
401
377
  raise TimeoutError
402
378
  end
@@ -408,7 +384,7 @@ class Redis
408
384
  when COLON then format_integer_reply(line)
409
385
  when DOLLAR then format_bulk_reply(line)
410
386
  when ASTERISK then format_multi_bulk_reply(line)
411
- else raise ProtocolError.new(reply_type)
387
+ else raise ProtocolError, reply_type
412
388
  end
413
389
  end
414
390
 
@@ -427,6 +403,7 @@ class Redis
427
403
  def format_bulk_reply(line)
428
404
  bulklen = line.to_i
429
405
  return if bulklen == -1
406
+
430
407
  reply = encode(@sock.read(bulklen))
431
408
  @sock.read(2) # Discard CRLF.
432
409
  reply
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "command_helper"
3
4
  require_relative "registry"
4
5
  require_relative "../errors"
5
6
  require "em-synchrony"
6
7
  require "hiredis/reader"
7
8
 
9
+ Kernel.warn(
10
+ "The redis synchrony driver is deprecated and will be removed in redis-rb 5.0. " \
11
+ "We're looking for people to maintain it as a separate gem, see https://github.com/redis/redis-rb/issues/915"
12
+ )
13
+
8
14
  class Redis
9
15
  module Connection
10
16
  class RedisClient < EventMachine::Connection
@@ -47,9 +53,7 @@ class Redis
47
53
 
48
54
  def read
49
55
  @req = EventMachine::DefaultDeferrable.new
50
- if @timeout > 0
51
- @req.timeout(@timeout, :timeout)
52
- end
56
+ @req.timeout(@timeout, :timeout) if @timeout > 0
53
57
  EventMachine::Synchrony.sync @req
54
58
  end
55
59
 
@@ -106,7 +110,7 @@ class Redis
106
110
  end
107
111
 
108
112
  def connected?
109
- @connection && @connection.connected?
113
+ @connection&.connected?
110
114
  end
111
115
 
112
116
  def timeout=(timeout)
@@ -1,16 +1,17 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "hash_ring"
3
4
 
4
5
  class Redis
5
6
  class Distributed
6
-
7
7
  class CannotDistribute < RuntimeError
8
8
  def initialize(command)
9
9
  @command = command
10
10
  end
11
11
 
12
12
  def message
13
- "#{@command.to_s.upcase} cannot be used in Redis::Distributed because the keys involved need to be on the same server or because we cannot guarantee that the operation will be atomic."
13
+ "#{@command.to_s.upcase} cannot be used in Redis::Distributed because the keys involved need " \
14
+ "to be on the same server or because we cannot guarantee that the operation will be atomic."
14
15
  end
15
16
  end
16
17
 
@@ -34,9 +35,9 @@ class Redis
34
35
  end
35
36
 
36
37
  def add_node(options)
37
- options = { :url => options } if options.is_a?(String)
38
+ options = { url: options } if options.is_a?(String)
38
39
  options = @default_options.merge(options)
39
- @ring.add_node Redis.new( options )
40
+ @ring.add_node Redis.new(options)
40
41
  end
41
42
 
42
43
  # Change the selected database for the current connection.
@@ -145,12 +146,12 @@ class Redis
145
146
  end
146
147
 
147
148
  # Create a key using the serialized value, previously obtained using DUMP.
148
- def restore(key, ttl, serialized_value, options = {})
149
- node_for(key).restore(key, ttl, serialized_value, options)
149
+ def restore(key, ttl, serialized_value, **options)
150
+ node_for(key).restore(key, ttl, serialized_value, **options)
150
151
  end
151
152
 
152
153
  # Transfer a key from the connected instance to another instance.
153
- def migrate(key, options)
154
+ def migrate(_key, _options)
154
155
  raise CannotDistribute, :migrate
155
156
  end
156
157
 
@@ -171,8 +172,33 @@ class Redis
171
172
  end
172
173
 
173
174
  # Determine if a key exists.
174
- def exists(key)
175
- node_for(key).exists(key)
175
+ def exists(*args)
176
+ if !Redis.exists_returns_integer && args.size == 1
177
+ message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
178
+ "use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
179
+ "(#{::Kernel.caller(1, 1).first})\n"
180
+
181
+ if defined?(::Warning)
182
+ ::Warning.warn(message)
183
+ else
184
+ warn(message)
185
+ end
186
+ exists?(*args)
187
+ else
188
+ keys_per_node = args.group_by { |key| node_for(key) }
189
+ keys_per_node.inject(0) do |sum, (node, keys)|
190
+ sum + node._exists(*keys)
191
+ end
192
+ end
193
+ end
194
+
195
+ # Determine if any of the keys exists.
196
+ def exists?(*args)
197
+ keys_per_node = args.group_by { |key| node_for(key) }
198
+ keys_per_node.each do |node, keys|
199
+ return true if node.exists?(*keys)
200
+ end
201
+ false
176
202
  end
177
203
 
178
204
  # Find all keys matching the given pattern.
@@ -205,11 +231,11 @@ class Redis
205
231
  end
206
232
 
207
233
  # Sort the elements in a list, set or sorted set.
208
- def sort(key, options = {})
234
+ def sort(key, **options)
209
235
  keys = [key, options[:by], options[:store], *Array(options[:get])].compact
210
236
 
211
237
  ensure_same_node(:sort, keys) do |node|
212
- node.sort(key, options)
238
+ node.sort(key, **options)
213
239
  end
214
240
  end
215
241
 
@@ -244,8 +270,8 @@ class Redis
244
270
  end
245
271
 
246
272
  # Set the string value of a key.
247
- def set(key, value, options = {})
248
- node_for(key).set(key, value, options)
273
+ def set(key, value, **options)
274
+ node_for(key).set(key, value, **options)
249
275
  end
250
276
 
251
277
  # Set the time to live in seconds of a key.
@@ -264,20 +290,20 @@ class Redis
264
290
  end
265
291
 
266
292
  # Set multiple keys to multiple values.
267
- def mset(*args)
293
+ def mset(*_args)
268
294
  raise CannotDistribute, :mset
269
295
  end
270
296
 
271
- def mapped_mset(hash)
297
+ def mapped_mset(_hash)
272
298
  raise CannotDistribute, :mapped_mset
273
299
  end
274
300
 
275
301
  # Set multiple keys to multiple values, only if none of the keys exist.
276
- def msetnx(*args)
302
+ def msetnx(*_args)
277
303
  raise CannotDistribute, :msetnx
278
304
  end
279
305
 
280
- def mapped_msetnx(hash)
306
+ def mapped_msetnx(_hash)
281
307
  raise CannotDistribute, :mapped_msetnx
282
308
  end
283
309
 
@@ -336,7 +362,7 @@ class Redis
336
362
  end
337
363
 
338
364
  # Return the position of the first bit set to 1 or 0 in a string.
339
- def bitpos(key, bit, start=nil, stop=nil)
365
+ def bitpos(key, bit, start = nil, stop = nil)
340
366
  node_for(key).bitpos(key, bit, start, stop)
341
367
  end
342
368
 
@@ -354,7 +380,7 @@ class Redis
354
380
  get(key)
355
381
  end
356
382
 
357
- def []=(key,value)
383
+ def []=(key, value)
358
384
  set(key, value)
359
385
  end
360
386
 
@@ -439,15 +465,9 @@ class Redis
439
465
 
440
466
  # Pop a value from a list, push it to another list and return it; or block
441
467
  # until one is available.
442
- def brpoplpush(source, destination, options = {})
443
- case options
444
- when Integer
445
- # Issue deprecation notice in obnoxious mode...
446
- options = { :timeout => options }
447
- end
448
-
468
+ def brpoplpush(source, destination, deprecated_timeout = 0, **options)
449
469
  ensure_same_node(:brpoplpush, [source, destination]) do |node|
450
- node.brpoplpush(source, destination, options)
470
+ node.brpoplpush(source, destination, deprecated_timeout, **options)
451
471
  end
452
472
  end
453
473
 
@@ -524,13 +544,13 @@ class Redis
524
544
  end
525
545
 
526
546
  # Scan a set
527
- def sscan(key, cursor, options={})
528
- node_for(key).sscan(key, cursor, options)
547
+ def sscan(key, cursor, **options)
548
+ node_for(key).sscan(key, cursor, **options)
529
549
  end
530
550
 
531
551
  # Scan a set and return an enumerator
532
- def sscan_each(key, options={}, &block)
533
- node_for(key).sscan_each(key, options, &block)
552
+ def sscan_each(key, **options, &block)
553
+ node_for(key).sscan_each(key, **options, &block)
534
554
  end
535
555
 
536
556
  # Subtract multiple sets.
@@ -585,6 +605,7 @@ class Redis
585
605
  def zadd(key, *args)
586
606
  node_for(key).zadd(key, *args)
587
607
  end
608
+ ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
588
609
 
589
610
  # Increment the score of a member in a sorted set.
590
611
  def zincrby(key, increment, member)
@@ -602,14 +623,14 @@ class Redis
602
623
  end
603
624
 
604
625
  # Return a range of members in a sorted set, by index.
605
- def zrange(key, start, stop, options = {})
606
- node_for(key).zrange(key, start, stop, options)
626
+ def zrange(key, start, stop, **options)
627
+ node_for(key).zrange(key, start, stop, **options)
607
628
  end
608
629
 
609
630
  # Return a range of members in a sorted set, by index, with scores ordered
610
631
  # from high to low.
611
- def zrevrange(key, start, stop, options = {})
612
- node_for(key).zrevrange(key, start, stop, options)
632
+ def zrevrange(key, start, stop, **options)
633
+ node_for(key).zrevrange(key, start, stop, **options)
613
634
  end
614
635
 
615
636
  # Determine the index of a member in a sorted set.
@@ -629,14 +650,14 @@ class Redis
629
650
  end
630
651
 
631
652
  # Return a range of members in a sorted set, by score.
632
- def zrangebyscore(key, min, max, options = {})
633
- node_for(key).zrangebyscore(key, min, max, options)
653
+ def zrangebyscore(key, min, max, **options)
654
+ node_for(key).zrangebyscore(key, min, max, **options)
634
655
  end
635
656
 
636
657
  # Return a range of members in a sorted set, by score, with scores ordered
637
658
  # from high to low.
638
- def zrevrangebyscore(key, max, min, options = {})
639
- node_for(key).zrevrangebyscore(key, max, min, options)
659
+ def zrevrangebyscore(key, max, min, **options)
660
+ node_for(key).zrevrangebyscore(key, max, min, **options)
640
661
  end
641
662
 
642
663
  # Remove all members in a sorted set within the given scores.
@@ -651,16 +672,16 @@ class Redis
651
672
 
652
673
  # Intersect multiple sorted sets and store the resulting sorted set in a new
653
674
  # key.
654
- def zinterstore(destination, keys, options = {})
675
+ def zinterstore(destination, keys, **options)
655
676
  ensure_same_node(:zinterstore, [destination] + keys) do |node|
656
- node.zinterstore(destination, keys, options)
677
+ node.zinterstore(destination, keys, **options)
657
678
  end
658
679
  end
659
680
 
660
681
  # Add multiple sorted sets and store the resulting sorted set in a new key.
661
- def zunionstore(destination, keys, options = {})
682
+ def zunionstore(destination, keys, **options)
662
683
  ensure_same_node(:zunionstore, [destination] + keys) do |node|
663
- node.zunionstore(destination, keys, options)
684
+ node.zunionstore(destination, keys, **options)
664
685
  end
665
686
  end
666
687
 
@@ -669,9 +690,9 @@ class Redis
669
690
  node_for(key).hlen(key)
670
691
  end
671
692
 
672
- # Set the string value of a hash field.
673
- def hset(key, field, value)
674
- node_for(key).hset(key, field, value)
693
+ # Set multiple hash fields to multiple values.
694
+ def hset(key, *attrs)
695
+ node_for(key).hset(key, *attrs)
675
696
  end
676
697
 
677
698
  # Set the value of a hash field, only if the field does not exist.
@@ -743,7 +764,7 @@ class Redis
743
764
  end
744
765
 
745
766
  def subscribed?
746
- !! @subscribed_node
767
+ !!@subscribed_node
747
768
  end
748
769
 
749
770
  # Listen for messages published to the given channels.
@@ -761,7 +782,8 @@ class Redis
761
782
 
762
783
  # Stop listening for messages posted to the given channels.
763
784
  def unsubscribe(*channels)
764
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
785
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
786
+
765
787
  @subscribed_node.unsubscribe(*channels)
766
788
  end
767
789
 
@@ -777,7 +799,7 @@ class Redis
777
799
  end
778
800
 
779
801
  # Watch the given keys to determine execution of the MULTI/EXEC block.
780
- def watch(*keys)
802
+ def watch(*_keys)
781
803
  raise CannotDistribute, :watch
782
804
  end
783
805
 
@@ -861,7 +883,7 @@ class Redis
861
883
  self.class.new(@node_configs, @default_options)
862
884
  end
863
885
 
864
- protected
886
+ protected
865
887
 
866
888
  def on_each_node(command, *args)
867
889
  nodes.map do |node|