redis 4.0.1 → 4.8.1

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