redis 4.8.0 → 5.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -1
  3. data/README.md +75 -161
  4. data/lib/redis/client.rb +77 -616
  5. data/lib/redis/commands/bitmaps.rb +4 -1
  6. data/lib/redis/commands/cluster.rb +1 -18
  7. data/lib/redis/commands/connection.rb +5 -10
  8. data/lib/redis/commands/geo.rb +3 -3
  9. data/lib/redis/commands/hashes.rb +9 -6
  10. data/lib/redis/commands/hyper_log_log.rb +1 -1
  11. data/lib/redis/commands/keys.rb +5 -23
  12. data/lib/redis/commands/lists.rb +20 -25
  13. data/lib/redis/commands/pubsub.rb +7 -25
  14. data/lib/redis/commands/server.rb +15 -15
  15. data/lib/redis/commands/sets.rb +31 -40
  16. data/lib/redis/commands/sorted_sets.rb +18 -12
  17. data/lib/redis/commands/streams.rb +13 -11
  18. data/lib/redis/commands/strings.rb +18 -17
  19. data/lib/redis/commands/transactions.rb +7 -31
  20. data/lib/redis/commands.rb +1 -6
  21. data/lib/redis/distributed.rb +86 -64
  22. data/lib/redis/errors.rb +14 -50
  23. data/lib/redis/hash_ring.rb +26 -26
  24. data/lib/redis/pipeline.rb +43 -222
  25. data/lib/redis/subscribe.rb +23 -15
  26. data/lib/redis/version.rb +1 -1
  27. data/lib/redis.rb +89 -184
  28. metadata +9 -53
  29. data/lib/redis/cluster/command.rb +0 -79
  30. data/lib/redis/cluster/command_loader.rb +0 -33
  31. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  32. data/lib/redis/cluster/node.rb +0 -120
  33. data/lib/redis/cluster/node_key.rb +0 -31
  34. data/lib/redis/cluster/node_loader.rb +0 -34
  35. data/lib/redis/cluster/option.rb +0 -100
  36. data/lib/redis/cluster/slot.rb +0 -86
  37. data/lib/redis/cluster/slot_loader.rb +0 -46
  38. data/lib/redis/cluster.rb +0 -315
  39. data/lib/redis/connection/command_helper.rb +0 -41
  40. data/lib/redis/connection/hiredis.rb +0 -68
  41. data/lib/redis/connection/registry.rb +0 -13
  42. data/lib/redis/connection/ruby.rb +0 -437
  43. data/lib/redis/connection/synchrony.rb +0 -148
  44. data/lib/redis/connection.rb +0 -11
@@ -39,7 +39,10 @@ class Redis
39
39
  # @param [String, Array<String>] keys one or more source keys to perform `operation`
40
40
  # @return [Integer] the length of the string stored in `destkey`
41
41
  def bitop(operation, destkey, *keys)
42
- send_command([:bitop, operation, destkey, *keys])
42
+ keys.flatten!(1)
43
+ command = [:bitop, operation, destkey]
44
+ command.concat(keys)
45
+ send_command(command)
43
46
  end
44
47
 
45
48
  # Return the position of the first bit set to 1 or 0 in a string.
@@ -12,24 +12,7 @@ class Redis
12
12
  #
13
13
  # @return [Object] depends on the subcommand
14
14
  def cluster(subcommand, *args)
15
- subcommand = subcommand.to_s.downcase
16
- block = case subcommand
17
- when 'slots'
18
- HashifyClusterSlots
19
- when 'nodes'
20
- HashifyClusterNodes
21
- when 'slaves'
22
- HashifyClusterSlaves
23
- when 'info'
24
- HashifyInfo
25
- else
26
- Noop
27
- end
28
-
29
- # @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
30
- block = Noop unless @cluster_mode
31
-
32
- send_command([:cluster, subcommand] + args, &block)
15
+ send_command([:cluster, subcommand] + args)
33
16
  end
34
17
 
35
18
  # Sends `ASKING` command to random node and returns its reply.
