redis 3.3.5 → 4.7.1

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