redis 4.0.0 → 5.0.0

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 (137) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +252 -1
  3. data/README.md +126 -88
  4. data/lib/redis/client.rb +79 -543
  5. data/lib/redis/commands/bitmaps.rb +66 -0
  6. data/lib/redis/commands/cluster.rb +28 -0
  7. data/lib/redis/commands/connection.rb +53 -0
  8. data/lib/redis/commands/geo.rb +84 -0
  9. data/lib/redis/commands/hashes.rb +254 -0
  10. data/lib/redis/commands/hyper_log_log.rb +37 -0
  11. data/lib/redis/commands/keys.rb +437 -0
  12. data/lib/redis/commands/lists.rb +285 -0
  13. data/lib/redis/commands/pubsub.rb +54 -0
  14. data/lib/redis/commands/scripting.rb +114 -0
  15. data/lib/redis/commands/server.rb +188 -0
  16. data/lib/redis/commands/sets.rb +214 -0
  17. data/lib/redis/commands/sorted_sets.rb +818 -0
  18. data/lib/redis/commands/streams.rb +384 -0
  19. data/lib/redis/commands/strings.rb +314 -0
  20. data/lib/redis/commands/transactions.rb +115 -0
  21. data/lib/redis/commands.rb +235 -0
  22. data/lib/redis/distributed.rb +301 -109
  23. data/lib/redis/errors.rb +19 -1
  24. data/lib/redis/hash_ring.rb +34 -33
  25. data/lib/redis/pipeline.rb +69 -77
  26. data/lib/redis/subscribe.rb +26 -19
  27. data/lib/redis/version.rb +3 -1
  28. data/lib/redis.rb +109 -2728
  29. metadata +37 -229
  30. data/.gitignore +0 -16
  31. data/.travis/Gemfile +0 -13
  32. data/.travis.yml +0 -73
  33. data/.yardopts +0 -3
  34. data/Gemfile +0 -3
  35. data/benchmarking/logging.rb +0 -71
  36. data/benchmarking/pipeline.rb +0 -51
  37. data/benchmarking/speed.rb +0 -21
  38. data/benchmarking/suite.rb +0 -24
  39. data/benchmarking/worker.rb +0 -71
  40. data/bors.toml +0 -14
  41. data/examples/basic.rb +0 -15
  42. data/examples/consistency.rb +0 -114
  43. data/examples/dist_redis.rb +0 -43
  44. data/examples/incr-decr.rb +0 -17
  45. data/examples/list.rb +0 -26
  46. data/examples/pubsub.rb +0 -37
  47. data/examples/sentinel/sentinel.conf +0 -9
  48. data/examples/sentinel/start +0 -49
  49. data/examples/sentinel.rb +0 -41
  50. data/examples/sets.rb +0 -36
  51. data/examples/unicorn/config.ru +0 -3
  52. data/examples/unicorn/unicorn.rb +0 -20
  53. data/lib/redis/connection/command_helper.rb +0 -38
  54. data/lib/redis/connection/hiredis.rb +0 -66
  55. data/lib/redis/connection/registry.rb +0 -12
  56. data/lib/redis/connection/ruby.rb +0 -409
  57. data/lib/redis/connection/synchrony.rb +0 -141
  58. data/lib/redis/connection.rb +0 -9
  59. data/makefile +0 -42
  60. data/redis.gemspec +0 -42
  61. data/test/bitpos_test.rb +0 -63
  62. data/test/blocking_commands_test.rb +0 -40
  63. data/test/client_test.rb +0 -59
  64. data/test/command_map_test.rb +0 -28
  65. data/test/commands_on_hashes_test.rb +0 -19
  66. data/test/commands_on_hyper_log_log_test.rb +0 -19
  67. data/test/commands_on_lists_test.rb +0 -18
  68. data/test/commands_on_sets_test.rb +0 -75
  69. data/test/commands_on_sorted_sets_test.rb +0 -150
  70. data/test/commands_on_strings_test.rb +0 -99
  71. data/test/commands_on_value_types_test.rb +0 -131
  72. data/test/connection_handling_test.rb +0 -275
  73. data/test/db/.gitkeep +0 -0
  74. data/test/distributed_blocking_commands_test.rb +0 -44
  75. data/test/distributed_commands_on_hashes_test.rb +0 -8
  76. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -31
  77. data/test/distributed_commands_on_lists_test.rb +0 -20
  78. data/test/distributed_commands_on_sets_test.rb +0 -81
  79. data/test/distributed_commands_on_sorted_sets_test.rb +0 -16
  80. data/test/distributed_commands_on_strings_test.rb +0 -57
  81. data/test/distributed_commands_on_value_types_test.rb +0 -93
  82. data/test/distributed_commands_requiring_clustering_test.rb +0 -162
  83. data/test/distributed_connection_handling_test.rb +0 -21
  84. data/test/distributed_internals_test.rb +0 -68
  85. data/test/distributed_key_tags_test.rb +0 -50
  86. data/test/distributed_persistence_control_commands_test.rb +0 -24
  87. data/test/distributed_publish_subscribe_test.rb +0 -90
  88. data/test/distributed_remote_server_control_commands_test.rb +0 -64
  89. data/test/distributed_scripting_test.rb +0 -100
  90. data/test/distributed_sorting_test.rb +0 -18
  91. data/test/distributed_test.rb +0 -56
  92. data/test/distributed_transactions_test.rb +0 -30
  93. data/test/encoding_test.rb +0 -14
  94. data/test/error_replies_test.rb +0 -57
  95. data/test/fork_safety_test.rb +0 -60
  96. data/test/helper.rb +0 -201
  97. data/test/helper_test.rb +0 -22
  98. data/test/internals_test.rb +0 -429
  99. data/test/lint/blocking_commands.rb +0 -150
  100. data/test/lint/hashes.rb +0 -162
  101. data/test/lint/hyper_log_log.rb +0 -60
  102. data/test/lint/lists.rb +0 -143
  103. data/test/lint/sets.rb +0 -140
  104. data/test/lint/sorted_sets.rb +0 -316
  105. data/test/lint/strings.rb +0 -246
  106. data/test/lint/value_types.rb +0 -130
  107. data/test/persistence_control_commands_test.rb +0 -24
  108. data/test/pipelining_commands_test.rb +0 -238
  109. data/test/publish_subscribe_test.rb +0 -280
  110. data/test/remote_server_control_commands_test.rb +0 -175
  111. data/test/scanning_test.rb +0 -407
  112. data/test/scripting_test.rb +0 -76
  113. data/test/sentinel_command_test.rb +0 -78
  114. data/test/sentinel_test.rb +0 -253
  115. data/test/sorting_test.rb +0 -57
  116. data/test/ssl_test.rb +0 -69
  117. data/test/support/connection/hiredis.rb +0 -1
  118. data/test/support/connection/ruby.rb +0 -1
  119. data/test/support/connection/synchrony.rb +0 -17
  120. data/test/support/redis_mock.rb +0 -130
  121. data/test/support/ssl/gen_certs.sh +0 -31
  122. data/test/support/ssl/trusted-ca.crt +0 -25
  123. data/test/support/ssl/trusted-ca.key +0 -27
  124. data/test/support/ssl/trusted-cert.crt +0 -81
  125. data/test/support/ssl/trusted-cert.key +0 -28
  126. data/test/support/ssl/untrusted-ca.crt +0 -26
  127. data/test/support/ssl/untrusted-ca.key +0 -27
  128. data/test/support/ssl/untrusted-cert.crt +0 -82
  129. data/test/support/ssl/untrusted-cert.key +0 -28
  130. data/test/support/wire/synchrony.rb +0 -24
  131. data/test/support/wire/thread.rb +0 -5
  132. data/test/synchrony_driver.rb +0 -85
  133. data/test/test.conf.erb +0 -9
  134. data/test/thread_safety_test.rb +0 -60
  135. data/test/transactions_test.rb +0 -262
  136. data/test/unknown_commands_test.rb +0 -12
  137. data/test/url_param_test.rb +0 -136
data/lib/redis.rb CHANGED
@@ -1,2815 +1,196 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "monitor"
2
- require_relative "redis/errors"
4
+ require "redis/errors"
5
+ require "redis/commands"
3
6
 
4
7
  class Redis
8
+ BASE_PATH = __dir__
9
+ Deprecated = Class.new(StandardError)
5
10
 
6
- def self.current
7
- @current ||= Redis.new
8
- end
11
+ class << self
12
+ attr_accessor :silence_deprecations, :raise_deprecations
9
13
 
10
- def self.current=(redis)
11
- @current = redis
14
+ def deprecate!(message)
15
+ unless silence_deprecations
16
+ if raise_deprecations
17
+ raise Deprecated, message
18
+ else
19
+ ::Kernel.warn(message)
20
+ end
21
+ end
22
+ end
12
23
  end
13
24
 
14
- include MonitorMixin
25
+ include Commands
26
+
27
+ SERVER_URL_OPTIONS = %i(url host port path).freeze
15
28
 
16
29
  # Create a new client instance
17
30
  #
18
31
  # @param [Hash] options
19
- # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection: `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket connection: `unix://[path to Redis socket]`. This overrides all other options.
32
+ # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
33
+ # `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
34
+ # connection: `unix://[path to Redis socket]`. This overrides all other options.
20
35
  # @option options [String] :host ("127.0.0.1") server hostname
21
- # @option options [Fixnum] :port (6379) server port
36
+ # @option options [Integer] :port (6379) server port
22
37
  # @option options [String] :path path to server socket (overrides host and port)
23
38
  # @option options [Float] :timeout (5.0) timeout in seconds
24
39
  # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
40
+ # @option options [String] :username Username to authenticate against server
25
41
  # @option options [String] :password Password to authenticate against server
26
- # @option options [Fixnum] :db (0) Database to select after initial connect
27
- # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
28
- # @option options [String] :id ID for the client connection, assigns name to current connection by sending `CLIENT SETNAME`
29
- # @option options [Hash, Fixnum] :tcp_keepalive Keepalive values, if Fixnum `intvl` and `probe` are calculated based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Fixnum
30
- # @option options [Fixnum] :reconnect_attempts Number of attempts trying to connect
42
+ # @option options [Integer] :db (0) Database to select after connect and on reconnects
43
+ # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`
44
+ # @option options [String] :id ID for the client connection, assigns name to current connection by sending
45
+ # `CLIENT SETNAME`
46
+ # @option options [Integer, Array<Integer, Float>] :reconnect_attempts Number of attempts trying to connect,
47
+ # or a list of sleep duration between attempts.
31
48
  # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
49
+ # @option options [String] :name The name of the server group to connect to.
32
50
  # @option options [Array] :sentinels List of sentinels to contact
33
- # @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
34
51
  #
35
52
  # @return [Redis] a new client instance
36
53
  def initialize(options = {})
54
+ @monitor = Monitor.new
37
55
  @options = options.dup
38
- @original_client = @client = Client.new(options)
39
- @queue = Hash.new { |h, k| h[k] = [] }
40
-
41
- super() # Monitor#initialize
42
- end
43
-
44
- def synchronize
45
- mon_synchronize { yield(@client) }
46
- end
47
-
48
- # Run code with the client reconnecting
49
- def with_reconnect(val=true, &blk)
50
- synchronize do |client|
51
- client.with_reconnect(val, &blk)
56
+ @options[:reconnect_attempts] = 1 unless @options.key?(:reconnect_attempts)
57
+ if ENV["REDIS_URL"] && SERVER_URL_OPTIONS.none? { |o| @options.key?(o) }
58
+ @options[:url] = ENV["REDIS_URL"]
52
59
  end
60
+ inherit_socket = @options.delete(:inherit_socket)
61
+ @subscription_client = nil
62
+
63
+ @client = initialize_client(@options)
64
+ @client.inherit_socket! if inherit_socket
53
65
  end
54
66
 
55
67
  # Run code without the client reconnecting
56
- def without_reconnect(&blk)
57
- with_reconnect(false, &blk)
68
+ def without_reconnect(&block)
69
+ @client.disable_reconnection(&block)
58
70
  end
59
71
 
60
72
  # Test whether or not the client is connected
61
73
  def connected?
62
- @original_client.connected?
74
+ @client.connected? || @subscription_client&.connected?
63
75
  end
64
76
 
65
77
  # Disconnect the client as quickly and silently as possible.
66
78
  def close
67
- @original_client.disconnect
79
+ @client.close
80
+ @subscription_client&.close
68
81
  end
69
82
  alias disconnect! close
70
83
 
71
- # Sends a command to Redis and returns its reply.
72
- #
73
- # Replies are converted to Ruby objects according to the RESP protocol, so
74
- # you can expect a Ruby array, integer or nil when Redis sends one. Higher
75
- # level transformations, such as converting an array of pairs into a Ruby
76
- # hash, are up to consumers.
77
- #
78
- # Redis error replies are raised as Ruby exceptions.
79
- def call(*command)
80
- synchronize do |client|
81
- client.call(command)
82
- end
83
- end
84
-
85
- # Queues a command for pipelining.
86
- #
87
- # Commands in the queue are executed with the Redis#commit method.
88
- #
89
- # See http://redis.io/topics/pipelining for more details.
90
- #
91
- def queue(*command)
92
- @queue[Thread.current.object_id] << command
93
- end
94
-
95
- # Sends all commands in the queue.
96
- #
97
- # See http://redis.io/topics/pipelining for more details.
98
- #
99
- def commit
100
- synchronize do |client|
101
- begin
102
- client.call_pipelined(@queue[Thread.current.object_id])
103
- ensure
104
- @queue.delete(Thread.current.object_id)
105
- end
106
- end
84
+ def with
85
+ yield self
107
86
  end