@@ -34,10 +34,7 @@ class Redis
34
34
  # @param [Integer] db zero-based index of the DB to use (0 to 15)
35
35
  # @return [String] `OK`
36
36
  def select(db)
37
- synchronize do |client|
38
- client.db = db
39
- client.call([:select, db])
40
- end
37
+ send_command([:select, db])
41
38
  end
42
39
 
43
40
  # Close the connection.
@@ -45,12 +42,10 @@ class Redis
45
42
  # @return [String] `OK`
46
43
  def quit
47
44
  synchronize do |client|
48
- begin
49
- client.call([:quit])
50
- rescue ConnectionError
51
- ensure
52
- client.disconnect
53
- end
45
+ client.call_v([:quit])
46
+ rescue ConnectionError
47
+ ensure
48
+ client.close
54
49
  end
55
50
  end
56
51
  end
@@ -74,9 +74,9 @@ class Redis
74
74
  private
75
75
 
76
76
  def _geoarguments(*args, options: nil, sort: nil, count: nil)
77
- args.push sort if sort
78
- args.push 'count', count if count
79
- args.push options if options
77
+ args << sort if sort
78
+ args << 'COUNT' << Integer(count) if count
79
+ args << options if options
80
80
  args
81
81
  end
82
82
  end
@@ -63,7 +63,7 @@ class Redis
63
63
  #
64
64
  # @see #hmset
65
65
  def mapped_hmset(key, hash)
66
- hmset(key, hash.to_a.flatten)
66
+ hmset(key, hash.flatten)
67
67
  end
68
68
 
69
69
  # Get the value of a hash field.
@@ -87,7 +87,8 @@ class Redis
87
87
  #
88
88
  # @see #mapped_hmget
89
89
  def hmget(key, *fields, &blk)
90
- send_command([:hmget, key] + fields, &blk)
90
+ fields.flatten!(1)
91
+ send_command([:hmget, key].concat(fields), &blk)
91
92
  end
92
93
 
93
94
  # Get the values of all the given hash fields.
@@ -102,7 +103,8 @@ class Redis
102
103
  #
103
104
  # @see #hmget
104
105
  def mapped_hmget(key, *fields)
105
- hmget(key, *fields) do |reply|
106
+ fields.flatten!(1)
107
+ hmget(key, fields) do |reply|
106
108
  if reply.is_a?(Array)
107
109
  Hash[fields.zip(reply)]
108
110
  else
@@ -152,7 +154,8 @@ class Redis
152
154
  # @param [String, Array<String>] field
153
155
  # @return [Integer] the number of fields that were removed from the hash
154
156
  def hdel(key, *fields)
155
- send_command([:hdel, key, *fields])
157
+ fields.flatten!(1)
158
+ send_command([:hdel, key].concat(fields))
156
159
  end
157
160
 
158
161
  # Determine if a hash field exists.
@@ -171,7 +174,7 @@ class Redis
171
174
  # @param [Integer] increment
172
175
  # @return [Integer] value of the field after incrementing it
173
176
  def hincrby(key, field, increment)
174
- send_command([:hincrby, key, field, increment])
177
+ send_command([:hincrby, key, field, Integer(increment)])
175
178
  end
176
179
 
177
180
  # Increment the numeric value of a hash field by the given float number.
@@ -181,7 +184,7 @@ class Redis
181
184
  # @param [Float] increment
182
185
  # @return [Float] value of the field after incrementing it
183
186
  def hincrbyfloat(key, field, increment)
184
- send_command([:hincrbyfloat, key, field, increment], &Floatify)
187
+ send_command([:hincrbyfloat, key, field, Float(increment)], &Floatify)
185
188
  end
186
189
 
187
190
  # Get all the fields in a hash.
@@ -20,7 +20,7 @@ class Redis
20
20
  # @param [String, Array<String>] keys
21
21
  # @return [Integer]
22
22
  def pfcount(*keys)
23
- send_command([:pfcount] + keys)
23
+ send_command([:pfcount] + keys.flatten(1))
24
24
  end
25
25
 
26
26
  # Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
@@ -76,7 +76,7 @@ class Redis
76
76
  # - `:lt => true`: Set expiry only when the new expiry is less than current one.
77
77
  # @return [Boolean] whether the timeout was set or not
78
78
  def expire(key, seconds, nx: nil, xx: nil, gt: nil, lt: nil)
79
- args = [:expire, key, seconds]
79
+ args = [:expire, key, Integer(seconds)]
80
80
  args << "NX" if nx
81
81
  args << "XX" if xx
82
82
  args << "GT" if gt
@@ -96,7 +96,7 @@ class Redis
96
96
  # - `:lt => true`: Set expiry only when the new expiry is less than current one.
97
97
  # @return [Boolean] whether the timeout was set or not
98
98
  def expireat(key, unix_time, nx: nil, xx: nil, gt: nil, lt: nil)
99
- args = [:expireat, key, unix_time]
99
+ args = [:expireat, key, Integer(unix_time)]
100
100
  args << "NX" if nx
101
101
  args << "XX" if xx
102
102
  args << "GT" if gt
@@ -132,7 +132,7 @@ class Redis
132
132
  # - `:lt => true`: Set expiry only when the new expiry is less than current one.
133
133
  # @return [Boolean] whether the timeout was set or not
134
134
  def pexpire(key, milliseconds, nx: nil, xx: nil, gt: nil, lt: nil)
135
- args = [:pexpire, key, milliseconds]
135
+ args = [:pexpire, key, Integer(milliseconds)]
136
136
  args << "NX" if nx
137
137
  args << "XX" if xx
138
138
  args << "GT" if gt
@@ -152,7 +152,7 @@ class Redis
152
152
  # - `:lt => true`: Set expiry only when the new expiry is less than current one.
153
153
  # @return [Boolean] whether the timeout was set or not
154
154
  def pexpireat(key, ms_unix_time, nx: nil, xx: nil, gt: nil, lt: nil)
155
- args = [:pexpireat, key, ms_unix_time]
155
+ args = [:pexpireat, key, Integer(ms_unix_time)]
156
156
  args << "NX" if nx
157
157
  args << "XX" if xx
158
158
  args << "GT" if gt
@@ -249,24 +249,6 @@ class Redis
249
249
  # @param [String, Array<String>] keys
250
250
  # @return [Integer]
251
251
  def exists(*keys)
252
- if !Redis.exists_returns_integer && keys.size == 1
253
- if Redis.exists_returns_integer.nil?
254
- message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
255
- "should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
256
- "true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
257
- "`Redis.exists_returns_integer = false`, but this option will be removed in 5.0.0. " \
258
- "(#{::Kernel.caller(1, 1).first})\n"
259
-
260
- ::Redis.deprecate!(message)
261
- end
262
-
263
- exists?(*keys)
264
- else
265
- _exists(*keys)
266
- end
267
- end
268
-
269
- def _exists(*keys)
270
252
  send_command([:exists, *keys])
271
253
  end
272
254
 
@@ -445,7 +427,7 @@ class Redis
445
427
 
446
428
  args << cursor
447
429
  args << "MATCH" << match if match
448
- args << "COUNT" << count if count
430
+ args << "COUNT" << Integer(count) if count
449
431
  args << "TYPE" << type if type
450
432
 
451
433
  send_command([command] + args, &block)
@@ -48,7 +48,7 @@ class Redis
48
48
  # @param [String, Symbol] where_destination where to push the element to the source list
49
49
  # e.g. 'LEFT' - to head, 'RIGHT' - to tail
50
50
  # @param [Hash] options
51
- # - `:timeout => Numeric`: timeout in seconds, defaults to no timeout
51
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
52
52
  #
53
53
  # @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
54
54
  #
@@ -99,10 +99,10 @@ class Redis
99
99
  #
100
100
  # @param [String] key
101
101
  # @param [Integer] count number of elements to remove
102
- # @return [String, Array<String>] the values of the first elements
102
+ # @return [nil, String, Array<String>] the values of the first elements
103
103
  def lpop(key, count = nil)
104
104
  command = [:lpop, key]