108
87
 
109
88
  def _client
110
89
  @client
111
90
  end
112
91
 
113
- # Authenticate to the server.
114
- #
115
- # @param [String] password must match the password specified in the
116
- # `requirepass` directive in the configuration file
117
- # @return [String] `OK`
118
- def auth(password)
92
+ def pipelined
119
93
  synchronize do |client|
120
- client.call([:auth, password])
94
+ client.pipelined do |raw_pipeline|
95
+ yield PipelinedConnection.new(raw_pipeline)
96
+ end
121
97
  end
122
98
  end
123
99
 
124
- # Change the selected database for the current connection.
125
- #
126
- # @param [Fixnum] db zero-based index of the DB to use (0 to 15)
127
- # @return [String] `OK`
128
- def select(db)
129
- synchronize do |client|
130
- client.db = db
131
- client.call([:select, db])
132
- end
100
+ def id
101
+ @client.id || @client.server_url
133
102
  end
134
103
 
135
- # Ping the server.
136
- #
137
- # @param [optional, String] message
138
- # @return [String] `PONG`
139
- def ping(message = nil)
140
- synchronize do |client|
141
- client.call([:ping, message].compact)
142
- end
104
+ def inspect
105
+ "#<Redis client v#{Redis::VERSION} for #{id}>"
143
106
  end
144
107
 
145
- # Echo the given string.
146
- #
147
- # @param [String] value
148
- # @return [String]
149
- def echo(value)
150
- synchronize do |client|
151
- client.call([:echo, value])
152
- end
108
+ def dup
109
+ self.class.new(@options)
153
110
  end
154
111
 
155
- # Close the connection.
156
- #
157
- # @return [String] `OK`
158
- def quit
159
- synchronize do |client|
160
- begin
161
- client.call([:quit])
162
- rescue ConnectionError
163
- ensure
164
- client.disconnect
165
- end
166
- end
112
+ def connection
113
+ {
114
+ host: @client.host,
115
+ port: @client.port,
116
+ db: @client.db,
117
+ id: id,
118
+ location: "#{@client.host}:#{@client.port}"
119
+ }
167
120
  end
168
121
 
169
- # Asynchronously rewrite the append-only file.
170
- #
171
- # @return [String] `OK`
172
- def bgrewriteaof
173
- synchronize do |client|
174
- client.call([:bgrewriteaof])
175
- end
176
- end
122
+ private
177
123
 
178
- # Asynchronously save the dataset to disk.
179
- #
180
- # @return [String] `OK`
181
- def bgsave
182
- synchronize do |client|
183
- client.call([:bgsave])
124
+ def initialize_client(options)
125
+ if options.key?(:cluster)
126
+ raise "Redis Cluster support was moved to the `redis_cluster` gem."
184
127
  end
185
- end
186
128
 
187
- # Get or set server configuration parameters.
188
- #
189
- # @param [Symbol] action e.g. `:get`, `:set`, `:resetstat`
190
- # @return [String, Hash] string reply, or hash when retrieving more than one
191
- # property with `CONFIG GET`
192
- def config(action, *args)
193
- synchronize do |client|
194
- client.call([:config, action] + args) do |reply|
195
- if reply.kind_of?(Array) && action == :get
196
- Hashify.call(reply)
197
- else
198
- reply
129
+ if options.key?(:sentinels)
130
+ if url = options.delete(:url)
131
+ uri = URI.parse(url)
132
+ if !options.key?(:name) && uri.host
133
+ options[:name] = uri.host
199
134
  end
200
- end
201
- end
202
- end
203
135
 
204
- # Manage client connections.
205
- #
206
- # @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
207
- # @return [String, Hash] depends on subcommand
208
- def client(subcommand = nil, *args)
209
- synchronize do |client|
210
- client.call([:client, subcommand] + args) do |reply|
211
- if subcommand.to_s == "list"
212
- reply.lines.map do |line|
213
- entries = line.chomp.split(/[ =]/)
214
- Hash[entries.each_slice(2).to_a]
215
- end
216
- else
217
- reply
136
+ if !options.key?(:password) && uri.password && !uri.password.empty?
137
+ options[:password] = uri.password
218
138
  end