105
- command << count if count
105
+ command << Integer(count) if count
106
106
  send_command(command)
107
107
  end
108
108
 
@@ -110,10 +110,10 @@ class Redis
110
110
  #
111
111
  # @param [String] key
112
112
  # @param [Integer] count number of elements to remove
113
- # @return [String, Array<String>] the values of the last elements
113
+ # @return [nil, String, Array<String>] the values of the last elements
114
114
  def rpop(key, count = nil)
115
115
  command = [:rpop, key]
116
- command << count if count
116
+ command << Integer(count) if count
117
117
  send_command(command)
118
118
  end
119
119
 
@@ -142,7 +142,7 @@ class Redis
142
142
  # @param [String, Array<String>] keys one or more keys to perform the
143
143
  # blocking pop on
144
144
  # @param [Hash] options
145
- # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
145
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
146
146
  #
147
147
  # @return [nil, [String, String]]
148
148
  # - `nil` when the operation timed out
@@ -156,7 +156,7 @@ class Redis
156
156
  # @param [String, Array<String>] keys one or more keys to perform the
157
157
  # blocking pop on
158
158
  # @param [Hash] options
159
- # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
159
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
160
160
  #
161
161
  # @return [nil, [String, String]]
162
162
  # - `nil` when the operation timed out
@@ -173,12 +173,12 @@ class Redis
173
173
  # @param [String] source source key
174
174
  # @param [String] destination destination key
175
175
  # @param [Hash] options
176
- # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
176
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
177
177
  #
178
178
  # @return [nil, String]
179
179
  # - `nil` when the operation timed out
180
180
  # - the element was popped and pushed otherwise
181
- def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
181
+ def brpoplpush(source, destination, timeout: 0)
182
182
  command = [:brpoplpush, source, destination, timeout]
183
183
  send_blocking_command(command, timeout)
184
184
  end
@@ -189,7 +189,7 @@ class Redis
189
189
  # @param [Integer] index
190
190
  # @return [String]
191
191
  def lindex(key, index)
192
- send_command([:lindex, key, index])
192
+ send_command([:lindex, key, Integer(index)])
193
193
  end
194
194
 
195
195
  # Insert an element before or after another element in a list.
@@ -211,7 +211,7 @@ class Redis
211
211
  # @param [Integer] stop stop index
212
212
  # @return [Array<String>]
213
213
  def lrange(key, start, stop)
214
- send_command([:lrange, key, start, stop])
214
+ send_command([:lrange, key, Integer(start), Integer(stop)])
215
215
  end
216
216
 
217
217
  # Remove elements from a list.
@@ -224,7 +224,7 @@ class Redis
224
224
  # @param [String] value
225
225
  # @return [Integer] the number of removed elements
226
226
  def lrem(key, count, value)
227
- send_command([:lrem, key, count, value])
227
+ send_command([:lrem, key, Integer(count), value])
228
228
  end
229
229
 
230
230
  # Set the value of an element in a list by its index.
@@ -234,7 +234,7 @@ class Redis
234
234
  # @param [String] value
235
235
  # @return [String] `OK`
236
236
  def lset(key, index, value)
237
- send_command([:lset, key, index, value])
237
+ send_command([:lset, key, Integer(index), value])
238
238
  end
239
239
 
240
240
  # Trim a list to the specified range.
@@ -244,7 +244,7 @@ class Redis
244
244
  # @param [Integer] stop stop index
245
245
  # @return [String] `OK`
246
246
  def ltrim(key, start, stop)
247
- send_command([:ltrim, key, start, stop])
247
+ send_command([:ltrim, key, Integer(start), Integer(stop)])
248
248
  end
249
249
 
250
250
  private
@@ -253,21 +253,16 @@ class Redis
253
253
  timeout = if args.last.is_a?(Hash)
254
254
  options = args.pop
255
255
  options[:timeout]
256
- elsif args.last.respond_to?(:to_int)
257
- last_arg = args.pop
258
- ::Redis.deprecate!(
259
- "Passing the timeout as a positional argument is deprecated, it should be passed as a keyword argument:\n" \
260
- " redis.#{cmd}(#{args.map(&:inspect).join(', ')}, timeout: #{last_arg.to_int})" \
261
- "(called from: #{caller(2, 1).first})"
262
- )
263
- last_arg.to_int
264
256
  end
265
257
 
266
258
  timeout ||= 0
259
+ unless timeout.is_a?(Integer) || timeout.is_a?(Float)
260
+ raise ArgumentError, "timeout must be an Integer or Float, got: #{timeout.class}"
261
+ end
267
262
 
268
- keys = args.flatten
269
-
270
- command = [cmd, keys, timeout]
263
+ args.flatten!(1)
264
+ command = [cmd].concat(args)
265
+ command << timeout
271
266
  send_blocking_command(command, timeout, &blk)
272
267
  end
273
268
 
@@ -9,57 +9,39 @@ class Redis
9
9
  end
10
10
 
11
11
  def subscribed?
12
- synchronize do |client|
13
- client.is_a? SubscribedClient
14
- end
12
+ !@subscription_client.nil?
15
13
  end
16
14
 
17
15
  # Listen for messages published to the given channels.
18
16
  def subscribe(*channels, &block)
19
- synchronize do |_client|
20
- _subscription(:subscribe, 0, channels, block)
21
- end
17
+ _subscription(:subscribe, 0, channels, block)
22
18
  end
23
19
 
24
20
  # Listen for messages published to the given channels. Throw a timeout error
25
21
  # if there is no messages for a timeout period.
26
22
  def subscribe_with_timeout(timeout, *channels, &block)
27
- synchronize do |_client|
28
- _subscription(:subscribe_with_timeout, timeout, channels, block)
29
- end
23
+ _subscription(:subscribe_with_timeout, timeout, channels, block)
30
24
  end
31
25
 
32
26
  # Stop listening for messages posted to the given channels.
33
27
  def unsubscribe(*channels)
34
- synchronize do |client|
35
- raise "Can't unsubscribe if not subscribed." unless subscribed?
36
-
37
- client.unsubscribe(*channels)
38
- end
28
+ _subscription(:unsubscribe, 0, channels, nil)
39
29
  end
40
30
 
41
31
  # Listen for messages published to channels matching the given patterns.
42
32
  def psubscribe(*channels, &block)
43
- synchronize do |_client|
44
- _subscription(:psubscribe, 0, channels, block)
45
- end
33
+ _subscription(:psubscribe, 0, channels, block)
46
34
  end
47
35
 
48
36
  # Listen for messages published to channels matching the given patterns.
49
37
  # Throw a timeout error if there is no messages for a timeout period.
50
38
  def psubscribe_with_timeout(timeout, *channels, &block)
51
- synchronize do |_client|
52
- _subscription(:psubscribe_with_timeout, timeout, channels, block)
53
- end
39
+ _subscription(:psubscribe_with_timeout, timeout, channels, block)
54
40
  end
55
41
 
56
42
  # Stop listening for messages posted to channels matching the given patterns.
57
43
  def punsubscribe(*channels)
58
- synchronize do |client|
59
- raise "Can't unsubscribe if not subscribed." unless subscribed?
60
-
61
- client.punsubscribe(*channels)
62
- end
44
+ _subscription(:punsubscribe, 0, channels, nil)
63
45
  end
64
46
 
65
47
  # Inspect the state of the Pub/Sub subsystem.
@@ -36,7 +36,7 @@ class Redis
36
36
  #
37
37
  # @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
38
38
  # @return [String, Hash] depends on subcommand
39
- def client(subcommand = nil, *args)
39
+ def client(subcommand, *args)
40
40
  send_command([:client, subcommand] + args) do |reply|
41
41
  if subcommand.to_s == "list"
42
42
  reply.lines.map do |line|
@@ -117,9 +117,13 @@ class Redis
117
117
  #
118
118
  # @yield a block to be called for every line of output
119
119
  # @yieldparam [String] line timestamp and command that was executed
120
- def monitor(&block)
120
+ def monitor
121
121
  synchronize do |client|