219
- end
220
- end
221
- end
222
-
223
- # Return the number of keys in the selected database.
224
- #
225
- # @return [Fixnum]
226
- def dbsize
227
- synchronize do |client|
228
- client.call([:dbsize])
229
- end
230
- end
231
-
232
- def debug(*args)
233
- synchronize do |client|
234
- client.call([:debug] + args)
235
- end
236
- end
237
-
238
- # Remove all keys from all databases.
239
- #
240
- # @return [String] `OK`
241
- def flushall
242
- synchronize do |client|
243
- client.call([:flushall])
244
- end
245
- end
246
-
247
- # Remove all keys from the current database.
248
- #
249
- # @return [String] `OK`
250
- def flushdb
251
- synchronize do |client|
252
- client.call([:flushdb])
253
- end
254
- end
255
-
256
- # Get information and statistics about the server.
257
- #
258
- # @param [String, Symbol] cmd e.g. "commandstats"
259
- # @return [Hash<String, String>]
260
- def info(cmd = nil)
261
- synchronize do |client|
262
- client.call([:info, cmd].compact) do |reply|
263
- if reply.kind_of?(String)
264
- reply = Hash[reply.split("\r\n").map do |line|
265
- line.split(":", 2) unless line =~ /^(#|$)/
266
- end.compact]
267
139
 
268
- if cmd && cmd.to_s == "commandstats"
269
- # Extract nested hashes for INFO COMMANDSTATS
270
- reply = Hash[reply.map do |k, v|
271
- v = v.split(",").map { |e| e.split("=") }
272
- [k[/^cmdstat_(.*)$/, 1], Hash[v]]
273
- end]
274
- end
140
+ if !options.key?(:username) && uri.user && !uri.user.empty?
141
+ options[:username] = uri.user
275
142
  end
276
-
277
- reply
278
143
  end
279
- end
280
- end
281
-
282
- # Get the UNIX time stamp of the last successful save to disk.
283
- #
284
- # @return [Fixnum]
285
- def lastsave
286
- synchronize do |client|
287
- client.call([:lastsave])
288
- end
289
- end
290
-
291
- # Listen for all requests received by the server in real time.
292
- #
293
- # There is no way to interrupt this command.
294
- #
295
- # @yield a block to be called for every line of output
296
- # @yieldparam [String] line timestamp and command that was executed
297
- def monitor(&block)
298
- synchronize do |client|
299
- client.call_loop([:monitor], &block)
300
- end
301
- end
302
144
 
303
- # Synchronously save the dataset to disk.
304
- #
305
- # @return [String]
306
- def save
307
- synchronize do |client|
308
- client.call([:save])
309
- end
310
- end
311
-
312
- # Synchronously save the dataset to disk and then shut down the server.
313
- def shutdown
314
- synchronize do |client|
315
- client.with_reconnect(false) do
316
- begin
317
- client.call([:shutdown])
318
- rescue ConnectionError
319
- # This means Redis has probably exited.
320
- nil
321
- end
322
- end
145
+ Client.sentinel(**options).new_client
146
+ else
147
+ Client.config(**options).new_client
323
148
  end
324
149
  end
325
150
 
326
- # Make the server a slave of another instance, or promote it as master.
327
- def slaveof(host, port)
328
- synchronize do |client|
329
- client.call([:slaveof, host, port])
330
- end
151
+ def synchronize
152
+ @monitor.synchronize { yield(@client) }
331
153
  end
332
154
 
333
- # Interact with the slowlog (get, len, reset)
334
- #
335
- # @param [String] subcommand e.g. `get`, `len`, `reset`
336
- # @param [Fixnum] length maximum number of entries to return
337
- # @return [Array<String>, Fixnum, String] depends on subcommand
338
- def slowlog(subcommand, length=nil)
339
- synchronize do |client|
340
- args = [:slowlog, subcommand]
341
- args << length if length
342
- client.call args
155
+ def send_command(command, &block)
156
+ @monitor.synchronize do
157
+ @client.call_v(command, &block)
343
158
  end
344
159
  end
345
160
 
346
- # Internal command used for replication.
347
- def sync
348
- synchronize do |client|
349
- client.call([:sync])
161
+ def send_blocking_command(command, timeout, &block)
162
+ @monitor.synchronize do
163
+ @client.blocking_call_v(timeout, command, &block)
350
164
  end
351
165
  end
352
166
 
353
- # Return the server time.
354
- #
355
- # @example
356
- # r.time # => [ 1333093196, 606806 ]
357
- #
358
- # @return [Array<Fixnum>] tuple of seconds since UNIX epoch and
359
- # microseconds in the current second
360
- def time
361
- synchronize do |client|
362
- client.call([:time]) do |reply|
363
- reply.map(&:to_i) if reply
167
+ def _subscription(method, timeout, channels, block)
168
+ if block
169
+ if @subscription_client
170
+ raise SubscriptionError, "This client is already subscribed"
364
171
  end
365
- end
366
- end
367
-
368
- # Remove the expiration from a key.
369
- #
370
- # @param [String] key
371
- # @return [Boolean] whether the timeout was removed or not
372
- def persist(key)
373
- synchronize do |client|
374
- client.call([:persist, key], &Boolify)
375
- end
376
- end
377
-
378
- # Set a key's time to live in seconds.
379
- #
380
- # @param [String] key
381
- # @param [Fixnum] seconds time to live
382
- # @return [Boolean] whether the timeout was set or not
383
- def expire(key, seconds)
384
- synchronize do |client|
385
- client.call([:expire, key, seconds], &Boolify)
386
- end
387
- end
388
-
389
- # Set the expiration for a key as a UNIX timestamp.
390
- #
391
- # @param [String] key
392
- # @param [Fixnum] unix_time expiry time specified as a UNIX timestamp
393
- # @return [Boolean] whether the timeout was set or not
394
- def expireat(key, unix_time)
395
- synchronize do |client|
396
- client.call([:expireat, key, unix_time], &Boolify)
397
- end
398
- end
399
-
400
- # Get the time to live (in seconds) for a key.
401
- #
402
- # @param [String] key
403
- # @return [Fixnum] remaining time to live in seconds.
404
- #
405
- # In Redis 2.6 or older the command returns -1 if the key does not exist or if
406
- # the key exist but has no associated expire.
407
- #
408
- # Starting with Redis 2.8 the return value in case of error changed:
409
- #
410
- # - The command returns -2 if the key does not exist.
411
- # - The command returns -1 if the key exists but has no associated expire.
412
- def ttl(key)
413
- synchronize do |client|
414
- client.call([:ttl, key])
415
- end
416
- end
417
-
418
- # Set a key's time to live in milliseconds.
419
- #
420
- # @param [String] key
421
- # @param [Fixnum] milliseconds time to live
422
- # @return [Boolean] whether the timeout was set or not
423
- def pexpire(key, milliseconds)
424
- synchronize do |client|
425
- client.call([:pexpire, key, milliseconds], &Boolify)
426
- end
427
- end
428
-
429
- # Set the expiration for a key as number of milliseconds from UNIX Epoch.
430
- #
431
- # @param [String] key
432
- # @param [Fixnum] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
433
- # @return [Boolean] whether the timeout was set or not
434
- def pexpireat(key, ms_unix_time)
435
- synchronize do |client|
436
- client.call([:pexpireat, key, ms_unix_time], &Boolify)
437
- end
438
- end
439
-
440
- # Get the time to live (in milliseconds) for a key.
441
- #
442
- # @param [String] key
443
- # @return [Fixnum] remaining time to live in milliseconds
444
- # In Redis 2.6 or older the command returns -1 if the key does not exist or if
445
- # the key exist but has no associated expire.
446
- #
447
- # Starting with Redis 2.8 the return value in case of error changed:
448
- #
449
- # - The command returns -2 if the key does not exist.
450
- # - The command returns -1 if the key exists but has no associated expire.
451
- def pttl(key)
452
- synchronize do |client|
453
- client.call([:pttl, key])
454
- end
455
- end
456
-
457
- # Return a serialized version of the value stored at a key.
458
- #
459
- # @param [String] key
460
- # @return [String] serialized_value
461
- def dump(key)
462
- synchronize do |client|
463
- client.call([:dump, key])
464
- end
465
- end
466
-
467
- # Create a key using the serialized value, previously obtained using DUMP.
468
- #
469
- # @param [String] key
470
- # @param [String] ttl
471
- # @param [String] serialized_value
472
- # @param [Hash] options
473
- # - `:replace => Boolean`: if false, raises an error if key already exists
474
- # @raise [Redis::CommandError]
475
- # @return [String] `"OK"`
476
- def restore(key, ttl, serialized_value, options = {})
477
- args = [:restore, key, ttl, serialized_value]
478
- args << 'REPLACE' if options[:replace]
479
-
480
- synchronize do |client|
481
- client.call(args)
482
- end
483
- end
484
-
485
- # Transfer a key from the connected instance to another instance.
486
- #
487
- # @param [String] key
488
- # @param [Hash] options
489
- # - `:host => String`: host of instance to migrate to
490
- # - `:port => Integer`: port of instance to migrate to
491
- # - `:db => Integer`: database to migrate to (default: same as source)
492
- # - `:timeout => Integer`: timeout (default: same as connection timeout)
493
- # @return [String] `"OK"`
494
- def migrate(key, options)
495
- host = options[:host] || raise(RuntimeError, ":host not specified")
496
- port = options[:port] || raise(RuntimeError, ":port not specified")
497
- db = (options[:db] || @client.db).to_i
498
- timeout = (options[:timeout] || @client.timeout).to_i
499
-
500
- synchronize do |client|
501
- client.call([:migrate, host, port, key, db, timeout])
502
- end
503
- end
504
172
 
505
- # Delete one or more keys.
506
- #
507
- # @param [String, Array<String>] keys
508
- # @return [Fixnum] number of keys that were deleted
509
- def del(*keys)
510
- synchronize do |client|
511
- client.call([:del] + keys)
512
- end
513
- end
514
-
515
- # Determine if a key exists.
516
- #
517
- # @param [String] key
518
- # @return [Boolean]
519
- def exists(key)
520
- synchronize do |client|
521
- client.call([:exists, key], &Boolify)
522
- end
523
- end
524
-
525
- # Find all keys matching the given pattern.
526
- #
527
- # @param [String] pattern
528
- # @return [Array<String>]
529
- def keys(pattern = "*")
530
- synchronize do |client|
531
- client.call([:keys, pattern]) do |reply|
532
- if reply.kind_of?(String)
533
- reply.split(" ")
173
+ begin
174
+ @subscription_client = SubscribedClient.new(@client.pubsub)
175
+ if timeout > 0
176
+ @subscription_client.send(method, timeout, *channels, &block)
534
177
  else
535
- reply
178
+ @subscription_client.send(method, *channels, &block)
536
179
  end
180
+ ensure
181
+ @subscription_client = nil
537
182
  end
538
- end
539
- end
540
-
541
- # Move a key to another database.
542
- #
543
- # @example Move a key to another database
544
- # redis.set "foo", "bar"
545
- # # => "OK"
546
- # redis.move "foo", 2
547
- # # => true
548
- # redis.exists "foo"
549
- # # => false
550
- # redis.select 2
551
- # # => "OK"
552
- # redis.exists "foo"
553
- # # => true
554
- # redis.get "foo"
555
- # # => "bar"
556
- #
557
- # @param [String] key
558
- # @param [Fixnum] db
559
- # @return [Boolean] whether the key was moved or not
560
- def move(key, db)
561
- synchronize do |client|
562
- client.call([:move, key, db], &Boolify)
563
- end
564
- end
565
-
566
- def object(*args)
567
- synchronize do |client|
568
- client.call([:object] + args)
569
- end
570
- end
571
-
572
- # Return a random key from the keyspace.
573
- #
574
- # @return [String]
575
- def randomkey
576
- synchronize do |client|
577
- client.call([:randomkey])
578
- end
579
- end
580
-
581
- # Rename a key. If the new key already exists it is overwritten.
582
- #
583
- # @param [String] old_name
584
- # @param [String] new_name
585
- # @return [String] `OK`
586
- def rename(old_name, new_name)
587
- synchronize do |client|
588
- client.call([:rename, old_name, new_name])
589
- end
590
- end
591
-
592
- # Rename a key, only if the new key does not exist.
593
- #
594
- # @param [String] old_name
595
- # @param [String] new_name
596
- # @return [Boolean] whether the key was renamed or not
597
- def renamenx(old_name, new_name)
598
- synchronize do |client|
599
- client.call([:renamenx, old_name, new_name], &Boolify)
600
- end
601
- end
602
-
603
- # Sort the elements in a list, set or sorted set.
604
- #
605
- # @example Retrieve the first 2 elements from an alphabetically sorted "list"
606
- # redis.sort("list", :order => "alpha", :limit => [0, 2])
607
- # # => ["a", "b"]
608
- # @example Store an alphabetically descending list in "target"
609
- # redis.sort("list", :order => "desc alpha", :store => "target")
610
- # # => 26
611
- #
612
- # @param [String] key
613
- # @param [Hash] options
614
- # - `:by => String`: use external key to sort elements by
615
- # - `:limit => [offset, count]`: skip `offset` elements, return a maximum
616
- # of `count` elements
617
- # - `:get => [String, Array<String>]`: single key or array of keys to
618
- # retrieve per element in the result
619
- # - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
620
- # - `:store => String`: key to store the result at
621
- #
622
- # @return [Array<String>, Array<Array<String>>, Fixnum]
623
- # - when `:get` is not specified, or holds a single element, an array of elements
624
- # - when `:get` is specified, and holds more than one element, an array of
625
- # elements where every element is an array with the result for every
626
- # element specified in `:get`
627
- # - when `:store` is specified, the number of elements in the stored result
628
- def sort(key, options = {})
629
- args = []
630
-
631
- by = options[:by]
632
- args.concat(["BY", by]) if by
633
-
634
- limit = options[:limit]
635
- args.concat(["LIMIT"] + limit) if limit
636
-
637
- get = Array(options[:get])
638
- args.concat(["GET"].product(get).flatten) unless get.empty?
639
-
640
- order = options[:order]
641
- args.concat(order.split(" ")) if order
642
-
643
- store = options[:store]
644
- args.concat(["STORE", store]) if store
645
-
646
- synchronize do |client|
647
- client.call([:sort, key] + args) do |reply|
648
- if get.size > 1 && !store
649
- if reply
650
- reply.each_slice(get.size).to_a
651
- end
652
- else
653
- reply
654
- end
183
+ else
184
+ unless @subscription_client
185
+ raise SubscriptionError, "This client is not subscribed"
655
186
  end
656
- end
657
- end
658
187
 
659
- # Determine the type stored at key.
660
- #
661
- # @param [String] key
662
- # @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
663
- def type(key)
664
- synchronize do |client|
665
- client.call([:type, key])
188
+ @subscription_client.call_v([method].concat(channels))
666
189
  end
667
190
  end
668
-
669
- # Decrement the integer value of a key by one.
670
- #
671
- # @example
672
- # redis.decr("value")
673
- # # => 4
674
- #
675
- # @param [String] key
676
- # @return [Fixnum] value after decrementing it
677
- def decr(key)
678
- synchronize do |client|
679
- client.call([:decr, key])
680
- end
681
- end
682
-
683
- # Decrement the integer value of a key by the given number.
684
- #
685
- # @example
686
- # redis.decrby("value", 5)
687
- # # => 0
688
- #
689
- # @param [String] key
690
- # @param [Fixnum] decrement
691
- # @return [Fixnum] value after decrementing it
692
- def decrby(key, decrement)
693
- synchronize do |client|
694
- client.call([:decrby, key, decrement])
695
- end
696
- end
697
-
698
- # Increment the integer value of a key by one.
699
- #
700
- # @example
701
- # redis.incr("value")
702
- # # => 6
703
- #
704
- # @param [String] key
705
- # @return [Fixnum] value after incrementing it
706
- def incr(key)
707
- synchronize do |client|
708
- client.call([:incr, key])
709
- end
710
- end
711
-
712
- # Increment the integer value of a key by the given integer number.
713
- #
714
- # @example
715
- # redis.incrby("value", 5)
716
- # # => 10
717
- #
718
- # @param [String] key
719
- # @param [Fixnum] increment
720
- # @return [Fixnum] value after incrementing it
721
- def incrby(key, increment)
722
- synchronize do |client|
723
- client.call([:incrby, key, increment])
724
- end
725
- end
726
-
727
- # Increment the numeric value of a key by the given float number.
728
- #
729
- # @example
730
- # redis.incrbyfloat("value", 1.23)
731
- # # => 1.23
732
- #
733
- # @param [String] key
734
- # @param [Float] increment
735
- # @return [Float] value after incrementing it
736
- def incrbyfloat(key, increment)
737
- synchronize do |client|
738
- client.call([:incrbyfloat, key, increment], &Floatify)
739
- end
740
- end
741
-
742
- # Set the string value of a key.
743
- #
744
- # @param [String] key
745
- # @param [String] value
746
- # @param [Hash] options
747
- # - `:ex => Fixnum`: Set the specified expire time, in seconds.
748
- # - `:px => Fixnum`: Set the specified expire time, in milliseconds.
749
- # - `:nx => true`: Only set the key if it does not already exist.
750
- # - `:xx => true`: Only set the key if it already exist.
751
- # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
752
- def set(key, value, options = {})
753
- args = []
754
-
755
- ex = options[:ex]
756
- args.concat(["EX", ex]) if ex
757
-
758
- px = options[:px]
759
- args.concat(["PX", px]) if px
760
-
761
- nx = options[:nx]
762
- args.concat(["NX"]) if nx
763
-
764
- xx = options[:xx]
765
- args.concat(["XX"]) if xx
766
-
767
- synchronize do |client|
768
- if nx || xx
769
- client.call([:set, key, value.to_s] + args, &BoolifySet)
770
- else
771
- client.call([:set, key, value.to_s] + args)
772
- end
773
- end
774
- end
775
-
776
- # Set the time to live in seconds of a key.
777
- #
778
- # @param [String] key
779
- # @param [Fixnum] ttl
780
- # @param [String] value
781
- # @return [String] `"OK"`
782
- def setex(key, ttl, value)
783
- synchronize do |client|
784
- client.call([:setex, key, ttl, value.to_s])
785
- end
786
- end
787
-
788
- # Set the time to live in milliseconds of a key.
789
- #
790
- # @param [String] key
791
- # @param [Fixnum] ttl
792
- # @param [String] value
793
- # @return [String] `"OK"`
794
- def psetex(key, ttl, value)
795
- synchronize do |client|
796
- client.call([:psetex, key, ttl, value.to_s])
797
- end
798
- end
799
-
800
- # Set the value of a key, only if the key does not exist.
801
- #
802
- # @param [String] key
803
- # @param [String] value
804
- # @return [Boolean] whether the key was set or not
805
- def setnx(key, value)
806
- synchronize do |client|
807
- client.call([:setnx, key, value.to_s], &Boolify)
808
- end
809
- end
810
-
811
- # Set one or more values.
812
- #
813
- # @example
814
- # redis.mset("key1", "v1", "key2", "v2")
815
- # # => "OK"
816
- #
817
- # @param [Array<String>] args array of keys and values
818
- # @return [String] `"OK"`
819
- #
820
- # @see #mapped_mset
821
- def mset(*args)
822
- synchronize do |client|
823
- client.call([:mset] + args)
824
- end
825
- end
826
-
827
- # Set one or more values.
828
- #
829
- # @example
830
- # redis.mapped_mset({ "f1" => "v1", "f2" => "v2" })
831
- # # => "OK"
832
- #
833
- # @param [Hash] hash keys mapping to values
834
- # @return [String] `"OK"`
835
- #
836
- # @see #mset
837
- def mapped_mset(hash)
838
- mset(hash.to_a.flatten)
839
- end
840
-
841
- # Set one or more values, only if none of the keys exist.
842
- #
843
- # @example
844
- # redis.msetnx("key1", "v1", "key2", "v2")
845
- # # => true
846
- #
847
- # @param [Array<String>] args array of keys and values
848
- # @return [Boolean] whether or not all values were set
849
- #
850
- # @see #mapped_msetnx
851
- def msetnx(*args)
852
- synchronize do |client|
853
- client.call([:msetnx] + args, &Boolify)
854
- end
855
- end
856
-
857
- # Set one or more values, only if none of the keys exist.
858
- #
859
- # @example
860
- # redis.mapped_msetnx({ "key1" => "v1", "key2" => "v2" })
861
- # # => true
862
- #
863
- # @param [Hash] hash keys mapping to values
864
- # @return [Boolean] whether or not all values were set
865
- #
866
- # @see #msetnx
867
- def mapped_msetnx(hash)
868
- msetnx(hash.to_a.flatten)
869
- end
870
-
871
- # Get the value of a key.
872
- #
873
- # @param [String] key
874
- # @return [String]
875
- def get(key)
876
- synchronize do |client|
877
- client.call([:get, key])
878
- end
879
- end
880
-
881
- # Get the values of all the given keys.
882
- #
883
- # @example
884
- # redis.mget("key1", "key1")
885
- # # => ["v1", "v2"]
886
- #
887
- # @param [Array<String>] keys
888
- # @return [Array<String>] an array of values for the specified keys
889
- #
890
- # @see #mapped_mget
891
- def mget(*keys, &blk)
892
- synchronize do |client|
893
- client.call([:mget] + keys, &blk)
894
- end
895
- end
896
-
897
- # Get the values of all the given keys.
898
- #
899
- # @example
900
- # redis.mapped_mget("key1", "key2")
901
- # # => { "key1" => "v1", "key2" => "v2" }
902
- #
903
- # @param [Array<String>] keys array of keys
904
- # @return [Hash] a hash mapping the specified keys to their values
905
- #
906
- # @see #mget
907
- def mapped_mget(*keys)
908
- mget(*keys) do |reply|
909
- if reply.kind_of?(Array)
910
- Hash[keys.zip(reply)]
911
- else
912
- reply
913
- end
914
- end
915
- end
916
-
917
- # Overwrite part of a string at key starting at the specified offset.
918
- #
919
- # @param [String] key
920
- # @param [Fixnum] offset byte offset
921
- # @param [String] value
922
- # @return [Fixnum] length of the string after it was modified
923
- def setrange(key, offset, value)
924
- synchronize do |client|
925
- client.call([:setrange, key, offset, value.to_s])
926
- end
927
- end
928
-
929
- # Get a substring of the string stored at a key.
930
- #
931
- # @param [String] key
932
- # @param [Fixnum] start zero-based start offset
933
- # @param [Fixnum] stop zero-based end offset. Use -1 for representing
934
- # the end of the string
935
- # @return [Fixnum] `0` or `1`
936
- def getrange(key, start, stop)
937
- synchronize do |client|
938
- client.call([:getrange, key, start, stop])
939
- end
940
- end
941
-
942
- # Sets or clears the bit at offset in the string value stored at key.
943
- #
944
- # @param [String] key
945
- # @param [Fixnum] offset bit offset
946
- # @param [Fixnum] value bit value `0` or `1`
947
- # @return [Fixnum] the original bit value stored at `offset`
948
- def setbit(key, offset, value)
949
- synchronize do |client|
950
- client.call([:setbit, key, offset, value])
951
- end
952
- end
953
-
954
- # Returns the bit value at offset in the string value stored at key.
955
- #
956
- # @param [String] key
957
- # @param [Fixnum] offset bit offset
958
- # @return [Fixnum] `0` or `1`
959
- def getbit(key, offset)
960
- synchronize do |client|
961
- client.call([:getbit, key, offset])
962
- end
963
- end
964
-
965
- # Append a value to a key.
966
- #
967
- # @param [String] key
968
- # @param [String] value value to append
969
- # @return [Fixnum] length of the string after appending
970
- def append(key, value)
971
- synchronize do |client|
972
- client.call([:append, key, value])
973
- end
974
- end
975
-
976
- # Count the number of set bits in a range of the string value stored at key.
977
- #
978
- # @param [String] key
979
- # @param [Fixnum] start start index
980
- # @param [Fixnum] stop stop index
981
- # @return [Fixnum] the number of bits set to 1
982
- def bitcount(key, start = 0, stop = -1)
983
- synchronize do |client|
984
- client.call([:bitcount, key, start, stop])
985
- end
986
- end
987
-
988
- # Perform a bitwise operation between strings and store the resulting string in a key.
989
- #
990
- # @param [String] operation e.g. `and`, `or`, `xor`, `not`
991
- # @param [String] destkey destination key
992
- # @param [String, Array<String>] keys one or more source keys to perform `operation`
993
- # @return [Fixnum] the length of the string stored in `destkey`
994
- def bitop(operation, destkey, *keys)
995
- synchronize do |client|
996
- client.call([:bitop, operation, destkey] + keys)
997
- end
998
- end
999
-
1000
- # Return the position of the first bit set to 1 or 0 in a string.
1001
- #
1002
- # @param [String] key
1003
- # @param [Fixnum] bit whether to look for the first 1 or 0 bit
1004
- # @param [Fixnum] start start index
1005
- # @param [Fixnum] stop stop index
1006
- # @return [Fixnum] the position of the first 1/0 bit.
1007
- # -1 if looking for 1 and it is not found or start and stop are given.
1008
- def bitpos(key, bit, start=nil, stop=nil)
1009
- if stop and not start
1010
- raise(ArgumentError, 'stop parameter specified without start parameter')
1011
- end
1012
-
1013
- synchronize do |client|
1014
- command = [:bitpos, key, bit]
1015
- command << start if start
1016
- command << stop if stop
1017
- client.call(command)
1018
- end
1019
- end
1020
-
1021
- # Set the string value of a key and return its old value.
1022
- #
1023
- # @param [String] key
1024
- # @param [String] value value to replace the current value with
1025
- # @return [String] the old value stored in the key, or `nil` if the key
1026
- # did not exist
1027
- def getset(key, value)
1028
- synchronize do |client|
1029
- client.call([:getset, key, value.to_s])
1030
- end
1031
- end
1032
-
1033
- # Get the length of the value stored in a key.
1034
- #
1035
- # @param [String] key
1036
- # @return [Fixnum] the length of the value stored in the key, or 0
1037
- # if the key does not exist
1038
- def strlen(key)
1039
- synchronize do |client|
1040
- client.call([:strlen, key])
1041
- end
1042
- end
1043
-
1044
- # Get the length of a list.
1045
- #
1046
- # @param [String] key
1047
- # @return [Fixnum]
1048
- def llen(key)
1049
- synchronize do |client|
1050
- client.call([:llen, key])
1051
- end
1052
- end
1053
-
1054
- # Prepend one or more values to a list, creating the list if it doesn't exist
1055
- #
1056
- # @param [String] key
1057
- # @param [String, Array<String>] value string value, or array of string values to push
1058
- # @return [Fixnum] the length of the list after the push operation
1059
- def lpush(key, value)
1060
- synchronize do |client|
1061
- client.call([:lpush, key, value])
1062
- end
1063
- end
1064
-
1065
- # Prepend a value to a list, only if the list exists.
1066
- #
1067
- # @param [String] key
1068
- # @param [String] value
1069
- # @return [Fixnum] the length of the list after the push operation
1070
- def lpushx(key, value)
1071
- synchronize do |client|
1072
- client.call([:lpushx, key, value])
1073
- end
1074
- end
1075
-
1076
- # Append one or more values to a list, creating the list if it doesn't exist
1077
- #
1078
- # @param [String] key
1079
- # @param [String, Array<String>] value string value, or array of string values to push
1080
- # @return [Fixnum] the length of the list after the push operation
1081
- def rpush(key, value)
1082
- synchronize do |client|
1083
- client.call([:rpush, key, value])
1084
- end
1085
- end
1086
-
1087
- # Append a value to a list, only if the list exists.
1088
- #
1089
- # @param [String] key
1090
- # @param [String] value
1091
- # @return [Fixnum] the length of the list after the push operation
1092
- def rpushx(key, value)
1093
- synchronize do |client|
1094
- client.call([:rpushx, key, value])
1095
- end
1096
- end
1097
-
1098
- # Remove and get the first element in a list.
1099
- #
1100
- # @param [String] key
1101
- # @return [String]
1102
- def lpop(key)
1103
- synchronize do |client|
1104
- client.call([:lpop, key])
1105
- end
1106
- end
1107
-
1108
- # Remove and get the last element in a list.
1109
- #
1110
- # @param [String] key
1111
- # @return [String]
1112
- def rpop(key)
1113
- synchronize do |client|
1114
- client.call([:rpop, key])
1115
- end
1116
- end
1117
-
1118
- # Remove the last element in a list, append it to another list and return it.
1119
- #
1120
- # @param [String] source source key
1121
- # @param [String] destination destination key
1122
- # @return [nil, String] the element, or nil when the source key does not exist
1123
- def rpoplpush(source, destination)
1124
- synchronize do |client|
1125
- client.call([:rpoplpush, source, destination])
1126
- end
1127
- end
1128
-
1129
- def _bpop(cmd, args)
1130
- options = {}
1131
-
1132
- case args.last
1133
- when Hash
1134
- options = args.pop
1135
- when Integer
1136
- # Issue deprecation notice in obnoxious mode...
1137
- options[:timeout] = args.pop
1138
- end
1139
-
1140
- if args.size > 1
1141
- # Issue deprecation notice in obnoxious mode...
1142
- end
1143
-
1144
- keys = args.flatten
1145
- timeout = options[:timeout] || 0
1146
-
1147
- synchronize do |client|
1148
- command = [cmd, keys, timeout]
1149
- timeout += client.timeout if timeout > 0
1150
- client.call_with_timeout(command, timeout)
1151
- end
1152
- end
1153
-
1154
- # Remove and get the first element in a list, or block until one is available.
1155
- #
1156
- # @example With timeout
1157
- # list, element = redis.blpop("list", :timeout => 5)
1158
- # # => nil on timeout
1159
- # # => ["list", "element"] on success
1160
- # @example Without timeout
1161
- # list, element = redis.blpop("list")
1162
- # # => ["list", "element"]
1163
- # @example Blocking pop on multiple lists
1164
- # list, element = redis.blpop(["list", "another_list"])
1165
- # # => ["list", "element"]
1166
- #
1167
- # @param [String, Array<String>] keys one or more keys to perform the
1168
- # blocking pop on
1169
- # @param [Hash] options
1170
- # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
1171
- #
1172
- # @return [nil, [String, String]]
1173
- # - `nil` when the operation timed out
1174
- # - tuple of the list that was popped from and element was popped otherwise
1175
- def blpop(*args)
1176
- _bpop(:blpop, args)
1177
- end
1178
-
1179
- # Remove and get the last element in a list, or block until one is available.
1180
- #
1181
- # @param [String, Array<String>] keys one or more keys to perform the
1182
- # blocking pop on
1183
- # @param [Hash] options
1184
- # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
1185
- #
1186
- # @return [nil, [String, String]]
1187
- # - `nil` when the operation timed out
1188
- # - tuple of the list that was popped from and element was popped otherwise
1189
- #
1190
- # @see #blpop
1191
- def brpop(*args)
1192
- _bpop(:brpop, args)
1193
- end
1194
-
1195
- # Pop a value from a list, push it to another list and return it; or block
1196
- # until one is available.
1197
- #
1198
- # @param [String] source source key
1199
- # @param [String] destination destination key
1200
- # @param [Hash] options
1201
- # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
1202
- #
1203
- # @return [nil, String]
1204
- # - `nil` when the operation timed out
1205
- # - the element was popped and pushed otherwise
1206
- def brpoplpush(source, destination, options = {})
1207
- case options
1208
- when Integer
1209
- # Issue deprecation notice in obnoxious mode...
1210
- options = { :timeout => options }
1211
- end
1212
-
1213
- timeout = options[:timeout] || 0
1214
-
1215
- synchronize do |client|
1216
- command = [:brpoplpush, source, destination, timeout]
1217
- timeout += client.timeout if timeout > 0
1218
- client.call_with_timeout(command, timeout)
1219
- end
1220
- end
1221
-
1222
- # Get an element from a list by its index.
1223
- #
1224
- # @param [String] key
1225
- # @param [Fixnum] index
1226
- # @return [String]
1227
- def lindex(key, index)
1228
- synchronize do |client|
1229
- client.call([:lindex, key, index])
1230
- end
1231
- end
1232
-
1233
- # Insert an element before or after another element in a list.
1234
- #
1235
- # @param [String] key
1236
- # @param [String, Symbol] where `BEFORE` or `AFTER`
1237
- # @param [String] pivot reference element
1238
- # @param [String] value
1239
- # @return [Fixnum] length of the list after the insert operation, or `-1`
1240
- # when the element `pivot` was not found
1241
- def linsert(key, where, pivot, value)
1242
- synchronize do |client|
1243
- client.call([:linsert, key, where, pivot, value])
1244
- end
1245
- end
1246
-
1247
- # Get a range of elements from a list.
1248
- #
1249
- # @param [String] key
1250
- # @param [Fixnum] start start index
1251
- # @param [Fixnum] stop stop index
1252
- # @return [Array<String>]
1253
- def lrange(key, start, stop)
1254
- synchronize do |client|
1255
- client.call([:lrange, key, start, stop])
1256
- end
1257
- end
1258
-
1259
- # Remove elements from a list.
1260
- #
1261
- # @param [String] key
1262
- # @param [Fixnum] count number of elements to remove. Use a positive
1263
- # value to remove the first `count` occurrences of `value`. A negative
1264
- # value to remove the last `count` occurrences of `value`. Or zero, to
1265
- # remove all occurrences of `value` from the list.
1266
- # @param [String] value
1267
- # @return [Fixnum] the number of removed elements
1268
- def lrem(key, count, value)
1269
- synchronize do |client|
1270
- client.call([:lrem, key, count, value])
1271
- end
1272
- end
1273
-
1274
- # Set the value of an element in a list by its index.
1275
- #
1276
- # @param [String] key
1277
- # @param [Fixnum] index
1278
- # @param [String] value
1279
- # @return [String] `OK`
1280
- def lset(key, index, value)
1281
- synchronize do |client|
1282
- client.call([:lset, key, index, value])
1283
- end
1284
- end
1285
-
1286
- # Trim a list to the specified range.
1287
- #
1288
- # @param [String] key
1289
- # @param [Fixnum] start start index
1290
- # @param [Fixnum] stop stop index
1291
- # @return [String] `OK`
1292
- def ltrim(key, start, stop)
1293
- synchronize do |client|
1294
- client.call([:ltrim, key, start, stop])
1295
- end
1296
- end
1297
-
1298
- # Get the number of members in a set.
1299
- #
1300
- # @param [String] key
1301
- # @return [Fixnum]
1302
- def scard(key)
1303
- synchronize do |client|
1304
- client.call([:scard, key])
1305
- end
1306
- end
1307
-
1308
- # Add one or more members to a set.
1309
- #
1310
- # @param [String] key
1311
- # @param [String, Array<String>] member one member, or array of members
1312
- # @return [Boolean, Fixnum] `Boolean` when a single member is specified,
1313
- # holding whether or not adding the member succeeded, or `Fixnum` when an
1314
- # array of members is specified, holding the number of members that were
1315
- # successfully added
1316
- def sadd(key, member)
1317
- synchronize do |client|
1318
- client.call([:sadd, key, member]) do |reply|
1319
- if member.is_a? Array
1320
- # Variadic: return integer
1321
- reply
1322
- else
1323
- # Single argument: return boolean
1324
- Boolify.call(reply)
1325
- end
1326
- end
1327
- end
1328
- end
1329
-
1330
- # Remove one or more members from a set.
1331
- #
1332
- # @param [String] key
1333
- # @param [String, Array<String>] member one member, or array of members
1334
- # @return [Boolean, Fixnum] `Boolean` when a single member is specified,
1335
- # holding whether or not removing the member succeeded, or `Fixnum` when an
1336
- # array of members is specified, holding the number of members that were
1337
- # successfully removed
1338
- def srem(key, member)
1339
- synchronize do |client|
1340
- client.call([:srem, key, member]) do |reply|
1341
- if member.is_a? Array
1342
- # Variadic: return integer
1343
- reply
1344
- else
1345
- # Single argument: return boolean
1346
- Boolify.call(reply)
1347
- end
1348
- end
1349
- end
1350
- end
1351
-
1352
- # Remove and return one or more random member from a set.
1353
- #
1354
- # @param [String] key
1355
- # @return [String]
1356
- # @param [Fixnum] count
1357
- def spop(key, count = nil)
1358
- synchronize do |client|
1359
- if count.nil?
1360
- client.call([:spop, key])
1361
- else
1362
- client.call([:spop, key, count])
1363
- end
1364
- end
1365
- end
1366
-
1367
- # Get one or more random members from a set.
1368
- #
1369
- # @param [String] key
1370
- # @param [Fixnum] count
1371
- # @return [String]
1372
- def srandmember(key, count = nil)
1373
- synchronize do |client|
1374
- if count.nil?
1375
- client.call([:srandmember, key])
1376
- else
1377
- client.call([:srandmember, key, count])
1378
- end
1379
- end
1380
- end
1381
-
1382
- # Move a member from one set to another.
1383
- #
1384
- # @param [String] source source key
1385
- # @param [String] destination destination key
1386
- # @param [String] member member to move from `source` to `destination`
1387
- # @return [Boolean]
1388
- def smove(source, destination, member)
1389
- synchronize do |client|
1390
- client.call([:smove, source, destination, member], &Boolify)
1391
- end
1392
- end
1393
-
1394
- # Determine if a given value is a member of a set.
1395
- #
1396
- # @param [String] key
1397
- # @param [String] member
1398
- # @return [Boolean]
1399
- def sismember(key, member)
1400
- synchronize do |client|
1401
- client.call([:sismember, key, member], &Boolify)
1402
- end
1403
- end
1404
-
1405
- # Get all the members in a set.
1406
- #
1407
- # @param [String] key
1408
- # @return [Array<String>]
1409
- def smembers(key)
1410
- synchronize do |client|
1411
- client.call([:smembers, key])
1412
- end
1413
- end
1414
-
1415
- # Subtract multiple sets.
1416
- #
1417
- # @param [String, Array<String>] keys keys pointing to sets to subtract
1418
- # @return [Array<String>] members in the difference
1419
- def sdiff(*keys)
1420
- synchronize do |client|
1421
- client.call([:sdiff] + keys)
1422
- end
1423
- end
1424
-
1425
- # Subtract multiple sets and store the resulting set in a key.
1426
- #
1427
- # @param [String] destination destination key
1428
- # @param [String, Array<String>] keys keys pointing to sets to subtract
1429
- # @return [Fixnum] number of elements in the resulting set
1430
- def sdiffstore(destination, *keys)
1431
- synchronize do |client|
1432
- client.call([:sdiffstore, destination] + keys)
1433
- end
1434
- end
1435
-
1436
- # Intersect multiple sets.
1437
- #
1438
- # @param [String, Array<String>] keys keys pointing to sets to intersect
1439
- # @return [Array<String>] members in the intersection
1440
- def sinter(*keys)
1441
- synchronize do |client|
1442
- client.call([:sinter] + keys)
1443
- end
1444
- end
1445
-
1446
- # Intersect multiple sets and store the resulting set in a key.
1447
- #
1448
- # @param [String] destination destination key
1449
- # @param [String, Array<String>] keys keys pointing to sets to intersect
1450
- # @return [Fixnum] number of elements in the resulting set
1451
- def sinterstore(destination, *keys)
1452
- synchronize do |client|
1453
- client.call([:sinterstore, destination] + keys)
1454
- end
1455
- end
1456
-
1457
- # Add multiple sets.
1458
- #
1459
- # @param [String, Array<String>] keys keys pointing to sets to unify
1460
- # @return [Array<String>] members in the union
1461
- def sunion(*keys)
1462
- synchronize do |client|
1463
- client.call([:sunion] + keys)
1464
- end
1465
- end
1466
-
1467
- # Add multiple sets and store the resulting set in a key.
1468
- #
1469
- # @param [String] destination destination key
1470
- # @param [String, Array<String>] keys keys pointing to sets to unify
1471
- # @return [Fixnum] number of elements in the resulting set
1472
- def sunionstore(destination, *keys)
1473
- synchronize do |client|
1474
- client.call([:sunionstore, destination] + keys)
1475
- end
1476
- end
1477
-
1478
- # Get the number of members in a sorted set.
1479
- #
1480
- # @example
1481
- # redis.zcard("zset")
1482
- # # => 4
1483
- #
1484
- # @param [String] key
1485
- # @return [Fixnum]
1486
- def zcard(key)
1487
- synchronize do |client|
1488
- client.call([:zcard, key])
1489
- end
1490
- end
1491
-
1492
- # Add one or more members to a sorted set, or update the score for members
1493
- # that already exist.
1494
- #
1495
- # @example Add a single `[score, member]` pair to a sorted set
1496
- # redis.zadd("zset", 32.0, "member")
1497
- # @example Add an array of `[score, member]` pairs to a sorted set
1498
- # redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
1499
- #
1500
- # @param [String] key
1501
- # @param [[Float, String], Array<[Float, String]>] args
1502
- # - a single `[score, member]` pair
1503
- # - an array of `[score, member]` pairs
1504
- # @param [Hash] options
1505
- # - `:xx => true`: Only update elements that already exist (never
1506
- # add elements)
1507
- # - `:nx => true`: Don't update already existing elements (always
1508
- # add new elements)
1509
- # - `:ch => true`: Modify the return value from the number of new
1510
- # elements added, to the total number of elements changed (CH is an
1511
- # abbreviation of changed); changed elements are new elements added
1512
- # and elements already existing for which the score was updated
1513
- # - `:incr => true`: When this option is specified ZADD acts like
1514
- # ZINCRBY; only one score-element pair can be specified in this mode
1515
- #
1516
- # @return [Boolean, Fixnum, Float]
1517
- # - `Boolean` when a single pair is specified, holding whether or not it was
1518
- # **added** to the sorted set.
1519
- # - `Fixnum` when an array of pairs is specified, holding the number of
1520
- # pairs that were **added** to the sorted set.
1521
- # - `Float` when option :incr is specified, holding the score of the member
1522
- # after incrementing it.
1523
- def zadd(key, *args) #, options
1524
- zadd_options = []
1525
- if args.last.is_a?(Hash)
1526
- options = args.pop
1527
-
1528
- nx = options[:nx]
1529
- zadd_options << "NX" if nx
1530
-
1531
- xx = options[:xx]
1532
- zadd_options << "XX" if xx
1533
-
1534
- ch = options[:ch]
1535
- zadd_options << "CH" if ch
1536
-
1537
- incr = options[:incr]
1538
- zadd_options << "INCR" if incr
1539
- end
1540
-
1541
- synchronize do |client|
1542
- if args.size == 1 && args[0].is_a?(Array)
1543
- # Variadic: return float if INCR, integer if !INCR
1544
- client.call([:zadd, key] + zadd_options + args[0], &(incr ? Floatify : nil))
1545
- elsif args.size == 2
1546
- # Single pair: return float if INCR, boolean if !INCR
1547
- client.call([:zadd, key] + zadd_options + args, &(incr ? Floatify : Boolify))
1548
- else
1549
- raise ArgumentError, "wrong number of arguments"
1550
- end
1551
- end
1552
- end
1553
-
1554
- # Increment the score of a member in a sorted set.
1555
- #
1556
- # @example
1557
- # redis.zincrby("zset", 32.0, "a")
1558
- # # => 64.0
1559
- #
1560
- # @param [String] key
1561
- # @param [Float] increment
1562
- # @param [String] member
1563
- # @return [Float] score of the member after incrementing it
1564
- def zincrby(key, increment, member)
1565
- synchronize do |client|
1566
- client.call([:zincrby, key, increment, member], &Floatify)
1567
- end
1568
- end
1569
-
1570
- # Remove one or more members from a sorted set.
1571
- #
1572
- # @example Remove a single member from a sorted set
1573
- # redis.zrem("zset", "a")
1574
- # @example Remove an array of members from a sorted set
1575
- # redis.zrem("zset", ["a", "b"])
1576
- #
1577
- # @param [String] key
1578
- # @param [String, Array<String>] member
1579
- # - a single member
1580
- # - an array of members
1581
- #
1582
- # @return [Boolean, Fixnum]
1583
- # - `Boolean` when a single member is specified, holding whether or not it
1584
- # was removed from the sorted set
1585
- # - `Fixnum` when an array of pairs is specified, holding the number of
1586
- # members that were removed to the sorted set
1587
- def zrem(key, member)
1588
- synchronize do |client|
1589
- client.call([:zrem, key, member]) do |reply|
1590
- if member.is_a? Array
1591
- # Variadic: return integer
1592
- reply
1593
- else
1594
- # Single argument: return boolean
1595
- Boolify.call(reply)
1596
- end
1597
- end
1598
- end
1599
- end
1600
-
1601
- # Get the score associated with the given member in a sorted set.
1602
- #
1603
- # @example Get the score for member "a"
1604
- # redis.zscore("zset", "a")
1605
- # # => 32.0
1606
- #
1607
- # @param [String] key
1608
- # @param [String] member
1609
- # @return [Float] score of the member
1610
- def zscore(key, member)
1611
- synchronize do |client|
1612
- client.call([:zscore, key, member], &Floatify)
1613
- end
1614
- end
1615
-
1616
- # Return a range of members in a sorted set, by index.
1617
- #
1618
- # @example Retrieve all members from a sorted set
1619
- # redis.zrange("zset", 0, -1)
1620
- # # => ["a", "b"]
1621
- # @example Retrieve all members and their scores from a sorted set
1622
- # redis.zrange("zset", 0, -1, :with_scores => true)
1623
- # # => [["a", 32.0], ["b", 64.0]]
1624
- #
1625
- # @param [String] key
1626
- # @param [Fixnum] start start index
1627
- # @param [Fixnum] stop stop index
1628
- # @param [Hash] options
1629
- # - `:with_scores => true`: include scores in output
1630
- #
1631
- # @return [Array<String>, Array<[String, Float]>]
1632
- # - when `:with_scores` is not specified, an array of members
1633
- # - when `:with_scores` is specified, an array with `[member, score]` pairs
1634
- def zrange(key, start, stop, options = {})
1635
- args = []
1636
-
1637
- with_scores = options[:with_scores] || options[:withscores]
1638
-
1639
- if with_scores
1640
- args << "WITHSCORES"
1641
- block = FloatifyPairs
1642
- end
1643
-
1644
- synchronize do |client|
1645
- client.call([:zrange, key, start, stop] + args, &block)
1646
- end
1647
- end
1648
-
1649
- # Return a range of members in a sorted set, by index, with scores ordered
1650
- # from high to low.
1651
- #
1652
- # @example Retrieve all members from a sorted set
1653
- # redis.zrevrange("zset", 0, -1)
1654
- # # => ["b", "a"]
1655
- # @example Retrieve all members and their scores from a sorted set
1656
- # redis.zrevrange("zset", 0, -1, :with_scores => true)
1657
- # # => [["b", 64.0], ["a", 32.0]]
1658
- #
1659
- # @see #zrange
1660
- def zrevrange(key, start, stop, options = {})
1661
- args = []
1662
-
1663
- with_scores = options[:with_scores] || options[:withscores]
1664
-
1665
- if with_scores
1666
- args << "WITHSCORES"
1667
- block = FloatifyPairs
1668
- end
1669
-
1670
- synchronize do |client|
1671
- client.call([:zrevrange, key, start, stop] + args, &block)
1672
- end
1673
- end
1674
-
1675
- # Determine the index of a member in a sorted set.
1676
- #
1677
- # @param [String] key
1678
- # @param [String] member
1679
- # @return [Fixnum]
1680
- def zrank(key, member)
1681
- synchronize do |client|
1682
- client.call([:zrank, key, member])
1683
- end
1684
- end
1685
-
1686
- # Determine the index of a member in a sorted set, with scores ordered from
1687
- # high to low.
1688
- #
1689
- # @param [String] key
1690
- # @param [String] member
1691
- # @return [Fixnum]
1692
- def zrevrank(key, member)
1693
- synchronize do |client|
1694
- client.call([:zrevrank, key, member])
1695
- end
1696
- end
1697
-
1698
- # Remove all members in a sorted set within the given indexes.
1699
- #
1700
- # @example Remove first 5 members
1701
- # redis.zremrangebyrank("zset", 0, 4)
1702
- # # => 5
1703
- # @example Remove last 5 members
1704
- # redis.zremrangebyrank("zset", -5, -1)
1705
- # # => 5
1706
- #
1707
- # @param [String] key
1708
- # @param [Fixnum] start start index
1709
- # @param [Fixnum] stop stop index
1710
- # @return [Fixnum] number of members that were removed
1711
- def zremrangebyrank(key, start, stop)
1712
- synchronize do |client|
1713
- client.call([:zremrangebyrank, key, start, stop])
1714
- end
1715
- end
1716
-
1717
- # Count the members, with the same score in a sorted set, within the given lexicographical range.
1718
- #
1719
- # @example Count members matching a
1720
- # redis.zlexcount("zset", "[a", "[a\xff")
1721
- # # => 1
1722
- # @example Count members matching a-z
1723
- # redis.zlexcount("zset", "[a", "[z\xff")
1724
- # # => 26
1725
- #
1726
- # @param [String] key
1727
- # @param [String] min
1728
- # - inclusive minimum is specified by prefixing `(`
1729
- # - exclusive minimum is specified by prefixing `[`
1730
- # @param [String] max
1731
- # - inclusive maximum is specified by prefixing `(`
1732
- # - exclusive maximum is specified by prefixing `[`
1733
- #
1734
- # @return [Fixnum] number of members within the specified lexicographical range
1735
- def zlexcount(key, min, max)
1736
- synchronize do |client|
1737
- client.call([:zlexcount, key, min, max])
1738
- end
1739
- end
1740
-
1741
- # Return a range of members with the same score in a sorted set, by lexicographical ordering
1742
- #
1743
- # @example Retrieve members matching a
1744
- # redis.zrangebylex("zset", "[a", "[a\xff")
1745
- # # => ["aaren", "aarika", "abagael", "abby"]
1746
- # @example Retrieve the first 2 members matching a
1747
- # redis.zrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
1748
- # # => ["aaren", "aarika"]
1749
- #
1750
- # @param [String] key
1751
- # @param [String] min
1752
- # - inclusive minimum is specified by prefixing `(`
1753
- # - exclusive minimum is specified by prefixing `[`
1754
- # @param [String] max
1755
- # - inclusive maximum is specified by prefixing `(`
1756
- # - exclusive maximum is specified by prefixing `[`
1757
- # @param [Hash] options
1758
- # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
1759
- # `count` members
1760
- #
1761
- # @return [Array<String>, Array<[String, Float]>]
1762
- def zrangebylex(key, min, max, options = {})
1763
- args = []
1764
-
1765
- limit = options[:limit]
1766
- args.concat(["LIMIT"] + limit) if limit
1767
-
1768
- synchronize do |client|
1769
- client.call([:zrangebylex, key, min, max] + args)
1770
- end
1771
- end
1772
-
1773
- # Return a range of members with the same score in a sorted set, by reversed lexicographical ordering.
1774
- # Apart from the reversed ordering, #zrevrangebylex is similar to #zrangebylex.
1775
- #
1776
- # @example Retrieve members matching a
1777
- # redis.zrevrangebylex("zset", "[a", "[a\xff")
1778
- # # => ["abbygail", "abby", "abagael", "aaren"]
1779
- # @example Retrieve the last 2 members matching a
1780
- # redis.zrevrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
1781
- # # => ["abbygail", "abby"]
1782
- #
1783
- # @see #zrangebylex
1784
- def zrevrangebylex(key, max, min, options = {})
1785
- args = []
1786
-
1787
- limit = options[:limit]
1788
- args.concat(["LIMIT"] + limit) if limit
1789
-
1790
- synchronize do |client|
1791
- client.call([:zrevrangebylex, key, max, min] + args)
1792
- end
1793
- end
1794
-
1795
- # Return a range of members in a sorted set, by score.
1796
- #
1797
- # @example Retrieve members with score `>= 5` and `< 100`
1798
- # redis.zrangebyscore("zset", "5", "(100")
1799
- # # => ["a", "b"]
1800
- # @example Retrieve the first 2 members with score `>= 0`
1801
- # redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
1802
- # # => ["a", "b"]
1803
- # @example Retrieve members and their scores with scores `> 5`
1804
- # redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
1805
- # # => [["a", 32.0], ["b", 64.0]]
1806
- #
1807
- # @param [String] key
1808
- # @param [String] min
1809
- # - inclusive minimum score is specified verbatim
1810
- # - exclusive minimum score is specified by prefixing `(`
1811
- # @param [String] max
1812
- # - inclusive maximum score is specified verbatim
1813
- # - exclusive maximum score is specified by prefixing `(`
1814
- # @param [Hash] options
1815
- # - `:with_scores => true`: include scores in output
1816
- # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
1817
- # `count` members
1818
- #
1819
- # @return [Array<String>, Array<[String, Float]>]
1820
- # - when `:with_scores` is not specified, an array of members
1821
- # - when `:with_scores` is specified, an array with `[member, score]` pairs
1822
- def zrangebyscore(key, min, max, options = {})
1823
- args = []
1824
-
1825
- with_scores = options[:with_scores] || options[:withscores]
1826
-
1827
- if with_scores
1828
- args << "WITHSCORES"
1829
- block = FloatifyPairs
1830
- end
1831
-
1832
- limit = options[:limit]
1833
- args.concat(["LIMIT"] + limit) if limit
1834
-
1835
- synchronize do |client|
1836
- client.call([:zrangebyscore, key, min, max] + args, &block)
1837
- end
1838
- end
1839
-
1840
- # Return a range of members in a sorted set, by score, with scores ordered
1841
- # from high to low.
1842
- #
1843
- # @example Retrieve members with score `< 100` and `>= 5`
1844
- # redis.zrevrangebyscore("zset", "(100", "5")
1845
- # # => ["b", "a"]
1846
- # @example Retrieve the first 2 members with score `<= 0`
1847
- # redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
1848
- # # => ["b", "a"]
1849
- # @example Retrieve members and their scores with scores `> 5`
1850
- # redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
1851
- # # => [["b", 64.0], ["a", 32.0]]
1852
- #
1853
- # @see #zrangebyscore
1854
- def zrevrangebyscore(key, max, min, options = {})
1855
- args = []
1856
-
1857
- with_scores = options[:with_scores] || options[:withscores]
1858
-
1859
- if with_scores
1860
- args << ["WITHSCORES"]
1861
- block = FloatifyPairs
1862
- end
1863
-
1864
- limit = options[:limit]
1865
- args.concat(["LIMIT"] + limit) if limit
1866
-
1867
- synchronize do |client|
1868
- client.call([:zrevrangebyscore, key, max, min] + args, &block)
1869
- end
1870
- end
1871
-
1872
- # Remove all members in a sorted set within the given scores.
1873
- #
1874
- # @example Remove members with score `>= 5` and `< 100`
1875
- # redis.zremrangebyscore("zset", "5", "(100")
1876
- # # => 2
1877
- # @example Remove members with scores `> 5`
1878
- # redis.zremrangebyscore("zset", "(5", "+inf")
1879
- # # => 2
1880
- #
1881
- # @param [String] key
1882
- # @param [String] min
1883
- # - inclusive minimum score is specified verbatim
1884
- # - exclusive minimum score is specified by prefixing `(`
1885
- # @param [String] max
1886
- # - inclusive maximum score is specified verbatim
1887
- # - exclusive maximum score is specified by prefixing `(`
1888
- # @return [Fixnum] number of members that were removed
1889
- def zremrangebyscore(key, min, max)
1890
- synchronize do |client|
1891
- client.call([:zremrangebyscore, key, min, max])
1892
- end
1893
- end
1894
-
1895
- # Count the members in a sorted set with scores within the given values.
1896
- #
1897
- # @example Count members with score `>= 5` and `< 100`
1898
- # redis.zcount("zset", "5", "(100")
1899
- # # => 2
1900
- # @example Count members with scores `> 5`
1901
- # redis.zcount("zset", "(5", "+inf")
1902
- # # => 2
1903
- #
1904
- # @param [String] key
1905
- # @param [String] min
1906
- # - inclusive minimum score is specified verbatim
1907
- # - exclusive minimum score is specified by prefixing `(`
1908
- # @param [String] max
1909
- # - inclusive maximum score is specified verbatim
1910
- # - exclusive maximum score is specified by prefixing `(`
1911
- # @return [Fixnum] number of members in within the specified range
1912
- def zcount(key, min, max)
1913
- synchronize do |client|
1914
- client.call([:zcount, key, min, max])
1915
- end
1916
- end
1917
-
1918
- # Intersect multiple sorted sets and store the resulting sorted set in a new
1919
- # key.
1920
- #
1921
- # @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
1922
- # redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
1923
- # # => 4
1924
- #
1925
- # @param [String] destination destination key
1926
- # @param [Array<String>] keys source keys
1927
- # @param [Hash] options
1928
- # - `:weights => [Float, Float, ...]`: weights to associate with source
1929
- # sorted sets
1930
- # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
1931
- # @return [Fixnum] number of elements in the resulting sorted set
1932
- def zinterstore(destination, keys, options = {})
1933
- args = []
1934
-
1935
- weights = options[:weights]
1936
- args.concat(["WEIGHTS"] + weights) if weights
1937
-
1938
- aggregate = options[:aggregate]
1939
- args.concat(["AGGREGATE", aggregate]) if aggregate
1940
-
1941
- synchronize do |client|
1942
- client.call([:zinterstore, destination, keys.size] + keys + args)
1943
- end
1944
- end
1945
-
1946
- # Add multiple sorted sets and store the resulting sorted set in a new key.
1947
- #
1948
- # @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
1949
- # redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
1950
- # # => 8
1951
- #
1952
- # @param [String] destination destination key
1953
- # @param [Array<String>] keys source keys
1954
- # @param [Hash] options
1955
- # - `:weights => [Float, Float, ...]`: weights to associate with source
1956
- # sorted sets
1957
- # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
1958
- # @return [Fixnum] number of elements in the resulting sorted set
1959
- def zunionstore(destination, keys, options = {})
1960
- args = []
1961
-
1962
- weights = options[:weights]
1963
- args.concat(["WEIGHTS"] + weights) if weights
1964
-
1965
- aggregate = options[:aggregate]
1966
- args.concat(["AGGREGATE", aggregate]) if aggregate
1967
-
1968
- synchronize do |client|
1969
- client.call([:zunionstore, destination, keys.size] + keys + args)
1970
- end
1971
- end
1972
-
1973
- # Get the number of fields in a hash.
1974
- #
1975
- # @param [String] key
1976
- # @return [Fixnum] number of fields in the hash
1977
- def hlen(key)
1978
- synchronize do |client|
1979
- client.call([:hlen, key])
1980
- end
1981
- end
1982
-
1983
- # Set the string value of a hash field.
1984
- #
1985
- # @param [String] key
1986
- # @param [String] field
1987
- # @param [String] value
1988
- # @return [Boolean] whether or not the field was **added** to the hash
1989
- def hset(key, field, value)
1990
- synchronize do |client|
1991
- client.call([:hset, key, field, value], &Boolify)
1992
- end
1993
- end
1994
-
1995
- # Set the value of a hash field, only if the field does not exist.
1996
- #
1997
- # @param [String] key
1998
- # @param [String] field
1999
- # @param [String] value
2000
- # @return [Boolean] whether or not the field was **added** to the hash
2001
- def hsetnx(key, field, value)
2002
- synchronize do |client|
2003
- client.call([:hsetnx, key, field, value], &Boolify)
2004
- end
2005
- end
2006
-
2007
- # Set one or more hash values.
2008
- #
2009
- # @example
2010
- # redis.hmset("hash", "f1", "v1", "f2", "v2")
2011
- # # => "OK"
2012
- #
2013
- # @param [String] key
2014
- # @param [Array<String>] attrs array of fields and values
2015
- # @return [String] `"OK"`
2016
- #
2017
- # @see #mapped_hmset
2018
- def hmset(key, *attrs)
2019
- synchronize do |client|
2020
- client.call([:hmset, key] + attrs)
2021
- end
2022
- end
2023
-
2024
- # Set one or more hash values.
2025
- #
2026
- # @example
2027
- # redis.mapped_hmset("hash", { "f1" => "v1", "f2" => "v2" })
2028
- # # => "OK"
2029
- #
2030
- # @param [String] key
2031
- # @param [Hash] hash a non-empty hash with fields mapping to values
2032
- # @return [String] `"OK"`
2033
- #
2034
- # @see #hmset
2035
- def mapped_hmset(key, hash)
2036
- hmset(key, hash.to_a.flatten)
2037
- end
2038
-
2039
- # Get the value of a hash field.
2040
- #
2041
- # @param [String] key
2042
- # @param [String] field
2043
- # @return [String]
2044
- def hget(key, field)
2045
- synchronize do |client|
2046
- client.call([:hget, key, field])
2047
- end
2048
- end
2049
-
2050
- # Get the values of all the given hash fields.
2051
- #
2052
- # @example
2053
- # redis.hmget("hash", "f1", "f2")
2054
- # # => ["v1", "v2"]
2055
- #
2056
- # @param [String] key
2057
- # @param [Array<String>] fields array of fields
2058
- # @return [Array<String>] an array of values for the specified fields
2059
- #
2060
- # @see #mapped_hmget
2061
- def hmget(key, *fields, &blk)
2062
- synchronize do |client|
2063
- client.call([:hmget, key] + fields, &blk)
2064
- end
2065
- end
2066
-
2067
- # Get the values of all the given hash fields.
2068
- #
2069
- # @example
2070
- # redis.mapped_hmget("hash", "f1", "f2")
2071
- # # => { "f1" => "v1", "f2" => "v2" }
2072
- #
2073
- # @param [String] key
2074
- # @param [Array<String>] fields array of fields
2075
- # @return [Hash] a hash mapping the specified fields to their values
2076
- #
2077
- # @see #hmget
2078
- def mapped_hmget(key, *fields)
2079
- hmget(key, *fields) do |reply|
2080
- if reply.kind_of?(Array)
2081
- Hash[fields.zip(reply)]
2082
- else
2083
- reply
2084
- end
2085
- end
2086
- end
2087
-
2088
- # Delete one or more hash fields.
2089
- #
2090
- # @param [String] key
2091
- # @param [String, Array<String>] field
2092
- # @return [Fixnum] the number of fields that were removed from the hash
2093
- def hdel(key, field)
2094
- synchronize do |client|
2095
- client.call([:hdel, key, field])
2096
- end
2097
- end
2098
-
2099
- # Determine if a hash field exists.
2100
- #
2101
- # @param [String] key
2102
- # @param [String] field
2103
- # @return [Boolean] whether or not the field exists in the hash
2104
- def hexists(key, field)
2105
- synchronize do |client|
2106
- client.call([:hexists, key, field], &Boolify)
2107
- end
2108
- end
2109
-
2110
- # Increment the integer value of a hash field by the given integer number.
2111
- #
2112
- # @param [String] key
2113
- # @param [String] field
2114
- # @param [Fixnum] increment
2115
- # @return [Fixnum] value of the field after incrementing it
2116
- def hincrby(key, field, increment)
2117
- synchronize do |client|
2118
- client.call([:hincrby, key, field, increment])
2119
- end
2120
- end
2121
-
2122
- # Increment the numeric value of a hash field by the given float number.
2123
- #
2124
- # @param [String] key
2125
- # @param [String] field
2126
- # @param [Float] increment
2127
- # @return [Float] value of the field after incrementing it
2128
- def hincrbyfloat(key, field, increment)
2129
- synchronize do |client|
2130
- client.call([:hincrbyfloat, key, field, increment], &Floatify)
2131
- end
2132
- end
2133
-
2134
- # Get all the fields in a hash.
2135
- #
2136
- # @param [String] key
2137
- # @return [Array<String>]
2138
- def hkeys(key)
2139
- synchronize do |client|
2140
- client.call([:hkeys, key])
2141
- end
2142
- end
2143
-
2144
- # Get all the values in a hash.
2145
- #
2146
- # @param [String] key
2147
- # @return [Array<String>]
2148
- def hvals(key)
2149
- synchronize do |client|
2150
- client.call([:hvals, key])
2151
- end
2152
- end
2153
-
2154
- # Get all the fields and values in a hash.
2155
- #
2156
- # @param [String] key
2157
- # @return [Hash<String, String>]
2158
- def hgetall(key)
2159
- synchronize do |client|
2160
- client.call([:hgetall, key], &Hashify)
2161
- end
2162
- end
2163
-
2164
- # Post a message to a channel.
2165
- def publish(channel, message)
2166
- synchronize do |client|
2167
- client.call([:publish, channel, message])
2168
- end
2169
- end
2170
-
2171
- def subscribed?
2172
- synchronize do |client|
2173
- client.kind_of? SubscribedClient
2174
- end
2175
- end
2176
-
2177
- # Listen for messages published to the given channels.
2178
- def subscribe(*channels, &block)
2179
- synchronize do |client|
2180
- _subscription(:subscribe, 0, channels, block)
2181
- end
2182
- end
2183
-
2184
- # Listen for messages published to the given channels. Throw a timeout error if there is no messages for a timeout period.
2185
- def subscribe_with_timeout(timeout, *channels, &block)
2186
- synchronize do |client|
2187
- _subscription(:subscribe_with_timeout, timeout, channels, block)
2188
- end
2189
- end
2190
-
2191
- # Stop listening for messages posted to the given channels.
2192
- def unsubscribe(*channels)
2193
- synchronize do |client|
2194
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2195
- client.unsubscribe(*channels)
2196
- end
2197
- end
2198
-
2199
- # Listen for messages published to channels matching the given patterns.
2200
- def psubscribe(*channels, &block)
2201
- synchronize do |client|
2202
- _subscription(:psubscribe, 0, channels, block)
2203
- end
2204
- end
2205
-
2206
- # Listen for messages published to channels matching the given patterns. Throw a timeout error if there is no messages for a timeout period.
2207
- def psubscribe_with_timeout(timeout, *channels, &block)
2208
- synchronize do |client|
2209
- _subscription(:psubscribe_with_timeout, timeout, channels, block)
2210
- end
2211
- end
2212
-
2213
- # Stop listening for messages posted to channels matching the given patterns.
2214
- def punsubscribe(*channels)
2215
- synchronize do |client|
2216
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2217
- client.punsubscribe(*channels)
2218
- end
2219
- end
2220
-
2221
- # Inspect the state of the Pub/Sub subsystem.
2222
- # Possible subcommands: channels, numsub, numpat.
2223
- def pubsub(subcommand, *args)
2224
- synchronize do |client|
2225
- client.call([:pubsub, subcommand] + args)
2226
- end
2227
- end
2228
-
2229
- # Watch the given keys to determine execution of the MULTI/EXEC block.
2230
- #
2231
- # Using a block is optional, but is necessary for thread-safety.
2232
- #
2233
- # An `#unwatch` is automatically issued if an exception is raised within the
2234
- # block that is a subclass of StandardError and is not a ConnectionError.
2235
- #
2236
- # @example With a block
2237
- # redis.watch("key") do
2238
- # if redis.get("key") == "some value"
2239
- # redis.multi do |multi|
2240
- # multi.set("key", "other value")
2241
- # multi.incr("counter")
2242
- # end
2243
- # else
2244
- # redis.unwatch
2245
- # end
2246
- # end
2247
- # # => ["OK", 6]
2248
- #
2249
- # @example Without a block
2250
- # redis.watch("key")
2251
- # # => "OK"
2252
- #
2253
- # @param [String, Array<String>] keys one or more keys to watch
2254
- # @return [Object] if using a block, returns the return value of the block
2255
- # @return [String] if not using a block, returns `OK`
2256
- #
2257
- # @see #unwatch
2258
- # @see #multi
2259
- def watch(*keys)
2260
- synchronize do |client|
2261
- res = client.call([:watch] + keys)
2262
-
2263
- if block_given?
2264
- begin
2265
- yield(self)
2266
- rescue ConnectionError
2267
- raise
2268
- rescue StandardError
2269
- unwatch
2270
- raise
2271
- end
2272
- else
2273
- res
2274
- end
2275
- end
2276
- end
2277
-
2278
- # Forget about all watched keys.
2279
- #
2280
- # @return [String] `OK`
2281
- #
2282
- # @see #watch
2283
- # @see #multi
2284
- def unwatch
2285
- synchronize do |client|
2286
- client.call([:unwatch])
2287
- end
2288
- end
2289
-
2290
- def pipelined
2291
- synchronize do |client|
2292
- begin
2293
- original, @client = @client, Pipeline.new
2294
- yield(self)
2295
- original.call_pipeline(@client)
2296
- ensure
2297
- @client = original
2298
- end
2299
- end
2300
- end
2301
-
2302
- # Mark the start of a transaction block.
2303
- #
2304
- # Passing a block is optional.
2305
- #
2306
- # @example With a block
2307
- # redis.multi do |multi|
2308
- # multi.set("key", "value")
2309
- # multi.incr("counter")
2310
- # end # => ["OK", 6]
2311
- #
2312
- # @example Without a block
2313
- # redis.multi
2314
- # # => "OK"
2315
- # redis.set("key", "value")
2316
- # # => "QUEUED"
2317
- # redis.incr("counter")
2318
- # # => "QUEUED"
2319
- # redis.exec
2320
- # # => ["OK", 6]
2321
- #
2322
- # @yield [multi] the commands that are called inside this block are cached
2323
- # and written to the server upon returning from it
2324
- # @yieldparam [Redis] multi `self`
2325
- #
2326
- # @return [String, Array<...>]
2327
- # - when a block is not given, `OK`
2328
- # - when a block is given, an array with replies
2329
- #
2330
- # @see #watch
2331
- # @see #unwatch
2332
- def multi
2333
- synchronize do |client|
2334
- if !block_given?
2335
- client.call([:multi])
2336
- else
2337
- begin
2338
- pipeline = Pipeline::Multi.new
2339
- original, @client = @client, pipeline
2340
- yield(self)
2341
- original.call_pipeline(pipeline)
2342
- ensure
2343
- @client = original
2344
- end
2345
- end
2346
- end
2347
- end
2348
-
2349
- # Execute all commands issued after MULTI.
2350
- #
2351
- # Only call this method when `#multi` was called **without** a block.
2352
- #
2353
- # @return [nil, Array<...>]
2354
- # - when commands were not executed, `nil`
2355
- # - when commands were executed, an array with their replies
2356
- #
2357
- # @see #multi
2358
- # @see #discard
2359
- def exec
2360
- synchronize do |client|
2361
- client.call([:exec])
2362
- end
2363
- end
2364
-
2365
- # Discard all commands issued after MULTI.
2366
- #
2367
- # Only call this method when `#multi` was called **without** a block.
2368
- #
2369
- # @return [String] `"OK"`
2370
- #
2371
- # @see #multi
2372
- # @see #exec
2373
- def discard
2374
- synchronize do |client|
2375
- client.call([:discard])
2376
- end
2377
- end
2378
-
2379
- # Control remote script registry.
2380
- #
2381
- # @example Load a script
2382
- # sha = redis.script(:load, "return 1")
2383
- # # => <sha of this script>
2384
- # @example Check if a script exists
2385
- # redis.script(:exists, sha)
2386
- # # => true
2387
- # @example Check if multiple scripts exist
2388
- # redis.script(:exists, [sha, other_sha])
2389
- # # => [true, false]
2390
- # @example Flush the script registry
2391
- # redis.script(:flush)
2392
- # # => "OK"
2393
- # @example Kill a running script
2394
- # redis.script(:kill)
2395
- # # => "OK"
2396
- #
2397
- # @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
2398
- # @param [Array<String>] args depends on subcommand
2399
- # @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
2400
- #
2401
- # @see #eval
2402
- # @see #evalsha
2403
- def script(subcommand, *args)
2404
- subcommand = subcommand.to_s.downcase
2405
-
2406
- if subcommand == "exists"
2407
- synchronize do |client|
2408
- arg = args.first
2409
-
2410
- client.call([:script, :exists, arg]) do |reply|
2411
- reply = reply.map { |r| Boolify.call(r) }
2412
-
2413
- if arg.is_a?(Array)
2414
- reply
2415
- else
2416
- reply.first
2417
- end
2418
- end
2419
- end
2420
- else
2421
- synchronize do |client|
2422
- client.call([:script, subcommand] + args)
2423
- end
2424
- end
2425
- end
2426
-
2427
- def _eval(cmd, args)
2428
- script = args.shift
2429
- options = args.pop if args.last.is_a?(Hash)
2430
- options ||= {}
2431
-
2432
- keys = args.shift || options[:keys] || []
2433
- argv = args.shift || options[:argv] || []
2434
-
2435
- synchronize do |client|
2436
- client.call([cmd, script, keys.length] + keys + argv)
2437
- end
2438
- end
2439
-
2440
- # Evaluate Lua script.
2441
- #
2442
- # @example EVAL without KEYS nor ARGV
2443
- # redis.eval("return 1")
2444
- # # => 1
2445
- # @example EVAL with KEYS and ARGV as array arguments
2446
- # redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
2447
- # # => [["k1", "k2"], ["a1", "a2"]]
2448
- # @example EVAL with KEYS and ARGV in a hash argument
2449
- # redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
2450
- # # => [["k1", "k2"], ["a1", "a2"]]
2451
- #
2452
- # @param [Array<String>] keys optional array with keys to pass to the script
2453
- # @param [Array<String>] argv optional array with arguments to pass to the script
2454
- # @param [Hash] options
2455
- # - `:keys => Array<String>`: optional array with keys to pass to the script
2456
- # - `:argv => Array<String>`: optional array with arguments to pass to the script
2457
- # @return depends on the script
2458
- #
2459
- # @see #script
2460
- # @see #evalsha
2461
- def eval(*args)
2462
- _eval(:eval, args)
2463
- end
2464
-
2465
- # Evaluate Lua script by its SHA.
2466
- #
2467
- # @example EVALSHA without KEYS nor ARGV
2468
- # redis.evalsha(sha)
2469
- # # => <depends on script>
2470
- # @example EVALSHA with KEYS and ARGV as array arguments
2471
- # redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
2472
- # # => <depends on script>
2473
- # @example EVALSHA with KEYS and ARGV in a hash argument
2474
- # redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
2475
- # # => <depends on script>
2476
- #
2477
- # @param [Array<String>] keys optional array with keys to pass to the script
2478
- # @param [Array<String>] argv optional array with arguments to pass to the script
2479
- # @param [Hash] options
2480
- # - `:keys => Array<String>`: optional array with keys to pass to the script
2481
- # - `:argv => Array<String>`: optional array with arguments to pass to the script
2482
- # @return depends on the script
2483
- #
2484
- # @see #script
2485
- # @see #eval
2486
- def evalsha(*args)
2487
- _eval(:evalsha, args)
2488
- end
2489
-
2490
- def _scan(command, cursor, args, options = {}, &block)
2491
- # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
2492
-
2493
- args << cursor
2494
-
2495
- if match = options[:match]
2496
- args.concat(["MATCH", match])
2497
- end
2498
-
2499
- if count = options[:count]
2500
- args.concat(["COUNT", count])
2501
- end
2502
-
2503
- synchronize do |client|
2504
- client.call([command] + args, &block)
2505
- end
2506
- end
2507
-
2508
- # Scan the keyspace
2509
- #
2510
- # @example Retrieve the first batch of keys
2511
- # redis.scan(0)
2512
- # # => ["4", ["key:21", "key:47", "key:42"]]
2513
- # @example Retrieve a batch of keys matching a pattern
2514
- # redis.scan(4, :match => "key:1?")
2515
- # # => ["92", ["key:13", "key:18"]]
2516
- #
2517
- # @param [String, Integer] cursor the cursor of the iteration
2518
- # @param [Hash] options
2519
- # - `:match => String`: only return keys matching the pattern
2520
- # - `:count => Integer`: return count keys at most per iteration
2521
- #
2522
- # @return [String, Array<String>] the next cursor and all found keys
2523
- def scan(cursor, options={})
2524
- _scan(:scan, cursor, [], options)
2525
- end
2526
-
2527
- # Scan the keyspace
2528
- #
2529
- # @example Retrieve all of the keys (with possible duplicates)
2530
- # redis.scan_each.to_a
2531
- # # => ["key:21", "key:47", "key:42"]
2532
- # @example Execute block for each key matching a pattern
2533
- # redis.scan_each(:match => "key:1?") {|key| puts key}
2534
- # # => key:13
2535
- # # => key:18
2536
- #
2537
- # @param [Hash] options
2538
- # - `:match => String`: only return keys matching the pattern
2539
- # - `:count => Integer`: return count keys at most per iteration
2540
- #
2541
- # @return [Enumerator] an enumerator for all found keys
2542
- def scan_each(options={}, &block)
2543
- return to_enum(:scan_each, options) unless block_given?
2544
- cursor = 0
2545
- loop do
2546
- cursor, keys = scan(cursor, options)
2547
- keys.each(&block)
2548
- break if cursor == "0"
2549
- end
2550
- end
2551
-
2552
- # Scan a hash
2553
- #
2554
- # @example Retrieve the first batch of key/value pairs in a hash
2555
- # redis.hscan("hash", 0)
2556
- #
2557
- # @param [String, Integer] cursor the cursor of the iteration
2558
- # @param [Hash] options
2559
- # - `:match => String`: only return keys matching the pattern
2560
- # - `:count => Integer`: return count keys at most per iteration
2561
- #
2562
- # @return [String, Array<[String, String]>] the next cursor and all found keys
2563
- def hscan(key, cursor, options={})
2564
- _scan(:hscan, cursor, [key], options) do |reply|
2565
- [reply[0], reply[1].each_slice(2).to_a]
2566
- end
2567
- end
2568
-
2569
- # Scan a hash
2570
- #
2571
- # @example Retrieve all of the key/value pairs in a hash
2572
- # redis.hscan_each("hash").to_a
2573
- # # => [["key70", "70"], ["key80", "80"]]
2574
- #
2575
- # @param [Hash] options
2576
- # - `:match => String`: only return keys matching the pattern
2577
- # - `:count => Integer`: return count keys at most per iteration
2578
- #
2579
- # @return [Enumerator] an enumerator for all found keys
2580
- def hscan_each(key, options={}, &block)
2581
- return to_enum(:hscan_each, key, options) unless block_given?
2582
- cursor = 0
2583
- loop do
2584
- cursor, values = hscan(key, cursor, options)
2585
- values.each(&block)
2586
- break if cursor == "0"
2587
- end
2588
- end
2589
-
2590
- # Scan a sorted set
2591
- #
2592
- # @example Retrieve the first batch of key/value pairs in a hash
2593
- # redis.zscan("zset", 0)
2594
- #
2595
- # @param [String, Integer] cursor the cursor of the iteration
2596
- # @param [Hash] options
2597
- # - `:match => String`: only return keys matching the pattern
2598
- # - `:count => Integer`: return count keys at most per iteration
2599
- #
2600
- # @return [String, Array<[String, Float]>] the next cursor and all found
2601
- # members and scores
2602
- def zscan(key, cursor, options={})
2603
- _scan(:zscan, cursor, [key], options) do |reply|
2604
- [reply[0], FloatifyPairs.call(reply[1])]
2605
- end
2606
- end
2607
-
2608
- # Scan a sorted set
2609
- #
2610
- # @example Retrieve all of the members/scores in a sorted set
2611
- # redis.zscan_each("zset").to_a
2612
- # # => [["key70", "70"], ["key80", "80"]]
2613
- #
2614
- # @param [Hash] options
2615
- # - `:match => String`: only return keys matching the pattern
2616
- # - `:count => Integer`: return count keys at most per iteration
2617
- #
2618
- # @return [Enumerator] an enumerator for all found scores and members
2619
- def zscan_each(key, options={}, &block)
2620
- return to_enum(:zscan_each, key, options) unless block_given?
2621
- cursor = 0
2622
- loop do
2623
- cursor, values = zscan(key, cursor, options)
2624
- values.each(&block)
2625
- break if cursor == "0"
2626
- end
2627
- end
2628
-
2629
- # Scan a set
2630
- #
2631
- # @example Retrieve the first batch of keys in a set
2632
- # redis.sscan("set", 0)
2633
- #
2634
- # @param [String, Integer] cursor the cursor of the iteration
2635
- # @param [Hash] options
2636
- # - `:match => String`: only return keys matching the pattern
2637
- # - `:count => Integer`: return count keys at most per iteration
2638
- #
2639
- # @return [String, Array<String>] the next cursor and all found members
2640
- def sscan(key, cursor, options={})
2641
- _scan(:sscan, cursor, [key], options)
2642
- end
2643
-
2644
- # Scan a set
2645
- #
2646
- # @example Retrieve all of the keys in a set
2647
- # redis.sscan_each("set").to_a
2648
- # # => ["key1", "key2", "key3"]
2649
- #
2650
- # @param [Hash] options
2651
- # - `:match => String`: only return keys matching the pattern
2652
- # - `:count => Integer`: return count keys at most per iteration
2653
- #
2654
- # @return [Enumerator] an enumerator for all keys in the set
2655
- def sscan_each(key, options={}, &block)
2656
- return to_enum(:sscan_each, key, options) unless block_given?
2657
- cursor = 0
2658
- loop do
2659
- cursor, keys = sscan(key, cursor, options)
2660
- keys.each(&block)
2661
- break if cursor == "0"
2662
- end
2663
- end
2664
-
2665
- # Add one or more members to a HyperLogLog structure.
2666
- #
2667
- # @param [String] key
2668
- # @param [String, Array<String>] member one member, or array of members
2669
- # @return [Boolean] true if at least 1 HyperLogLog internal register was altered. false otherwise.
2670
- def pfadd(key, member)
2671
- synchronize do |client|
2672
- client.call([:pfadd, key, member], &Boolify)
2673
- end
2674
- end
2675
-
2676
- # Get the approximate cardinality of members added to HyperLogLog structure.
2677
- #
2678
- # If called with multiple keys, returns the approximate cardinality of the
2679
- # union of the HyperLogLogs contained in the keys.
2680
- #
2681
- # @param [String, Array<String>] keys
2682
- # @return [Fixnum]
2683
- def pfcount(*keys)
2684
- synchronize do |client|
2685
- client.call([:pfcount] + keys)
2686
- end
2687
- end
2688
-
2689
- # Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
2690
- # the observed Sets of the source HyperLogLog structures.
2691
- #
2692
- # @param [String] dest_key destination key
2693
- # @param [String, Array<String>] source_key source key, or array of keys
2694
- # @return [Boolean]
2695
- def pfmerge(dest_key, *source_key)
2696
- synchronize do |client|
2697
- client.call([:pfmerge, dest_key, *source_key], &BoolifySet)
2698
- end
2699
- end
2700
-
2701
- # Interact with the sentinel command (masters, master, slaves, failover)
2702
- #
2703
- # @param [String] subcommand e.g. `masters`, `master`, `slaves`
2704
- # @param [Array<String>] args depends on subcommand
2705
- # @return [Array<String>, Hash<String, String>, String] depends on subcommand
2706
- def sentinel(subcommand, *args)
2707
- subcommand = subcommand.to_s.downcase
2708
- synchronize do |client|
2709
- client.call([:sentinel, subcommand] + args) do |reply|
2710
- case subcommand
2711
- when "get-master-addr-by-name"
2712
- reply
2713
- else
2714
- if reply.kind_of?(Array)
2715
- if reply[0].kind_of?(Array)
2716
- reply.map(&Hashify)
2717
- else
2718
- Hashify.call(reply)
2719
- end
2720
- else
2721
- reply
2722
- end
2723
- end
2724
- end
2725
- end
2726
- end
2727
-
2728
- def id
2729
- @original_client.id
2730
- end
2731
-
2732
- def inspect
2733
- "#<Redis client v#{Redis::VERSION} for #{id}>"
2734
- end
2735
-
2736
- def dup
2737
- self.class.new(@options)
2738
- end
2739
-
2740
- def method_missing(command, *args)
2741
- synchronize do |client|
2742
- client.call([command] + args)
2743
- end
2744
- end
2745
-
2746
- private
2747
-
2748
- # Commands returning 1 for true and 0 for false may be executed in a pipeline
2749
- # where the method call will return nil. Propagate the nil instead of falsely
2750
- # returning false.
2751
- Boolify =
2752
- lambda { |value|
2753
- value == 1 if value
2754
- }
2755
-
2756
- BoolifySet =
2757
- lambda { |value|
2758
- if value && "OK" == value
2759
- true
2760
- else
2761
- false
2762
- end
2763
- }
2764
-
2765
- Hashify =
2766
- lambda { |array|
2767
- hash = Hash.new
2768
- array.each_slice(2) do |field, value|
2769
- hash[field] = value
2770
- end
2771
- hash
2772
- }
2773
-
2774
- Floatify =
2775
- lambda { |str|
2776
- if str
2777
- if (inf = str.match(/^(-)?inf/i))
2778
- (inf[1] ? -1.0 : 1.0) / 0.0
2779
- else
2780
- Float(str)
2781
- end
2782
- end
2783
- }
2784
-
2785
- FloatifyPairs =
2786
- lambda { |array|
2787
- if array
2788
- array.each_slice(2).map do |member, score|
2789
- [member, Floatify.call(score)]
2790
- end
2791
- end
2792
- }
2793
-
2794
- def _subscription(method, timeout, channels, block)
2795
- return @client.call([method] + channels) if subscribed?
2796
-
2797
- begin
2798
- original, @client = @client, SubscribedClient.new(@client)
2799
- if timeout > 0
2800
- @client.send(method, timeout, *channels, &block)
2801
- else
2802
- @client.send(method, *channels, &block)
2803
- end
2804
- ensure
2805
- @client = original
2806
- end
2807
- end
2808
-
2809
191
  end
2810
192
 
2811
- require_relative "redis/version"
2812
- require_relative "redis/connection"
2813
- require_relative "redis/client"
2814
- require_relative "redis/pipeline"
2815
- require_relative "redis/subscribe"
193
+ require "redis/version"
194
+ require "redis/client"
195
+ require "redis/pipeline"
196
+ require "redis/subscribe"