122
- client.call_loop([:monitor], &block)
122
+ client = client.pubsub
123
+ client.call_v([:monitor])
124
+ loop do
125
+ yield client.next_event
126
+ end
123
127
  end
124
128
  end
125
129
 
@@ -133,13 +137,11 @@ class Redis
133
137
  # Synchronously save the dataset to disk and then shut down the server.
134
138
  def shutdown
135
139
  synchronize do |client|
136
- client.with_reconnect(false) do
137
- begin
138
- client.call([:shutdown])
139
- rescue ConnectionError
140
- # This means Redis has probably exited.
141
- nil
142
- end
140
+ client.disable_reconnection do
141
+ client.call_v([:shutdown])
142
+ rescue ConnectionError
143
+ # This means Redis has probably exited.
144
+ nil
143
145
  end
144
146
  end
145
147
  end
@@ -155,11 +157,9 @@ class Redis
155
157
  # @param [Integer] length maximum number of entries to return
156
158
  # @return [Array<String>, Integer, String] depends on subcommand
157
159
  def slowlog(subcommand, length = nil)
158
- synchronize do |client|
159
- args = [:slowlog, subcommand]
160
- args << length if length
161
- client.call args
162
- end
160
+ args = [:slowlog, subcommand]
161
+ args << Integer(length) if length
162
+ send_command(args)
163
163
  end
164
164
 
165
165
  # Internal command used for replication.
@@ -15,56 +15,40 @@ class Redis
15
15
  #
16
16
  # @param [String] key
17
17
  # @param [String, Array<String>] member one member, or array of members
18
- # @return [Boolean, Integer] `Boolean` when a single member is specified,
19
- # holding whether or not adding the member succeeded, or `Integer` when an
20
- # array of members is specified, holding the number of members that were
21
- # successfully added
22
- def sadd(key, member)
23
- block = if Redis.sadd_returns_boolean && !member.is_a?(Array)
24
- ::Redis.deprecate!(
25
- "Redis#sadd will always return an Integer in Redis 5.0.0. Use Redis#sadd? instead." \
26
- "(called from: #{caller(1, 1).first})"
27
- )
28
- Boolify
29
- end
30
- send_command([:sadd, key, member], &block)
18
+ # @return [Integer] The number of members that were successfully added
19
+ def sadd(key, *members)
20
+ members.flatten!(1)
21
+ send_command([:sadd, key].concat(members))
31
22
  end
32
23
 
33
24
  # Add one or more members to a set.
34
25
  #
35
26
  # @param [String] key
36
27
  # @param [String, Array<String>] member one member, or array of members
37
- # @return [Boolean] Whether or not at least one member was added.
38
- def sadd?(key, member)
39
- send_command([:sadd, key, member], &Boolify)
28
+ # @return [Boolean] Wether at least one member was successfully added.
29
+ def sadd?(key, *members)
30
+ members.flatten!(1)
31
+ send_command([:sadd, key].concat(members), &Boolify)
40
32
  end
41
33
 
42
34
  # Remove one or more members from a set.
43
35
  #
44
36
  # @param [String] key
45
37
  # @param [String, Array<String>] member one member, or array of members
46
- # @return [Boolean, Integer] `Boolean` when a single member is specified,
47
- # holding whether or not removing the member succeeded, or `Integer` when an
48
- # array of members is specified, holding the number of members that were
49
- # successfully removed
50
- def srem(key, member)
51
- block = if Redis.sadd_returns_boolean && !member.is_a?(Array)
52
- ::Redis.deprecate!(
53
- "Redis#sadd will always return an Integer in Redis 5.0.0. Use Redis#sadd? instead." \
54
- "(called from: #{caller(1, 1).first})"
55
- )
56
- Boolify
57
- end
58
- send_command([:srem, key, member], &block)
38
+ # @return [Integer] The number of members that were successfully removed
39
+ def srem(key, *members)
40
+ members.flatten!(1)
41
+ send_command([:srem, key].concat(members))
59
42
  end
60
43
 
61
44
  # Remove one or more members from a set.
62
45
  #
63
46
  # @param [String] key
64
47
  # @param [String, Array<String>] member one member, or array of members
65
- # @return [Boolean] `Boolean` Whether or not a member was removed.
66
- def srem?(key, member)
67
- send_command([:srem, key, member], &Boolify)
48
+ # @return [Boolean] Wether at least one member was successfully removed.
49
+ def srem?(key, *members)
50
+ members.flatten!(1)
51
+ send_command([:srem, key].concat(members), &Boolify)
68
52
  end
69
53
 
70
54
  # Remove and return one or more random member from a set.
@@ -76,7 +60,7 @@ class Redis
76
60
  if count.nil?
77
61
  send_command([:spop, key])
78
62
  else
79
- send_command([:spop, key, count])
63
+ send_command([:spop, key, Integer(count)])
80
64
  end
81
65
  end
82
66
 
@@ -118,7 +102,8 @@ class Redis
118
102
  # @param [String, Array<String>] members
119
103
  # @return [Array<Boolean>]
120
104
  def smismember(key, *members)
121
- send_command([:smismember, key, *members]) do |reply|
105
+ members.flatten!(1)
106
+ send_command([:smismember, key].concat(members)) do |reply|
122
107
  reply.map(&Boolify)
123
108
  end
124
109
  end
@@ -136,7 +121,8 @@ class Redis
136
121
  # @param [String, Array<String>] keys keys pointing to sets to subtract
137
122
  # @return [Array<String>] members in the difference
138
123
  def sdiff(*keys)
139
- send_command([:sdiff, *keys])
124
+ keys.flatten!(1)
125
+ send_command([:sdiff].concat(keys))
140
126
  end
141
127
 
142
128
  # Subtract multiple sets and store the resulting set in a key.
@@ -145,7 +131,8 @@ class Redis
145
131
  # @param [String, Array<String>] keys keys pointing to sets to subtract
146
132
  # @return [Integer] number of elements in the resulting set
147
133
  def sdiffstore(destination, *keys)
148
- send_command([:sdiffstore, destination, *keys])
134
+ keys.flatten!(1)
135
+ send_command([:sdiffstore, destination].concat(keys))
149
136
  end
150
137
 
151
138
  # Intersect multiple sets.
@@ -153,7 +140,8 @@ class Redis
153
140
  # @param [String, Array<String>] keys keys pointing to sets to intersect
154
141
  # @return [Array<String>] members in the intersection
155
142
  def sinter(*keys)
156
- send_command([:sinter, *keys])
143
+ keys.flatten!(1)
144
+ send_command([:sinter].concat(keys))
157
145
  end
158
146
 
159
147
  # Intersect multiple sets and store the resulting set in a key.
@@ -162,7 +150,8 @@ class Redis
162
150
  # @param [String, Array<String>] keys keys pointing to sets to intersect
163
151
  # @return [Integer] number of elements in the resulting set
164
152
  def sinterstore(destination, *keys)
165
- send_command([:sinterstore, destination, *keys])
153
+ keys.flatten!(1)
154
+ send_command([:sinterstore, destination].concat(keys))
166
155
  end
167
156
 
168
157
  # Add multiple sets.
@@ -170,7 +159,8 @@ class Redis
170
159
  # @param [String, Array<String>] keys keys pointing to sets to unify
171
160
  # @return [Array<String>] members in the union
172
161
  def sunion(*keys)
173
- send_command([:sunion, *keys])
162
+ keys.flatten!(1)
163
+ send_command([:sunion].concat(keys))
174
164
  end
175
165
 
176
166
  # Add multiple sets and store the resulting set in a key.
@@ -179,7 +169,8 @@ class Redis
179
169
  # @param [String, Array<String>] keys keys pointing to sets to unify
180
170
  # @return [Integer] number of elements in the resulting set
181
171
  def sunionstore(destination, *keys)
182
- send_command([:sunionstore, destination, *keys])
172
+ keys.flatten!(1)
173
+ send_command([:sunionstore, destination].concat(keys))
183
174
  end
184
175
 
185
176
  # Scan a set