redis 4.1.2 → 4.2.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -1
- data/README.md +14 -5
- data/lib/redis.rb +385 -343
- data/lib/redis/client.rb +66 -69
- data/lib/redis/cluster.rb +13 -4
- data/lib/redis/cluster/node.rb +3 -0
- data/lib/redis/cluster/node_key.rb +3 -7
- data/lib/redis/cluster/option.rb +27 -14
- data/lib/redis/cluster/slot.rb +30 -13
- data/lib/redis/cluster/slot_loader.rb +4 -4
- data/lib/redis/connection.rb +2 -0
- data/lib/redis/connection/command_helper.rb +3 -2
- data/lib/redis/connection/hiredis.rb +4 -3
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +47 -58
- data/lib/redis/connection/synchrony.rb +9 -4
- data/lib/redis/distributed.rb +117 -62
- data/lib/redis/errors.rb +2 -0
- data/lib/redis/hash_ring.rb +15 -14
- data/lib/redis/pipeline.rb +16 -3
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- metadata +14 -9
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 28eb23d20152436fc47f334f517c6fa62855e8e4711b44979cb05d86f8dcdd34
         | 
| 4 | 
            +
              data.tar.gz: 46d01b3f5539800142582aeb7ebeabe4327bce970e31ce2048dcba027dfe6eb1
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: f2f11269c6a3a030231eeb93dfa6bee54b340ff5ad244df6d79074c95f111e3d2081f49f77756576cb8225640e8b8cad144884c8519bcf1b0e7bedea3ca1b00f
         | 
| 7 | 
            +
              data.tar.gz: 44ec06531632060b497cf1d02e6678c3d087ec3371cba86f53f9687848f73bb4e02c166b11d4db9e6b531b62ba2ca830649d71114063560f6dd764ca2ef10f07
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,8 +1,46 @@ | |
| 1 1 | 
             
            # Unreleased
         | 
| 2 2 |  | 
| 3 | 
            -
            # 4. | 
| 3 | 
            +
            # 4.2.2
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            * Fix `WATCH` support for `Redis::Distributed`. See #941.
         | 
| 6 | 
            +
            * Fix handling of empty stream responses. See #905, #929.
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            # 4.2.1
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            * Fix `exists?` returning an actual boolean when called with multiple keys. See #918.
         | 
| 11 | 
            +
            * Setting `Redis.exists_returns_integer = false` disables warning message about new behaviour. See #920.
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            # 4.2.0
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            * Convert commands to accept keyword arguments rather than option hashes. This both help catching typos, and reduce needless allocations.
         | 
| 16 | 
            +
            * Deprecate the synchrony driver. It will be removed in 5.0 and hopefully maintained as a separate gem. See #915.
         | 
| 17 | 
            +
            * Make `Redis#exists` variadic, will return an Integer if called with multiple keys.
         | 
| 18 | 
            +
            * Add `Redis#exists?` to get a Boolean if any of the keys exists.
         | 
| 19 | 
            +
            * `Redis#exists` when called with a single key will warn that future versions will return an Integer.
         | 
| 20 | 
            +
              Set `Redis.exists_returns_integer = true` to opt-in to the new behavior.
         | 
| 21 | 
            +
            * Support `keepttl` ooption in `set`. See #913.
         | 
| 22 | 
            +
            * Optimized initialization of Redis::Cluster. See #912.
         | 
| 23 | 
            +
            * Accept sentinel options even with string key. See #599.
         | 
| 24 | 
            +
            * Verify TLS connections by default. See #900.
         | 
| 25 | 
            +
            * Make `Redis#hset` variadic. It now returns an integer, not a boolean. See #910.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            # 4.1.4
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            * Alias `Redis#disconnect` as `#close`. See #901.
         | 
| 30 | 
            +
            * Handle clusters with multiple slot ranges. See #894.
         | 
| 31 | 
            +
            * Fix password authentication to a redis cluster. See #889.
         | 
| 32 | 
            +
            * Handle recursive MOVED responses. See #882.
         | 
| 33 | 
            +
            * Increase buffer size in the ruby connector. See #880.
         | 
| 34 | 
            +
            * Fix thread safety of `Redis.queue`. See #878.
         | 
| 35 | 
            +
            * Deprecate `Redis::Future#==` as it's likely to be a mistake. See #876.
         | 
| 36 | 
            +
            * Support `KEEPTTL` option for SET command. See #913.
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            # 4.1.3
         | 
| 4 39 |  | 
| 5 40 | 
             
            * Fix the client hanging forever when connecting with SSL to a non-SSL server. See #835.
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            # 4.1.2
         | 
| 43 | 
            +
             | 
| 6 44 | 
             
            * Fix several authentication problems with sentinel. See #850 and #856.
         | 
| 7 45 | 
             
            * Explicitly drop Ruby 2.2 support.
         | 
| 8 46 |  | 
    
        data/README.md
    CHANGED
    
    | @@ -1,8 +1,9 @@ | |
| 1 | 
            -
            # redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link]
         | 
| 1 | 
            +
            # redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link] 
         | 
| 2 2 |  | 
| 3 3 | 
             
            A Ruby client that tries to match [Redis][redis-home]' API one-to-one, while still
         | 
| 4 4 | 
             
            providing an idiomatic interface.
         | 
| 5 5 |  | 
| 6 | 
            +
            See [RubyDoc.info][rubydoc] for the API docs of the latest published gem.
         | 
| 6 7 |  | 
| 7 8 | 
             
            ## Getting started
         | 
| 8 9 |  | 
| @@ -34,6 +35,9 @@ You can also specify connection options as a [`redis://` URL][redis-url]: | |
| 34 35 | 
             
            redis = Redis.new(url: "redis://:p4ssw0rd@10.0.1.1:6380/15")
         | 
| 35 36 | 
             
            ```
         | 
| 36 37 |  | 
| 38 | 
            +
            The client expects passwords with special chracters to be URL-encoded (i.e.
         | 
| 39 | 
            +
            `CGI.escape(password)`).
         | 
| 40 | 
            +
             | 
| 37 41 | 
             
            By default, the client will try to read the `REDIS_URL` environment variable
         | 
| 38 42 | 
             
            and use that as URL to connect to. The above statement is therefore equivalent
         | 
| 39 43 | 
             
            to setting this environment variable and calling `Redis.new` without arguments.
         | 
| @@ -142,12 +146,13 @@ redis.mget('{key}1', '{key}2') | |
| 142 146 | 
             
            ```
         | 
| 143 147 |  | 
| 144 148 | 
             
            * The client automatically reconnects after a failover occurred, but the caller is responsible for handling errors while it is happening.
         | 
| 149 | 
            +
            * The client support permanent node failures, and will reroute requests to promoted slaves.
         | 
| 145 150 | 
             
            * The client supports `MOVED` and `ASK` redirections transparently.
         | 
| 146 151 |  | 
| 147 152 | 
             
            ## Storing objects
         | 
| 148 153 |  | 
| 149 | 
            -
            Redis  | 
| 150 | 
            -
             | 
| 154 | 
            +
            Redis "string" types can be used to store serialized Ruby objects, for
         | 
| 155 | 
            +
            example with JSON:
         | 
| 151 156 |  | 
| 152 157 | 
             
            ```ruby
         | 
| 153 158 | 
             
            require "json"
         | 
| @@ -321,7 +326,7 @@ This library supports natively terminating client side SSL/TLS connections | |
| 321 326 | 
             
            when talking to Redis via a server-side proxy such as [stunnel], [hitch],
         | 
| 322 327 | 
             
            or [ghostunnel].
         | 
| 323 328 |  | 
| 324 | 
            -
            To enable SSL support, pass the `:ssl =>  | 
| 329 | 
            +
            To enable SSL support, pass the `:ssl => true` option when configuring the
         | 
| 325 330 | 
             
            Redis client, or pass in `:url => "rediss://..."` (like HTTPS for Redis).
         | 
| 326 331 | 
             
            You will also need to pass in an `:ssl_params => { ... }` hash used to
         | 
| 327 332 | 
             
            configure the `OpenSSL::SSL::SSLContext` object used for the connection:
         | 
| @@ -436,6 +441,10 @@ redis = Redis.new(:driver => :synchrony) | |
| 436 441 | 
             
            This library is tested against recent Ruby and Redis versions.
         | 
| 437 442 | 
             
            Check [Travis][travis-link] for the exact versions supported.
         | 
| 438 443 |  | 
| 444 | 
            +
            ## See Also
         | 
| 445 | 
            +
             | 
| 446 | 
            +
            - [async-redis](https://github.com/socketry/async-redis) — An [async](https://github.com/socketry/async) compatible Redis client.
         | 
| 447 | 
            +
             | 
| 439 448 | 
             
            ## Contributors
         | 
| 440 449 |  | 
| 441 450 | 
             
            Several people contributed to redis-rb, but we would like to especially
         | 
| @@ -446,7 +455,7 @@ client and evangelized Redis in Rubyland. Thank you, Ezra. | |
| 446 455 | 
             
            ## Contributing
         | 
| 447 456 |  | 
| 448 457 | 
             
            [Fork the project](https://github.com/redis/redis-rb) and send pull
         | 
| 449 | 
            -
            requests. | 
| 458 | 
            +
            requests.
         | 
| 450 459 |  | 
| 451 460 |  | 
| 452 461 | 
             
            [inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
         | 
    
        data/lib/redis.rb
    CHANGED
    
    | @@ -4,13 +4,26 @@ require "monitor" | |
| 4 4 | 
             
            require_relative "redis/errors"
         | 
| 5 5 |  | 
| 6 6 | 
             
            class Redis
         | 
| 7 | 
            +
              class << self
         | 
| 8 | 
            +
                attr_reader :exists_returns_integer
         | 
| 7 9 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            +
                def exists_returns_integer=(value)
         | 
| 11 | 
            +
                  unless value
         | 
| 12 | 
            +
                    message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
         | 
| 13 | 
            +
                      "disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
         | 
| 14 | 
            +
                      "`exists?` instead."
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    ::Kernel.warn(message)
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  @exists_returns_integer = value
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                attr_writer :current
         | 
| 10 23 | 
             
              end
         | 
| 11 24 |  | 
| 12 | 
            -
              def self.current | 
| 13 | 
            -
                @current  | 
| 25 | 
            +
              def self.current
         | 
| 26 | 
            +
                @current ||= Redis.new
         | 
| 14 27 | 
             
              end
         | 
| 15 28 |  | 
| 16 29 | 
             
              include MonitorMixin
         | 
| @@ -18,18 +31,22 @@ class Redis | |
| 18 31 | 
             
              # Create a new client instance
         | 
| 19 32 | 
             
              #
         | 
| 20 33 | 
             
              # @param [Hash] options
         | 
| 21 | 
            -
              # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection: | 
| 34 | 
            +
              # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
         | 
| 35 | 
            +
              #   `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
         | 
| 36 | 
            +
              #    connection: `unix://[path to Redis socket]`. This overrides all other options.
         | 
| 22 37 | 
             
              # @option options [String] :host ("127.0.0.1") server hostname
         | 
| 23 | 
            -
              # @option options [ | 
| 38 | 
            +
              # @option options [Integer] :port (6379) server port
         | 
| 24 39 | 
             
              # @option options [String] :path path to server socket (overrides host and port)
         | 
| 25 40 | 
             
              # @option options [Float] :timeout (5.0) timeout in seconds
         | 
| 26 41 | 
             
              # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
         | 
| 27 42 | 
             
              # @option options [String] :password Password to authenticate against server
         | 
| 28 | 
            -
              # @option options [ | 
| 43 | 
            +
              # @option options [Integer] :db (0) Database to select after initial connect
         | 
| 29 44 | 
             
              # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
         | 
| 30 | 
            -
              # @option options [String] :id ID for the client connection, assigns name to current connection by sending | 
| 31 | 
            -
              # | 
| 32 | 
            -
              # @option options [ | 
| 45 | 
            +
              # @option options [String] :id ID for the client connection, assigns name to current connection by sending
         | 
| 46 | 
            +
              #   `CLIENT SETNAME`
         | 
| 47 | 
            +
              # @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
         | 
| 48 | 
            +
              #   based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
         | 
| 49 | 
            +
              # @option options [Integer] :reconnect_attempts Number of attempts trying to connect
         | 
| 33 50 | 
             
              # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
         | 
| 34 51 | 
             
              # @option options [Array] :sentinels List of sentinels to contact
         | 
| 35 52 | 
             
              # @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
         | 
| @@ -53,7 +70,7 @@ class Redis | |
| 53 70 | 
             
              end
         | 
| 54 71 |  | 
| 55 72 | 
             
              # Run code with the client reconnecting
         | 
| 56 | 
            -
              def with_reconnect(val=true, &blk)
         | 
| 73 | 
            +
              def with_reconnect(val = true, &blk)
         | 
| 57 74 | 
             
                synchronize do |client|
         | 
| 58 75 | 
             
                  client.with_reconnect(val, &blk)
         | 
| 59 76 | 
             
                end
         | 
| @@ -96,7 +113,9 @@ class Redis | |
| 96 113 | 
             
              # See http://redis.io/topics/pipelining for more details.
         | 
| 97 114 | 
             
              #
         | 
| 98 115 | 
             
              def queue(*command)
         | 
| 99 | 
            -
                 | 
| 116 | 
            +
                synchronize do
         | 
| 117 | 
            +
                  @queue[Thread.current.object_id] << command
         | 
| 118 | 
            +
                end
         | 
| 100 119 | 
             
              end
         | 
| 101 120 |  | 
| 102 121 | 
             
              # Sends all commands in the queue.
         | 
| @@ -135,7 +154,7 @@ class Redis | |
| 135 154 |  | 
| 136 155 | 
             
              # Change the selected database for the current connection.
         | 
| 137 156 | 
             
              #
         | 
| 138 | 
            -
              # @param [ | 
| 157 | 
            +
              # @param [Integer] db zero-based index of the DB to use (0 to 15)
         | 
| 139 158 | 
             
              # @return [String] `OK`
         | 
| 140 159 | 
             
              def select(db)
         | 
| 141 160 | 
             
                synchronize do |client|
         | 
| @@ -204,7 +223,7 @@ class Redis | |
| 204 223 | 
             
              def config(action, *args)
         | 
| 205 224 | 
             
                synchronize do |client|
         | 
| 206 225 | 
             
                  client.call([:config, action] + args) do |reply|
         | 
| 207 | 
            -
                    if reply. | 
| 226 | 
            +
                    if reply.is_a?(Array) && action == :get
         | 
| 208 227 | 
             
                      Hashify.call(reply)
         | 
| 209 228 | 
             
                    else
         | 
| 210 229 | 
             
                      reply
         | 
| @@ -234,7 +253,7 @@ class Redis | |
| 234 253 |  | 
| 235 254 | 
             
              # Return the number of keys in the selected database.
         | 
| 236 255 | 
             
              #
         | 
| 237 | 
            -
              # @return [ | 
| 256 | 
            +
              # @return [Integer]
         | 
| 238 257 | 
             
              def dbsize
         | 
| 239 258 | 
             
                synchronize do |client|
         | 
| 240 259 | 
             
                  client.call([:dbsize])
         | 
| @@ -255,7 +274,7 @@ class Redis | |
| 255 274 | 
             
              def flushall(options = nil)
         | 
| 256 275 | 
             
                synchronize do |client|
         | 
| 257 276 | 
             
                  if options && options[:async]
         | 
| 258 | 
            -
                    client.call([ | 
| 277 | 
            +
                    client.call(%i[flushall async])
         | 
| 259 278 | 
             
                  else
         | 
| 260 279 | 
             
                    client.call([:flushall])
         | 
| 261 280 | 
             
                  end
         | 
| @@ -270,7 +289,7 @@ class Redis | |
| 270 289 | 
             
              def flushdb(options = nil)
         | 
| 271 290 | 
             
                synchronize do |client|
         | 
| 272 291 | 
             
                  if options && options[:async]
         | 
| 273 | 
            -
                    client.call([ | 
| 292 | 
            +
                    client.call(%i[flushdb async])
         | 
| 274 293 | 
             
                  else
         | 
| 275 294 | 
             
                    client.call([:flushdb])
         | 
| 276 295 | 
             
                  end
         | 
| @@ -284,7 +303,7 @@ class Redis | |
| 284 303 | 
             
              def info(cmd = nil)
         | 
| 285 304 | 
             
                synchronize do |client|
         | 
| 286 305 | 
             
                  client.call([:info, cmd].compact) do |reply|
         | 
| 287 | 
            -
                    if reply. | 
| 306 | 
            +
                    if reply.is_a?(String)
         | 
| 288 307 | 
             
                      reply = HashifyInfo.call(reply)
         | 
| 289 308 |  | 
| 290 309 | 
             
                      if cmd && cmd.to_s == "commandstats"
         | 
| @@ -303,7 +322,7 @@ class Redis | |
| 303 322 |  | 
| 304 323 | 
             
              # Get the UNIX time stamp of the last successful save to disk.
         | 
| 305 324 | 
             
              #
         | 
| 306 | 
            -
              # @return [ | 
| 325 | 
            +
              # @return [Integer]
         | 
| 307 326 | 
             
              def lastsave
         | 
| 308 327 | 
             
                synchronize do |client|
         | 
| 309 328 | 
             
                  client.call([:lastsave])
         | 
| @@ -355,9 +374,9 @@ class Redis | |
| 355 374 | 
             
              # Interact with the slowlog (get, len, reset)
         | 
| 356 375 | 
             
              #
         | 
| 357 376 | 
             
              # @param [String] subcommand e.g. `get`, `len`, `reset`
         | 
| 358 | 
            -
              # @param [ | 
| 359 | 
            -
              # @return [Array<String>,  | 
| 360 | 
            -
              def slowlog(subcommand, length=nil)
         | 
| 377 | 
            +
              # @param [Integer] length maximum number of entries to return
         | 
| 378 | 
            +
              # @return [Array<String>, Integer, String] depends on subcommand
         | 
| 379 | 
            +
              def slowlog(subcommand, length = nil)
         | 
| 361 380 | 
             
                synchronize do |client|
         | 
| 362 381 | 
             
                  args = [:slowlog, subcommand]
         | 
| 363 382 | 
             
                  args << length if length
         | 
| @@ -377,12 +396,12 @@ class Redis | |
| 377 396 | 
             
              # @example
         | 
| 378 397 | 
             
              #   r.time # => [ 1333093196, 606806 ]
         | 
| 379 398 | 
             
              #
         | 
| 380 | 
            -
              # @return [Array< | 
| 399 | 
            +
              # @return [Array<Integer>] tuple of seconds since UNIX epoch and
         | 
| 381 400 | 
             
              #   microseconds in the current second
         | 
| 382 401 | 
             
              def time
         | 
| 383 402 | 
             
                synchronize do |client|
         | 
| 384 403 | 
             
                  client.call([:time]) do |reply|
         | 
| 385 | 
            -
                    reply | 
| 404 | 
            +
                    reply&.map(&:to_i)
         | 
| 386 405 | 
             
                  end
         | 
| 387 406 | 
             
                end
         | 
| 388 407 | 
             
              end
         | 
| @@ -400,7 +419,7 @@ class Redis | |
| 400 419 | 
             
              # Set a key's time to live in seconds.
         | 
| 401 420 | 
             
              #
         | 
| 402 421 | 
             
              # @param [String] key
         | 
| 403 | 
            -
              # @param [ | 
| 422 | 
            +
              # @param [Integer] seconds time to live
         | 
| 404 423 | 
             
              # @return [Boolean] whether the timeout was set or not
         | 
| 405 424 | 
             
              def expire(key, seconds)
         | 
| 406 425 | 
             
                synchronize do |client|
         | 
| @@ -411,7 +430,7 @@ class Redis | |
| 411 430 | 
             
              # Set the expiration for a key as a UNIX timestamp.
         | 
| 412 431 | 
             
              #
         | 
| 413 432 | 
             
              # @param [String] key
         | 
| 414 | 
            -
              # @param [ | 
| 433 | 
            +
              # @param [Integer] unix_time expiry time specified as a UNIX timestamp
         | 
| 415 434 | 
             
              # @return [Boolean] whether the timeout was set or not
         | 
| 416 435 | 
             
              def expireat(key, unix_time)
         | 
| 417 436 | 
             
                synchronize do |client|
         | 
| @@ -422,7 +441,7 @@ class Redis | |
| 422 441 | 
             
              # Get the time to live (in seconds) for a key.
         | 
| 423 442 | 
             
              #
         | 
| 424 443 | 
             
              # @param [String] key
         | 
| 425 | 
            -
              # @return [ | 
| 444 | 
            +
              # @return [Integer] remaining time to live in seconds.
         | 
| 426 445 | 
             
              #
         | 
| 427 446 | 
             
              # In Redis 2.6 or older the command returns -1 if the key does not exist or if
         | 
| 428 447 | 
             
              # the key exist but has no associated expire.
         | 
| @@ -440,7 +459,7 @@ class Redis | |
| 440 459 | 
             
              # Set a key's time to live in milliseconds.
         | 
| 441 460 | 
             
              #
         | 
| 442 461 | 
             
              # @param [String] key
         | 
| 443 | 
            -
              # @param [ | 
| 462 | 
            +
              # @param [Integer] milliseconds time to live
         | 
| 444 463 | 
             
              # @return [Boolean] whether the timeout was set or not
         | 
| 445 464 | 
             
              def pexpire(key, milliseconds)
         | 
| 446 465 | 
             
                synchronize do |client|
         | 
| @@ -451,7 +470,7 @@ class Redis | |
| 451 470 | 
             
              # Set the expiration for a key as number of milliseconds from UNIX Epoch.
         | 
| 452 471 | 
             
              #
         | 
| 453 472 | 
             
              # @param [String] key
         | 
| 454 | 
            -
              # @param [ | 
| 473 | 
            +
              # @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
         | 
| 455 474 | 
             
              # @return [Boolean] whether the timeout was set or not
         | 
| 456 475 | 
             
              def pexpireat(key, ms_unix_time)
         | 
| 457 476 | 
             
                synchronize do |client|
         | 
| @@ -462,7 +481,7 @@ class Redis | |
| 462 481 | 
             
              # Get the time to live (in milliseconds) for a key.
         | 
| 463 482 | 
             
              #
         | 
| 464 483 | 
             
              # @param [String] key
         | 
| 465 | 
            -
              # @return [ | 
| 484 | 
            +
              # @return [Integer] remaining time to live in milliseconds
         | 
| 466 485 | 
             
              # In Redis 2.6 or older the command returns -1 if the key does not exist or if
         | 
| 467 486 | 
             
              # the key exist but has no associated expire.
         | 
| 468 487 | 
             
              #
         | 
| @@ -495,9 +514,9 @@ class Redis | |
| 495 514 | 
             
              #   - `:replace => Boolean`: if false, raises an error if key already exists
         | 
| 496 515 | 
             
              # @raise [Redis::CommandError]
         | 
| 497 516 | 
             
              # @return [String] `"OK"`
         | 
| 498 | 
            -
              def restore(key, ttl, serialized_value,  | 
| 517 | 
            +
              def restore(key, ttl, serialized_value, replace: nil)
         | 
| 499 518 | 
             
                args = [:restore, key, ttl, serialized_value]
         | 
| 500 | 
            -
                args << 'REPLACE' if  | 
| 519 | 
            +
                args << 'REPLACE' if replace
         | 
| 501 520 |  | 
| 502 521 | 
             
                synchronize do |client|
         | 
| 503 522 | 
             
                  client.call(args)
         | 
| @@ -532,7 +551,7 @@ class Redis | |
| 532 551 | 
             
              # Delete one or more keys.
         | 
| 533 552 | 
             
              #
         | 
| 534 553 | 
             
              # @param [String, Array<String>] keys
         | 
| 535 | 
            -
              # @return [ | 
| 554 | 
            +
              # @return [Integer] number of keys that were deleted
         | 
| 536 555 | 
             
              def del(*keys)
         | 
| 537 556 | 
             
                synchronize do |client|
         | 
| 538 557 | 
             
                  client.call([:del] + keys)
         | 
| @@ -542,20 +561,50 @@ class Redis | |
| 542 561 | 
             
              # Unlink one or more keys.
         | 
| 543 562 | 
             
              #
         | 
| 544 563 | 
             
              # @param [String, Array<String>] keys
         | 
| 545 | 
            -
              # @return [ | 
| 564 | 
            +
              # @return [Integer] number of keys that were unlinked
         | 
| 546 565 | 
             
              def unlink(*keys)
         | 
| 547 566 | 
             
                synchronize do |client|
         | 
| 548 567 | 
             
                  client.call([:unlink] + keys)
         | 
| 549 568 | 
             
                end
         | 
| 550 569 | 
             
              end
         | 
| 551 570 |  | 
| 552 | 
            -
              # Determine  | 
| 571 | 
            +
              # Determine how many of the keys exists.
         | 
| 553 572 | 
             
              #
         | 
| 554 | 
            -
              # @param [String]  | 
| 573 | 
            +
              # @param [String, Array<String>] keys
         | 
| 574 | 
            +
              # @return [Integer]
         | 
| 575 | 
            +
              def exists(*keys)
         | 
| 576 | 
            +
                if !Redis.exists_returns_integer && keys.size == 1
         | 
| 577 | 
            +
                  if Redis.exists_returns_integer.nil?
         | 
| 578 | 
            +
                    message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
         | 
| 579 | 
            +
                      "should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer =  " \
         | 
| 580 | 
            +
                      "true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
         | 
| 581 | 
            +
                      "`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
         | 
| 582 | 
            +
                      "(#{::Kernel.caller(1, 1).first})\n"
         | 
| 583 | 
            +
             | 
| 584 | 
            +
                    ::Kernel.warn(message)
         | 
| 585 | 
            +
                  end
         | 
| 586 | 
            +
             | 
| 587 | 
            +
                  exists?(*keys)
         | 
| 588 | 
            +
                else
         | 
| 589 | 
            +
                  _exists(*keys)
         | 
| 590 | 
            +
                end
         | 
| 591 | 
            +
              end
         | 
| 592 | 
            +
             | 
| 593 | 
            +
              def _exists(*keys)
         | 
| 594 | 
            +
                synchronize do |client|
         | 
| 595 | 
            +
                  client.call([:exists, *keys])
         | 
| 596 | 
            +
                end
         | 
| 597 | 
            +
              end
         | 
| 598 | 
            +
             | 
| 599 | 
            +
              # Determine if any of the keys exists.
         | 
| 600 | 
            +
              #
         | 
| 601 | 
            +
              # @param [String, Array<String>] keys
         | 
| 555 602 | 
             
              # @return [Boolean]
         | 
| 556 | 
            -
              def exists( | 
| 603 | 
            +
              def exists?(*keys)
         | 
| 557 604 | 
             
                synchronize do |client|
         | 
| 558 | 
            -
                  client.call([:exists,  | 
| 605 | 
            +
                  client.call([:exists, *keys]) do |value|
         | 
| 606 | 
            +
                    value > 0
         | 
| 607 | 
            +
                  end
         | 
| 559 608 | 
             
                end
         | 
| 560 609 | 
             
              end
         | 
| 561 610 |  | 
| @@ -566,7 +615,7 @@ class Redis | |
| 566 615 | 
             
              def keys(pattern = "*")
         | 
| 567 616 | 
             
                synchronize do |client|
         | 
| 568 617 | 
             
                  client.call([:keys, pattern]) do |reply|
         | 
| 569 | 
            -
                    if reply. | 
| 618 | 
            +
                    if reply.is_a?(String)
         | 
| 570 619 | 
             
                      reply.split(" ")
         | 
| 571 620 | 
             
                    else
         | 
| 572 621 | 
             
                      reply
         | 
| @@ -592,7 +641,7 @@ class Redis | |
| 592 641 | 
             
              #     # => "bar"
         | 
| 593 642 | 
             
              #
         | 
| 594 643 | 
             
              # @param [String] key
         | 
| 595 | 
            -
              # @param [ | 
| 644 | 
            +
              # @param [Integer] db
         | 
| 596 645 | 
             
              # @return [Boolean] whether the key was moved or not
         | 
| 597 646 | 
             
              def move(key, db)
         | 
| 598 647 | 
             
                synchronize do |client|
         | 
| @@ -656,36 +705,33 @@ class Redis | |
| 656 705 | 
             
              #   - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
         | 
| 657 706 | 
             
              #   - `:store => String`: key to store the result at
         | 
| 658 707 | 
             
              #
         | 
| 659 | 
            -
              # @return [Array<String>, Array<Array<String>>,  | 
| 708 | 
            +
              # @return [Array<String>, Array<Array<String>>, Integer]
         | 
| 660 709 | 
             
              #   - when `:get` is not specified, or holds a single element, an array of elements
         | 
| 661 710 | 
             
              #   - when `:get` is specified, and holds more than one element, an array of
         | 
| 662 711 | 
             
              #   elements where every element is an array with the result for every
         | 
| 663 712 | 
             
              #   element specified in `:get`
         | 
| 664 713 | 
             
              #   - when `:store` is specified, the number of elements in the stored result
         | 
| 665 | 
            -
              def sort(key,  | 
| 666 | 
            -
                args = []
         | 
| 667 | 
            -
             | 
| 668 | 
            -
                by = options[:by]
         | 
| 669 | 
            -
                args.concat(["BY", by]) if by
         | 
| 714 | 
            +
              def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
         | 
| 715 | 
            +
                args = [:sort, key]
         | 
| 716 | 
            +
                args << "BY" << by if by
         | 
| 670 717 |  | 
| 671 | 
            -
                 | 
| 672 | 
            -
             | 
| 718 | 
            +
                if limit
         | 
| 719 | 
            +
                  args << "LIMIT"
         | 
| 720 | 
            +
                  args.concat(limit)
         | 
| 721 | 
            +
                end
         | 
| 673 722 |  | 
| 674 | 
            -
                get = Array( | 
| 675 | 
            -
                 | 
| 723 | 
            +
                get = Array(get)
         | 
| 724 | 
            +
                get.each do |item|
         | 
| 725 | 
            +
                  args << "GET" << item
         | 
| 726 | 
            +
                end
         | 
| 676 727 |  | 
| 677 | 
            -
                order = options[:order]
         | 
| 678 728 | 
             
                args.concat(order.split(" ")) if order
         | 
| 679 | 
            -
             | 
| 680 | 
            -
                store = options[:store]
         | 
| 681 | 
            -
                args.concat(["STORE", store]) if store
         | 
| 729 | 
            +
                args << "STORE" << store if store
         | 
| 682 730 |  | 
| 683 731 | 
             
                synchronize do |client|
         | 
| 684 | 
            -
                  client.call( | 
| 732 | 
            +
                  client.call(args) do |reply|
         | 
| 685 733 | 
             
                    if get.size > 1 && !store
         | 
| 686 | 
            -
                      if reply
         | 
| 687 | 
            -
                        reply.each_slice(get.size).to_a
         | 
| 688 | 
            -
                      end
         | 
| 734 | 
            +
                      reply.each_slice(get.size).to_a if reply
         | 
| 689 735 | 
             
                    else
         | 
| 690 736 | 
             
                      reply
         | 
| 691 737 | 
             
                    end
         | 
| @@ -710,7 +756,7 @@ class Redis | |
| 710 756 | 
             
              #     # => 4
         | 
| 711 757 | 
             
              #
         | 
| 712 758 | 
             
              # @param [String] key
         | 
| 713 | 
            -
              # @return [ | 
| 759 | 
            +
              # @return [Integer] value after decrementing it
         | 
| 714 760 | 
             
              def decr(key)
         | 
| 715 761 | 
             
                synchronize do |client|
         | 
| 716 762 | 
             
                  client.call([:decr, key])
         | 
| @@ -724,8 +770,8 @@ class Redis | |
| 724 770 | 
             
              #     # => 0
         | 
| 725 771 | 
             
              #
         | 
| 726 772 | 
             
              # @param [String] key
         | 
| 727 | 
            -
              # @param [ | 
| 728 | 
            -
              # @return [ | 
| 773 | 
            +
              # @param [Integer] decrement
         | 
| 774 | 
            +
              # @return [Integer] value after decrementing it
         | 
| 729 775 | 
             
              def decrby(key, decrement)
         | 
| 730 776 | 
             
                synchronize do |client|
         | 
| 731 777 | 
             
                  client.call([:decrby, key, decrement])
         | 
| @@ -739,7 +785,7 @@ class Redis | |
| 739 785 | 
             
              #     # => 6
         | 
| 740 786 | 
             
              #
         | 
| 741 787 | 
             
              # @param [String] key
         | 
| 742 | 
            -
              # @return [ | 
| 788 | 
            +
              # @return [Integer] value after incrementing it
         | 
| 743 789 | 
             
              def incr(key)
         | 
| 744 790 | 
             
                synchronize do |client|
         | 
| 745 791 | 
             
                  client.call([:incr, key])
         | 
| @@ -753,8 +799,8 @@ class Redis | |
| 753 799 | 
             
              #     # => 10
         | 
| 754 800 | 
             
              #
         | 
| 755 801 | 
             
              # @param [String] key
         | 
| 756 | 
            -
              # @param [ | 
| 757 | 
            -
              # @return [ | 
| 802 | 
            +
              # @param [Integer] increment
         | 
| 803 | 
            +
              # @return [Integer] value after incrementing it
         | 
| 758 804 | 
             
              def incrby(key, increment)
         | 
| 759 805 | 
             
                synchronize do |client|
         | 
| 760 806 | 
             
                  client.call([:incrby, key, increment])
         | 
| @@ -781,31 +827,25 @@ class Redis | |
| 781 827 | 
             
              # @param [String] key
         | 
| 782 828 | 
             
              # @param [String] value
         | 
| 783 829 | 
             
              # @param [Hash] options
         | 
| 784 | 
            -
              #   - `:ex =>  | 
| 785 | 
            -
              #   - `:px =>  | 
| 830 | 
            +
              #   - `:ex => Integer`: Set the specified expire time, in seconds.
         | 
| 831 | 
            +
              #   - `:px => Integer`: Set the specified expire time, in milliseconds.
         | 
| 786 832 | 
             
              #   - `:nx => true`: Only set the key if it does not already exist.
         | 
| 787 833 | 
             
              #   - `:xx => true`: Only set the key if it already exist.
         | 
| 834 | 
            +
              #   - `:keepttl => true`: Retain the time to live associated with the key.
         | 
| 788 835 | 
             
              # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
         | 
| 789 | 
            -
              def set(key, value,  | 
| 790 | 
            -
                args = []
         | 
| 791 | 
            -
             | 
| 792 | 
            -
                 | 
| 793 | 
            -
                args | 
| 794 | 
            -
             | 
| 795 | 
            -
                 | 
| 796 | 
            -
                args.concat(["PX", px]) if px
         | 
| 797 | 
            -
             | 
| 798 | 
            -
                nx = options[:nx]
         | 
| 799 | 
            -
                args.concat(["NX"]) if nx
         | 
| 800 | 
            -
             | 
| 801 | 
            -
                xx = options[:xx]
         | 
| 802 | 
            -
                args.concat(["XX"]) if xx
         | 
| 836 | 
            +
              def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
         | 
| 837 | 
            +
                args = [:set, key, value.to_s]
         | 
| 838 | 
            +
                args << "EX" << ex if ex
         | 
| 839 | 
            +
                args << "PX" << px if px
         | 
| 840 | 
            +
                args << "NX" if nx
         | 
| 841 | 
            +
                args << "XX" if xx
         | 
| 842 | 
            +
                args << "KEEPTTL" if keepttl
         | 
| 803 843 |  | 
| 804 844 | 
             
                synchronize do |client|
         | 
| 805 845 | 
             
                  if nx || xx
         | 
| 806 | 
            -
                    client.call( | 
| 846 | 
            +
                    client.call(args, &BoolifySet)
         | 
| 807 847 | 
             
                  else
         | 
| 808 | 
            -
                    client.call( | 
| 848 | 
            +
                    client.call(args)
         | 
| 809 849 | 
             
                  end
         | 
| 810 850 | 
             
                end
         | 
| 811 851 | 
             
              end
         | 
| @@ -813,7 +853,7 @@ class Redis | |
| 813 853 | 
             
              # Set the time to live in seconds of a key.
         | 
| 814 854 | 
             
              #
         | 
| 815 855 | 
             
              # @param [String] key
         | 
| 816 | 
            -
              # @param [ | 
| 856 | 
            +
              # @param [Integer] ttl
         | 
| 817 857 | 
             
              # @param [String] value
         | 
| 818 858 | 
             
              # @return [String] `"OK"`
         | 
| 819 859 | 
             
              def setex(key, ttl, value)
         | 
| @@ -825,7 +865,7 @@ class Redis | |
| 825 865 | 
             
              # Set the time to live in milliseconds of a key.
         | 
| 826 866 | 
             
              #
         | 
| 827 867 | 
             
              # @param [String] key
         | 
| 828 | 
            -
              # @param [ | 
| 868 | 
            +
              # @param [Integer] ttl
         | 
| 829 869 | 
             
              # @param [String] value
         | 
| 830 870 | 
             
              # @return [String] `"OK"`
         | 
| 831 871 | 
             
              def psetex(key, ttl, value)
         | 
| @@ -887,7 +927,7 @@ class Redis | |
| 887 927 | 
             
              # @see #mapped_msetnx
         | 
| 888 928 | 
             
              def msetnx(*args)
         | 
| 889 929 | 
             
                synchronize do |client|
         | 
| 890 | 
            -
                  client.call([:msetnx | 
| 930 | 
            +
                  client.call([:msetnx, *args], &Boolify)
         | 
| 891 931 | 
             
                end
         | 
| 892 932 | 
             
              end
         | 
| 893 933 |  | 
| @@ -918,7 +958,7 @@ class Redis | |
| 918 958 | 
             
              # Get the values of all the given keys.
         | 
| 919 959 | 
             
              #
         | 
| 920 960 | 
             
              # @example
         | 
| 921 | 
            -
              #   redis.mget("key1", " | 
| 961 | 
            +
              #   redis.mget("key1", "key2")
         | 
| 922 962 | 
             
              #     # => ["v1", "v2"]
         | 
| 923 963 | 
             
              #
         | 
| 924 964 | 
             
              # @param [Array<String>] keys
         | 
| @@ -927,7 +967,7 @@ class Redis | |
| 927 967 | 
             
              # @see #mapped_mget
         | 
| 928 968 | 
             
              def mget(*keys, &blk)
         | 
| 929 969 | 
             
                synchronize do |client|
         | 
| 930 | 
            -
                  client.call([:mget | 
| 970 | 
            +
                  client.call([:mget, *keys], &blk)
         | 
| 931 971 | 
             
                end
         | 
| 932 972 | 
             
              end
         | 
| 933 973 |  | 
| @@ -943,7 +983,7 @@ class Redis | |
| 943 983 | 
             
              # @see #mget
         | 
| 944 984 | 
             
              def mapped_mget(*keys)
         | 
| 945 985 | 
             
                mget(*keys) do |reply|
         | 
| 946 | 
            -
                  if reply. | 
| 986 | 
            +
                  if reply.is_a?(Array)
         | 
| 947 987 | 
             
                    Hash[keys.zip(reply)]
         | 
| 948 988 | 
             
                  else
         | 
| 949 989 | 
             
                    reply
         | 
| @@ -954,9 +994,9 @@ class Redis | |
| 954 994 | 
             
              # Overwrite part of a string at key starting at the specified offset.
         | 
| 955 995 | 
             
              #
         | 
| 956 996 | 
             
              # @param [String] key
         | 
| 957 | 
            -
              # @param [ | 
| 997 | 
            +
              # @param [Integer] offset byte offset
         | 
| 958 998 | 
             
              # @param [String] value
         | 
| 959 | 
            -
              # @return [ | 
| 999 | 
            +
              # @return [Integer] length of the string after it was modified
         | 
| 960 1000 | 
             
              def setrange(key, offset, value)
         | 
| 961 1001 | 
             
                synchronize do |client|
         | 
| 962 1002 | 
             
                  client.call([:setrange, key, offset, value.to_s])
         | 
| @@ -966,10 +1006,10 @@ class Redis | |
| 966 1006 | 
             
              # Get a substring of the string stored at a key.
         | 
| 967 1007 | 
             
              #
         | 
| 968 1008 | 
             
              # @param [String] key
         | 
| 969 | 
            -
              # @param [ | 
| 970 | 
            -
              # @param [ | 
| 1009 | 
            +
              # @param [Integer] start zero-based start offset
         | 
| 1010 | 
            +
              # @param [Integer] stop zero-based end offset. Use -1 for representing
         | 
| 971 1011 | 
             
              #   the end of the string
         | 
| 972 | 
            -
              # @return [ | 
| 1012 | 
            +
              # @return [Integer] `0` or `1`
         | 
| 973 1013 | 
             
              def getrange(key, start, stop)
         | 
| 974 1014 | 
             
                synchronize do |client|
         | 
| 975 1015 | 
             
                  client.call([:getrange, key, start, stop])
         | 
| @@ -979,9 +1019,9 @@ class Redis | |
| 979 1019 | 
             
              # Sets or clears the bit at offset in the string value stored at key.
         | 
| 980 1020 | 
             
              #
         | 
| 981 1021 | 
             
              # @param [String] key
         | 
| 982 | 
            -
              # @param [ | 
| 983 | 
            -
              # @param [ | 
| 984 | 
            -
              # @return [ | 
| 1022 | 
            +
              # @param [Integer] offset bit offset
         | 
| 1023 | 
            +
              # @param [Integer] value bit value `0` or `1`
         | 
| 1024 | 
            +
              # @return [Integer] the original bit value stored at `offset`
         | 
| 985 1025 | 
             
              def setbit(key, offset, value)
         | 
| 986 1026 | 
             
                synchronize do |client|
         | 
| 987 1027 | 
             
                  client.call([:setbit, key, offset, value])
         | 
| @@ -991,8 +1031,8 @@ class Redis | |
| 991 1031 | 
             
              # Returns the bit value at offset in the string value stored at key.
         | 
| 992 1032 | 
             
              #
         | 
| 993 1033 | 
             
              # @param [String] key
         | 
| 994 | 
            -
              # @param [ | 
| 995 | 
            -
              # @return [ | 
| 1034 | 
            +
              # @param [Integer] offset bit offset
         | 
| 1035 | 
            +
              # @return [Integer] `0` or `1`
         | 
| 996 1036 | 
             
              def getbit(key, offset)
         | 
| 997 1037 | 
             
                synchronize do |client|
         | 
| 998 1038 | 
             
                  client.call([:getbit, key, offset])
         | 
| @@ -1003,7 +1043,7 @@ class Redis | |
| 1003 1043 | 
             
              #
         | 
| 1004 1044 | 
             
              # @param [String] key
         | 
| 1005 1045 | 
             
              # @param [String] value value to append
         | 
| 1006 | 
            -
              # @return [ | 
| 1046 | 
            +
              # @return [Integer] length of the string after appending
         | 
| 1007 1047 | 
             
              def append(key, value)
         | 
| 1008 1048 | 
             
                synchronize do |client|
         | 
| 1009 1049 | 
             
                  client.call([:append, key, value])
         | 
| @@ -1013,9 +1053,9 @@ class Redis | |
| 1013 1053 | 
             
              # Count the number of set bits in a range of the string value stored at key.
         | 
| 1014 1054 | 
             
              #
         | 
| 1015 1055 | 
             
              # @param [String] key
         | 
| 1016 | 
            -
              # @param [ | 
| 1017 | 
            -
              # @param [ | 
| 1018 | 
            -
              # @return [ | 
| 1056 | 
            +
              # @param [Integer] start start index
         | 
| 1057 | 
            +
              # @param [Integer] stop stop index
         | 
| 1058 | 
            +
              # @return [Integer] the number of bits set to 1
         | 
| 1019 1059 | 
             
              def bitcount(key, start = 0, stop = -1)
         | 
| 1020 1060 | 
             
                synchronize do |client|
         | 
| 1021 1061 | 
             
                  client.call([:bitcount, key, start, stop])
         | 
| @@ -1027,25 +1067,23 @@ class Redis | |
| 1027 1067 | 
             
              # @param [String] operation e.g. `and`, `or`, `xor`, `not`
         | 
| 1028 1068 | 
             
              # @param [String] destkey destination key
         | 
| 1029 1069 | 
             
              # @param [String, Array<String>] keys one or more source keys to perform `operation`
         | 
| 1030 | 
            -
              # @return [ | 
| 1070 | 
            +
              # @return [Integer] the length of the string stored in `destkey`
         | 
| 1031 1071 | 
             
              def bitop(operation, destkey, *keys)
         | 
| 1032 1072 | 
             
                synchronize do |client|
         | 
| 1033 | 
            -
                  client.call([:bitop, operation, destkey | 
| 1073 | 
            +
                  client.call([:bitop, operation, destkey, *keys])
         | 
| 1034 1074 | 
             
                end
         | 
| 1035 1075 | 
             
              end
         | 
| 1036 1076 |  | 
| 1037 1077 | 
             
              # Return the position of the first bit set to 1 or 0 in a string.
         | 
| 1038 1078 | 
             
              #
         | 
| 1039 1079 | 
             
              # @param [String] key
         | 
| 1040 | 
            -
              # @param [ | 
| 1041 | 
            -
              # @param [ | 
| 1042 | 
            -
              # @param [ | 
| 1043 | 
            -
              # @return [ | 
| 1080 | 
            +
              # @param [Integer] bit whether to look for the first 1 or 0 bit
         | 
| 1081 | 
            +
              # @param [Integer] start start index
         | 
| 1082 | 
            +
              # @param [Integer] stop stop index
         | 
| 1083 | 
            +
              # @return [Integer] the position of the first 1/0 bit.
         | 
| 1044 1084 | 
             
              #                  -1 if looking for 1 and it is not found or start and stop are given.
         | 
| 1045 | 
            -
              def bitpos(key, bit, start=nil, stop=nil)
         | 
| 1046 | 
            -
                 | 
| 1047 | 
            -
                  raise(ArgumentError, 'stop parameter specified without start parameter')
         | 
| 1048 | 
            -
                end
         | 
| 1085 | 
            +
              def bitpos(key, bit, start = nil, stop = nil)
         | 
| 1086 | 
            +
                raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
         | 
| 1049 1087 |  | 
| 1050 1088 | 
             
                synchronize do |client|
         | 
| 1051 1089 | 
             
                  command = [:bitpos, key, bit]
         | 
| @@ -1070,7 +1108,7 @@ class Redis | |
| 1070 1108 | 
             
              # Get the length of the value stored in a key.
         | 
| 1071 1109 | 
             
              #
         | 
| 1072 1110 | 
             
              # @param [String] key
         | 
| 1073 | 
            -
              # @return [ | 
| 1111 | 
            +
              # @return [Integer] the length of the value stored in the key, or 0
         | 
| 1074 1112 | 
             
              #   if the key does not exist
         | 
| 1075 1113 | 
             
              def strlen(key)
         | 
| 1076 1114 | 
             
                synchronize do |client|
         | 
| @@ -1081,7 +1119,7 @@ class Redis | |
| 1081 1119 | 
             
              # Get the length of a list.
         | 
| 1082 1120 | 
             
              #
         | 
| 1083 1121 | 
             
              # @param [String] key
         | 
| 1084 | 
            -
              # @return [ | 
| 1122 | 
            +
              # @return [Integer]
         | 
| 1085 1123 | 
             
              def llen(key)
         | 
| 1086 1124 | 
             
                synchronize do |client|
         | 
| 1087 1125 | 
             
                  client.call([:llen, key])
         | 
| @@ -1092,7 +1130,7 @@ class Redis | |
| 1092 1130 | 
             
              #
         | 
| 1093 1131 | 
             
              # @param [String] key
         | 
| 1094 1132 | 
             
              # @param [String, Array<String>] value string value, or array of string values to push
         | 
| 1095 | 
            -
              # @return [ | 
| 1133 | 
            +
              # @return [Integer] the length of the list after the push operation
         | 
| 1096 1134 | 
             
              def lpush(key, value)
         | 
| 1097 1135 | 
             
                synchronize do |client|
         | 
| 1098 1136 | 
             
                  client.call([:lpush, key, value])
         | 
| @@ -1103,7 +1141,7 @@ class Redis | |
| 1103 1141 | 
             
              #
         | 
| 1104 1142 | 
             
              # @param [String] key
         | 
| 1105 1143 | 
             
              # @param [String] value
         | 
| 1106 | 
            -
              # @return [ | 
| 1144 | 
            +
              # @return [Integer] the length of the list after the push operation
         | 
| 1107 1145 | 
             
              def lpushx(key, value)
         | 
| 1108 1146 | 
             
                synchronize do |client|
         | 
| 1109 1147 | 
             
                  client.call([:lpushx, key, value])
         | 
| @@ -1114,7 +1152,7 @@ class Redis | |
| 1114 1152 | 
             
              #
         | 
| 1115 1153 | 
             
              # @param [String] key
         | 
| 1116 1154 | 
             
              # @param [String, Array<String>] value string value, or array of string values to push
         | 
| 1117 | 
            -
              # @return [ | 
| 1155 | 
            +
              # @return [Integer] the length of the list after the push operation
         | 
| 1118 1156 | 
             
              def rpush(key, value)
         | 
| 1119 1157 | 
             
                synchronize do |client|
         | 
| 1120 1158 | 
             
                  client.call([:rpush, key, value])
         | 
| @@ -1125,7 +1163,7 @@ class Redis | |
| 1125 1163 | 
             
              #
         | 
| 1126 1164 | 
             
              # @param [String] key
         | 
| 1127 1165 | 
             
              # @param [String] value
         | 
| 1128 | 
            -
              # @return [ | 
| 1166 | 
            +
              # @return [Integer] the length of the list after the push operation
         | 
| 1129 1167 | 
             
              def rpushx(key, value)
         | 
| 1130 1168 | 
             
                synchronize do |client|
         | 
| 1131 1169 | 
             
                  client.call([:rpushx, key, value])
         | 
| @@ -1164,21 +1202,21 @@ class Redis | |
| 1164 1202 | 
             
              end
         | 
| 1165 1203 |  | 
| 1166 1204 | 
             
              def _bpop(cmd, args, &blk)
         | 
| 1167 | 
            -
                 | 
| 1168 | 
            -
             | 
| 1169 | 
            -
                if args.last.is_a?(Hash)
         | 
| 1205 | 
            +
                timeout = if args.last.is_a?(Hash)
         | 
| 1170 1206 | 
             
                  options = args.pop
         | 
| 1207 | 
            +
                  options[:timeout]
         | 
| 1171 1208 | 
             
                elsif args.last.respond_to?(:to_int)
         | 
| 1172 1209 | 
             
                  # Issue deprecation notice in obnoxious mode...
         | 
| 1173 | 
            -
                   | 
| 1210 | 
            +
                  args.pop.to_int
         | 
| 1174 1211 | 
             
                end
         | 
| 1175 1212 |  | 
| 1213 | 
            +
                timeout ||= 0
         | 
| 1214 | 
            +
             | 
| 1176 1215 | 
             
                if args.size > 1
         | 
| 1177 1216 | 
             
                  # Issue deprecation notice in obnoxious mode...
         | 
| 1178 1217 | 
             
                end
         | 
| 1179 1218 |  | 
| 1180 1219 | 
             
                keys = args.flatten
         | 
| 1181 | 
            -
                timeout = options[:timeout] || 0
         | 
| 1182 1220 |  | 
| 1183 1221 | 
             
                synchronize do |client|
         | 
| 1184 1222 | 
             
                  command = [cmd, keys, timeout]
         | 
| @@ -1203,7 +1241,7 @@ class Redis | |
| 1203 1241 | 
             
              # @param [String, Array<String>] keys one or more keys to perform the
         | 
| 1204 1242 | 
             
              #   blocking pop on
         | 
| 1205 1243 | 
             
              # @param [Hash] options
         | 
| 1206 | 
            -
              #   - `:timeout =>  | 
| 1244 | 
            +
              #   - `:timeout => Integer`: timeout in seconds, defaults to no timeout
         | 
| 1207 1245 | 
             
              #
         | 
| 1208 1246 | 
             
              # @return [nil, [String, String]]
         | 
| 1209 1247 | 
             
              #   - `nil` when the operation timed out
         | 
| @@ -1217,7 +1255,7 @@ class Redis | |
| 1217 1255 | 
             
              # @param [String, Array<String>] keys one or more keys to perform the
         | 
| 1218 1256 | 
             
              #   blocking pop on
         | 
| 1219 1257 | 
             
              # @param [Hash] options
         | 
| 1220 | 
            -
              #   - `:timeout =>  | 
| 1258 | 
            +
              #   - `:timeout => Integer`: timeout in seconds, defaults to no timeout
         | 
| 1221 1259 | 
             
              #
         | 
| 1222 1260 | 
             
              # @return [nil, [String, String]]
         | 
| 1223 1261 | 
             
              #   - `nil` when the operation timed out
         | 
| @@ -1234,20 +1272,12 @@ class Redis | |
| 1234 1272 | 
             
              # @param [String] source source key
         | 
| 1235 1273 | 
             
              # @param [String] destination destination key
         | 
| 1236 1274 | 
             
              # @param [Hash] options
         | 
| 1237 | 
            -
              #   - `:timeout =>  | 
| 1275 | 
            +
              #   - `:timeout => Integer`: timeout in seconds, defaults to no timeout
         | 
| 1238 1276 | 
             
              #
         | 
| 1239 1277 | 
             
              # @return [nil, String]
         | 
| 1240 1278 | 
             
              #   - `nil` when the operation timed out
         | 
| 1241 1279 | 
             
              #   - the element was popped and pushed otherwise
         | 
| 1242 | 
            -
              def brpoplpush(source, destination,  | 
| 1243 | 
            -
                case options
         | 
| 1244 | 
            -
                when Integer
         | 
| 1245 | 
            -
                  # Issue deprecation notice in obnoxious mode...
         | 
| 1246 | 
            -
                  options = { :timeout => options }
         | 
| 1247 | 
            -
                end
         | 
| 1248 | 
            -
             | 
| 1249 | 
            -
                timeout = options[:timeout] || 0
         | 
| 1250 | 
            -
             | 
| 1280 | 
            +
              def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
         | 
| 1251 1281 | 
             
                synchronize do |client|
         | 
| 1252 1282 | 
             
                  command = [:brpoplpush, source, destination, timeout]
         | 
| 1253 1283 | 
             
                  timeout += client.timeout if timeout > 0
         | 
| @@ -1258,7 +1288,7 @@ class Redis | |
| 1258 1288 | 
             
              # Get an element from a list by its index.
         | 
| 1259 1289 | 
             
              #
         | 
| 1260 1290 | 
             
              # @param [String] key
         | 
| 1261 | 
            -
              # @param [ | 
| 1291 | 
            +
              # @param [Integer] index
         | 
| 1262 1292 | 
             
              # @return [String]
         | 
| 1263 1293 | 
             
              def lindex(key, index)
         | 
| 1264 1294 | 
             
                synchronize do |client|
         | 
| @@ -1272,7 +1302,7 @@ class Redis | |
| 1272 1302 | 
             
              # @param [String, Symbol] where `BEFORE` or `AFTER`
         | 
| 1273 1303 | 
             
              # @param [String] pivot reference element
         | 
| 1274 1304 | 
             
              # @param [String] value
         | 
| 1275 | 
            -
              # @return [ | 
| 1305 | 
            +
              # @return [Integer] length of the list after the insert operation, or `-1`
         | 
| 1276 1306 | 
             
              #   when the element `pivot` was not found
         | 
| 1277 1307 | 
             
              def linsert(key, where, pivot, value)
         | 
| 1278 1308 | 
             
                synchronize do |client|
         | 
| @@ -1283,8 +1313,8 @@ class Redis | |
| 1283 1313 | 
             
              # Get a range of elements from a list.
         | 
| 1284 1314 | 
             
              #
         | 
| 1285 1315 | 
             
              # @param [String] key
         | 
| 1286 | 
            -
              # @param [ | 
| 1287 | 
            -
              # @param [ | 
| 1316 | 
            +
              # @param [Integer] start start index
         | 
| 1317 | 
            +
              # @param [Integer] stop stop index
         | 
| 1288 1318 | 
             
              # @return [Array<String>]
         | 
| 1289 1319 | 
             
              def lrange(key, start, stop)
         | 
| 1290 1320 | 
             
                synchronize do |client|
         | 
| @@ -1295,12 +1325,12 @@ class Redis | |
| 1295 1325 | 
             
              # Remove elements from a list.
         | 
| 1296 1326 | 
             
              #
         | 
| 1297 1327 | 
             
              # @param [String] key
         | 
| 1298 | 
            -
              # @param [ | 
| 1328 | 
            +
              # @param [Integer] count number of elements to remove. Use a positive
         | 
| 1299 1329 | 
             
              #   value to remove the first `count` occurrences of `value`. A negative
         | 
| 1300 1330 | 
             
              #   value to remove the last `count` occurrences of `value`. Or zero, to
         | 
| 1301 1331 | 
             
              #   remove all occurrences of `value` from the list.
         | 
| 1302 1332 | 
             
              # @param [String] value
         | 
| 1303 | 
            -
              # @return [ | 
| 1333 | 
            +
              # @return [Integer] the number of removed elements
         | 
| 1304 1334 | 
             
              def lrem(key, count, value)
         | 
| 1305 1335 | 
             
                synchronize do |client|
         | 
| 1306 1336 | 
             
                  client.call([:lrem, key, count, value])
         | 
| @@ -1310,7 +1340,7 @@ class Redis | |
| 1310 1340 | 
             
              # Set the value of an element in a list by its index.
         | 
| 1311 1341 | 
             
              #
         | 
| 1312 1342 | 
             
              # @param [String] key
         | 
| 1313 | 
            -
              # @param [ | 
| 1343 | 
            +
              # @param [Integer] index
         | 
| 1314 1344 | 
             
              # @param [String] value
         | 
| 1315 1345 | 
             
              # @return [String] `OK`
         | 
| 1316 1346 | 
             
              def lset(key, index, value)
         | 
| @@ -1322,8 +1352,8 @@ class Redis | |
| 1322 1352 | 
             
              # Trim a list to the specified range.
         | 
| 1323 1353 | 
             
              #
         | 
| 1324 1354 | 
             
              # @param [String] key
         | 
| 1325 | 
            -
              # @param [ | 
| 1326 | 
            -
              # @param [ | 
| 1355 | 
            +
              # @param [Integer] start start index
         | 
| 1356 | 
            +
              # @param [Integer] stop stop index
         | 
| 1327 1357 | 
             
              # @return [String] `OK`
         | 
| 1328 1358 | 
             
              def ltrim(key, start, stop)
         | 
| 1329 1359 | 
             
                synchronize do |client|
         | 
| @@ -1334,7 +1364,7 @@ class Redis | |
| 1334 1364 | 
             
              # Get the number of members in a set.
         | 
| 1335 1365 | 
             
              #
         | 
| 1336 1366 | 
             
              # @param [String] key
         | 
| 1337 | 
            -
              # @return [ | 
| 1367 | 
            +
              # @return [Integer]
         | 
| 1338 1368 | 
             
              def scard(key)
         | 
| 1339 1369 | 
             
                synchronize do |client|
         | 
| 1340 1370 | 
             
                  client.call([:scard, key])
         | 
| @@ -1345,8 +1375,8 @@ class Redis | |
| 1345 1375 | 
             
              #
         | 
| 1346 1376 | 
             
              # @param [String] key
         | 
| 1347 1377 | 
             
              # @param [String, Array<String>] member one member, or array of members
         | 
| 1348 | 
            -
              # @return [Boolean,  | 
| 1349 | 
            -
              #   holding whether or not adding the member succeeded, or ` | 
| 1378 | 
            +
              # @return [Boolean, Integer] `Boolean` when a single member is specified,
         | 
| 1379 | 
            +
              #   holding whether or not adding the member succeeded, or `Integer` when an
         | 
| 1350 1380 | 
             
              #   array of members is specified, holding the number of members that were
         | 
| 1351 1381 | 
             
              #   successfully added
         | 
| 1352 1382 | 
             
              def sadd(key, member)
         | 
| @@ -1367,8 +1397,8 @@ class Redis | |
| 1367 1397 | 
             
              #
         | 
| 1368 1398 | 
             
              # @param [String] key
         | 
| 1369 1399 | 
             
              # @param [String, Array<String>] member one member, or array of members
         | 
| 1370 | 
            -
              # @return [Boolean,  | 
| 1371 | 
            -
              #   holding whether or not removing the member succeeded, or ` | 
| 1400 | 
            +
              # @return [Boolean, Integer] `Boolean` when a single member is specified,
         | 
| 1401 | 
            +
              #   holding whether or not removing the member succeeded, or `Integer` when an
         | 
| 1372 1402 | 
             
              #   array of members is specified, holding the number of members that were
         | 
| 1373 1403 | 
             
              #   successfully removed
         | 
| 1374 1404 | 
             
              def srem(key, member)
         | 
| @@ -1389,7 +1419,7 @@ class Redis | |
| 1389 1419 | 
             
              #
         | 
| 1390 1420 | 
             
              # @param [String] key
         | 
| 1391 1421 | 
             
              # @return [String]
         | 
| 1392 | 
            -
              # @param [ | 
| 1422 | 
            +
              # @param [Integer] count
         | 
| 1393 1423 | 
             
              def spop(key, count = nil)
         | 
| 1394 1424 | 
             
                synchronize do |client|
         | 
| 1395 1425 | 
             
                  if count.nil?
         | 
| @@ -1403,7 +1433,7 @@ class Redis | |
| 1403 1433 | 
             
              # Get one or more random members from a set.
         | 
| 1404 1434 | 
             
              #
         | 
| 1405 1435 | 
             
              # @param [String] key
         | 
| 1406 | 
            -
              # @param [ | 
| 1436 | 
            +
              # @param [Integer] count
         | 
| 1407 1437 | 
             
              # @return [String]
         | 
| 1408 1438 | 
             
              def srandmember(key, count = nil)
         | 
| 1409 1439 | 
             
                synchronize do |client|
         | 
| @@ -1454,7 +1484,7 @@ class Redis | |
| 1454 1484 | 
             
              # @return [Array<String>] members in the difference
         | 
| 1455 1485 | 
             
              def sdiff(*keys)
         | 
| 1456 1486 | 
             
                synchronize do |client|
         | 
| 1457 | 
            -
                  client.call([:sdiff | 
| 1487 | 
            +
                  client.call([:sdiff, *keys])
         | 
| 1458 1488 | 
             
                end
         | 
| 1459 1489 | 
             
              end
         | 
| 1460 1490 |  | 
| @@ -1462,10 +1492,10 @@ class Redis | |
| 1462 1492 | 
             
              #
         | 
| 1463 1493 | 
             
              # @param [String] destination destination key
         | 
| 1464 1494 | 
             
              # @param [String, Array<String>] keys keys pointing to sets to subtract
         | 
| 1465 | 
            -
              # @return [ | 
| 1495 | 
            +
              # @return [Integer] number of elements in the resulting set
         | 
| 1466 1496 | 
             
              def sdiffstore(destination, *keys)
         | 
| 1467 1497 | 
             
                synchronize do |client|
         | 
| 1468 | 
            -
                  client.call([:sdiffstore, destination | 
| 1498 | 
            +
                  client.call([:sdiffstore, destination, *keys])
         | 
| 1469 1499 | 
             
                end
         | 
| 1470 1500 | 
             
              end
         | 
| 1471 1501 |  | 
| @@ -1475,7 +1505,7 @@ class Redis | |
| 1475 1505 | 
             
              # @return [Array<String>] members in the intersection
         | 
| 1476 1506 | 
             
              def sinter(*keys)
         | 
| 1477 1507 | 
             
                synchronize do |client|
         | 
| 1478 | 
            -
                  client.call([:sinter | 
| 1508 | 
            +
                  client.call([:sinter, *keys])
         | 
| 1479 1509 | 
             
                end
         | 
| 1480 1510 | 
             
              end
         | 
| 1481 1511 |  | 
| @@ -1483,10 +1513,10 @@ class Redis | |
| 1483 1513 | 
             
              #
         | 
| 1484 1514 | 
             
              # @param [String] destination destination key
         | 
| 1485 1515 | 
             
              # @param [String, Array<String>] keys keys pointing to sets to intersect
         | 
| 1486 | 
            -
              # @return [ | 
| 1516 | 
            +
              # @return [Integer] number of elements in the resulting set
         | 
| 1487 1517 | 
             
              def sinterstore(destination, *keys)
         | 
| 1488 1518 | 
             
                synchronize do |client|
         | 
| 1489 | 
            -
                  client.call([:sinterstore, destination | 
| 1519 | 
            +
                  client.call([:sinterstore, destination, *keys])
         | 
| 1490 1520 | 
             
                end
         | 
| 1491 1521 | 
             
              end
         | 
| 1492 1522 |  | 
| @@ -1496,7 +1526,7 @@ class Redis | |
| 1496 1526 | 
             
              # @return [Array<String>] members in the union
         | 
| 1497 1527 | 
             
              def sunion(*keys)
         | 
| 1498 1528 | 
             
                synchronize do |client|
         | 
| 1499 | 
            -
                  client.call([:sunion | 
| 1529 | 
            +
                  client.call([:sunion, *keys])
         | 
| 1500 1530 | 
             
                end
         | 
| 1501 1531 | 
             
              end
         | 
| 1502 1532 |  | 
| @@ -1504,10 +1534,10 @@ class Redis | |
| 1504 1534 | 
             
              #
         | 
| 1505 1535 | 
             
              # @param [String] destination destination key
         | 
| 1506 1536 | 
             
              # @param [String, Array<String>] keys keys pointing to sets to unify
         | 
| 1507 | 
            -
              # @return [ | 
| 1537 | 
            +
              # @return [Integer] number of elements in the resulting set
         | 
| 1508 1538 | 
             
              def sunionstore(destination, *keys)
         | 
| 1509 1539 | 
             
                synchronize do |client|
         | 
| 1510 | 
            -
                  client.call([:sunionstore, destination | 
| 1540 | 
            +
                  client.call([:sunionstore, destination, *keys])
         | 
| 1511 1541 | 
             
                end
         | 
| 1512 1542 | 
             
              end
         | 
| 1513 1543 |  | 
| @@ -1518,7 +1548,7 @@ class Redis | |
| 1518 1548 | 
             
              #     # => 4
         | 
| 1519 1549 | 
             
              #
         | 
| 1520 1550 | 
             
              # @param [String] key
         | 
| 1521 | 
            -
              # @return [ | 
| 1551 | 
            +
              # @return [Integer]
         | 
| 1522 1552 | 
             
              def zcard(key)
         | 
| 1523 1553 | 
             
                synchronize do |client|
         | 
| 1524 1554 | 
             
                  client.call([:zcard, key])
         | 
| @@ -1549,38 +1579,27 @@ class Redis | |
| 1549 1579 | 
             
              #   - `:incr => true`: When this option is specified ZADD acts like
         | 
| 1550 1580 | 
             
              #   ZINCRBY; only one score-element pair can be specified in this mode
         | 
| 1551 1581 | 
             
              #
         | 
| 1552 | 
            -
              # @return [Boolean,  | 
| 1582 | 
            +
              # @return [Boolean, Integer, Float]
         | 
| 1553 1583 | 
             
              #   - `Boolean` when a single pair is specified, holding whether or not it was
         | 
| 1554 1584 | 
             
              #   **added** to the sorted set.
         | 
| 1555 | 
            -
              #   - ` | 
| 1585 | 
            +
              #   - `Integer` when an array of pairs is specified, holding the number of
         | 
| 1556 1586 | 
             
              #   pairs that were **added** to the sorted set.
         | 
| 1557 1587 | 
             
              #   - `Float` when option :incr is specified, holding the score of the member
         | 
| 1558 1588 | 
             
              #   after incrementing it.
         | 
| 1559 | 
            -
              def zadd(key, *args | 
| 1560 | 
            -
                 | 
| 1561 | 
            -
                if  | 
| 1562 | 
            -
             | 
| 1563 | 
            -
             | 
| 1564 | 
            -
             | 
| 1565 | 
            -
                  zadd_options << "NX" if nx
         | 
| 1566 | 
            -
             | 
| 1567 | 
            -
                  xx = options[:xx]
         | 
| 1568 | 
            -
                  zadd_options << "XX" if xx
         | 
| 1569 | 
            -
             | 
| 1570 | 
            -
                  ch = options[:ch]
         | 
| 1571 | 
            -
                  zadd_options << "CH" if ch
         | 
| 1572 | 
            -
             | 
| 1573 | 
            -
                  incr = options[:incr]
         | 
| 1574 | 
            -
                  zadd_options << "INCR" if incr
         | 
| 1575 | 
            -
                end
         | 
| 1589 | 
            +
              def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
         | 
| 1590 | 
            +
                command = [:zadd, key]
         | 
| 1591 | 
            +
                command << "NX" if nx
         | 
| 1592 | 
            +
                command << "XX" if xx
         | 
| 1593 | 
            +
                command << "CH" if ch
         | 
| 1594 | 
            +
                command << "INCR" if incr
         | 
| 1576 1595 |  | 
| 1577 1596 | 
             
                synchronize do |client|
         | 
| 1578 1597 | 
             
                  if args.size == 1 && args[0].is_a?(Array)
         | 
| 1579 1598 | 
             
                    # Variadic: return float if INCR, integer if !INCR
         | 
| 1580 | 
            -
                    client.call( | 
| 1599 | 
            +
                    client.call(command + args[0], &(incr ? Floatify : nil))
         | 
| 1581 1600 | 
             
                  elsif args.size == 2
         | 
| 1582 1601 | 
             
                    # Single pair: return float if INCR, boolean if !INCR
         | 
| 1583 | 
            -
                    client.call( | 
| 1602 | 
            +
                    client.call(command + args, &(incr ? Floatify : Boolify))
         | 
| 1584 1603 | 
             
                  else
         | 
| 1585 1604 | 
             
                    raise ArgumentError, "wrong number of arguments"
         | 
| 1586 1605 | 
             
                  end
         | 
| @@ -1615,10 +1634,10 @@ class Redis | |
| 1615 1634 | 
             
              #   - a single member
         | 
| 1616 1635 | 
             
              #   - an array of members
         | 
| 1617 1636 | 
             
              #
         | 
| 1618 | 
            -
              # @return [Boolean,  | 
| 1637 | 
            +
              # @return [Boolean, Integer]
         | 
| 1619 1638 | 
             
              #   - `Boolean` when a single member is specified, holding whether or not it
         | 
| 1620 1639 | 
             
              #   was removed from the sorted set
         | 
| 1621 | 
            -
              #   - ` | 
| 1640 | 
            +
              #   - `Integer` when an array of pairs is specified, holding the number of
         | 
| 1622 1641 | 
             
              #   members that were removed to the sorted set
         | 
| 1623 1642 | 
             
              def zrem(key, member)
         | 
| 1624 1643 | 
             
                synchronize do |client|
         | 
| @@ -1743,18 +1762,16 @@ class Redis | |
| 1743 1762 | 
             
              #     # => [["a", 32.0], ["b", 64.0]]
         | 
| 1744 1763 | 
             
              #
         | 
| 1745 1764 | 
             
              # @param [String] key
         | 
| 1746 | 
            -
              # @param [ | 
| 1747 | 
            -
              # @param [ | 
| 1765 | 
            +
              # @param [Integer] start start index
         | 
| 1766 | 
            +
              # @param [Integer] stop stop index
         | 
| 1748 1767 | 
             
              # @param [Hash] options
         | 
| 1749 1768 | 
             
              #   - `:with_scores => true`: include scores in output
         | 
| 1750 1769 | 
             
              #
         | 
| 1751 1770 | 
             
              # @return [Array<String>, Array<[String, Float]>]
         | 
| 1752 1771 | 
             
              #   - when `:with_scores` is not specified, an array of members
         | 
| 1753 1772 | 
             
              #   - when `:with_scores` is specified, an array with `[member, score]` pairs
         | 
| 1754 | 
            -
              def zrange(key, start, stop,  | 
| 1755 | 
            -
                args = []
         | 
| 1756 | 
            -
             | 
| 1757 | 
            -
                with_scores = options[:with_scores] || options[:withscores]
         | 
| 1773 | 
            +
              def zrange(key, start, stop, withscores: false, with_scores: withscores)
         | 
| 1774 | 
            +
                args = [:zrange, key, start, stop]
         | 
| 1758 1775 |  | 
| 1759 1776 | 
             
                if with_scores
         | 
| 1760 1777 | 
             
                  args << "WITHSCORES"
         | 
| @@ -1762,7 +1779,7 @@ class Redis | |
| 1762 1779 | 
             
                end
         | 
| 1763 1780 |  | 
| 1764 1781 | 
             
                synchronize do |client|
         | 
| 1765 | 
            -
                  client.call( | 
| 1782 | 
            +
                  client.call(args, &block)
         | 
| 1766 1783 | 
             
                end
         | 
| 1767 1784 | 
             
              end
         | 
| 1768 1785 |  | 
| @@ -1777,10 +1794,8 @@ class Redis | |
| 1777 1794 | 
             
              #     # => [["b", 64.0], ["a", 32.0]]
         | 
| 1778 1795 | 
             
              #
         | 
| 1779 1796 | 
             
              # @see #zrange
         | 
| 1780 | 
            -
              def zrevrange(key, start, stop,  | 
| 1781 | 
            -
                args = []
         | 
| 1782 | 
            -
             | 
| 1783 | 
            -
                with_scores = options[:with_scores] || options[:withscores]
         | 
| 1797 | 
            +
              def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
         | 
| 1798 | 
            +
                args = [:zrevrange, key, start, stop]
         | 
| 1784 1799 |  | 
| 1785 1800 | 
             
                if with_scores
         | 
| 1786 1801 | 
             
                  args << "WITHSCORES"
         | 
| @@ -1788,7 +1803,7 @@ class Redis | |
| 1788 1803 | 
             
                end
         | 
| 1789 1804 |  | 
| 1790 1805 | 
             
                synchronize do |client|
         | 
| 1791 | 
            -
                  client.call( | 
| 1806 | 
            +
                  client.call(args, &block)
         | 
| 1792 1807 | 
             
                end
         | 
| 1793 1808 | 
             
              end
         | 
| 1794 1809 |  | 
| @@ -1796,7 +1811,7 @@ class Redis | |
| 1796 1811 | 
             
              #
         | 
| 1797 1812 | 
             
              # @param [String] key
         | 
| 1798 1813 | 
             
              # @param [String] member
         | 
| 1799 | 
            -
              # @return [ | 
| 1814 | 
            +
              # @return [Integer]
         | 
| 1800 1815 | 
             
              def zrank(key, member)
         | 
| 1801 1816 | 
             
                synchronize do |client|
         | 
| 1802 1817 | 
             
                  client.call([:zrank, key, member])
         | 
| @@ -1808,7 +1823,7 @@ class Redis | |
| 1808 1823 | 
             
              #
         | 
| 1809 1824 | 
             
              # @param [String] key
         | 
| 1810 1825 | 
             
              # @param [String] member
         | 
| 1811 | 
            -
              # @return [ | 
| 1826 | 
            +
              # @return [Integer]
         | 
| 1812 1827 | 
             
              def zrevrank(key, member)
         | 
| 1813 1828 | 
             
                synchronize do |client|
         | 
| 1814 1829 | 
             
                  client.call([:zrevrank, key, member])
         | 
| @@ -1825,9 +1840,9 @@ class Redis | |
| 1825 1840 | 
             
              #     # => 5
         | 
| 1826 1841 | 
             
              #
         | 
| 1827 1842 | 
             
              # @param [String] key
         | 
| 1828 | 
            -
              # @param [ | 
| 1829 | 
            -
              # @param [ | 
| 1830 | 
            -
              # @return [ | 
| 1843 | 
            +
              # @param [Integer] start start index
         | 
| 1844 | 
            +
              # @param [Integer] stop stop index
         | 
| 1845 | 
            +
              # @return [Integer] number of members that were removed
         | 
| 1831 1846 | 
             
              def zremrangebyrank(key, start, stop)
         | 
| 1832 1847 | 
             
                synchronize do |client|
         | 
| 1833 1848 | 
             
                  client.call([:zremrangebyrank, key, start, stop])
         | 
| @@ -1851,7 +1866,7 @@ class Redis | |
| 1851 1866 | 
             
              #   - inclusive maximum is specified by prefixing `(`
         | 
| 1852 1867 | 
             
              #   - exclusive maximum is specified by prefixing `[`
         | 
| 1853 1868 | 
             
              #
         | 
| 1854 | 
            -
              # @return [ | 
| 1869 | 
            +
              # @return [Integer] number of members within the specified lexicographical range
         | 
| 1855 1870 | 
             
              def zlexcount(key, min, max)
         | 
| 1856 1871 | 
             
                synchronize do |client|
         | 
| 1857 1872 | 
             
                  client.call([:zlexcount, key, min, max])
         | 
| @@ -1879,14 +1894,16 @@ class Redis | |
| 1879 1894 | 
             
              #   `count` members
         | 
| 1880 1895 | 
             
              #
         | 
| 1881 1896 | 
             
              # @return [Array<String>, Array<[String, Float]>]
         | 
| 1882 | 
            -
              def zrangebylex(key, min, max,  | 
| 1883 | 
            -
                args = []
         | 
| 1897 | 
            +
              def zrangebylex(key, min, max, limit: nil)
         | 
| 1898 | 
            +
                args = [:zrangebylex, key, min, max]
         | 
| 1884 1899 |  | 
| 1885 | 
            -
                 | 
| 1886 | 
            -
             | 
| 1900 | 
            +
                if limit
         | 
| 1901 | 
            +
                  args << "LIMIT"
         | 
| 1902 | 
            +
                  args.concat(limit)
         | 
| 1903 | 
            +
                end
         | 
| 1887 1904 |  | 
| 1888 1905 | 
             
                synchronize do |client|
         | 
| 1889 | 
            -
                  client.call( | 
| 1906 | 
            +
                  client.call(args)
         | 
| 1890 1907 | 
             
                end
         | 
| 1891 1908 | 
             
              end
         | 
| 1892 1909 |  | 
| @@ -1901,14 +1918,16 @@ class Redis | |
| 1901 1918 | 
             
              #     # => ["abbygail", "abby"]
         | 
| 1902 1919 | 
             
              #
         | 
| 1903 1920 | 
             
              # @see #zrangebylex
         | 
| 1904 | 
            -
              def zrevrangebylex(key, max, min,  | 
| 1905 | 
            -
                args = []
         | 
| 1921 | 
            +
              def zrevrangebylex(key, max, min, limit: nil)
         | 
| 1922 | 
            +
                args = [:zrevrangebylex, key, max, min]
         | 
| 1906 1923 |  | 
| 1907 | 
            -
                 | 
| 1908 | 
            -
             | 
| 1924 | 
            +
                if limit
         | 
| 1925 | 
            +
                  args << "LIMIT"
         | 
| 1926 | 
            +
                  args.concat(limit)
         | 
| 1927 | 
            +
                end
         | 
| 1909 1928 |  | 
| 1910 1929 | 
             
                synchronize do |client|
         | 
| 1911 | 
            -
                  client.call( | 
| 1930 | 
            +
                  client.call(args)
         | 
| 1912 1931 | 
             
                end
         | 
| 1913 1932 | 
             
              end
         | 
| 1914 1933 |  | 
| @@ -1939,21 +1958,21 @@ class Redis | |
| 1939 1958 | 
             
              # @return [Array<String>, Array<[String, Float]>]
         | 
| 1940 1959 | 
             
              #   - when `:with_scores` is not specified, an array of members
         | 
| 1941 1960 | 
             
              #   - when `:with_scores` is specified, an array with `[member, score]` pairs
         | 
| 1942 | 
            -
              def zrangebyscore(key, min, max,  | 
| 1943 | 
            -
                args = []
         | 
| 1944 | 
            -
             | 
| 1945 | 
            -
                with_scores = options[:with_scores] || options[:withscores]
         | 
| 1961 | 
            +
              def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
         | 
| 1962 | 
            +
                args = [:zrangebyscore, key, min, max]
         | 
| 1946 1963 |  | 
| 1947 1964 | 
             
                if with_scores
         | 
| 1948 1965 | 
             
                  args << "WITHSCORES"
         | 
| 1949 1966 | 
             
                  block = FloatifyPairs
         | 
| 1950 1967 | 
             
                end
         | 
| 1951 1968 |  | 
| 1952 | 
            -
                 | 
| 1953 | 
            -
             | 
| 1969 | 
            +
                if limit
         | 
| 1970 | 
            +
                  args << "LIMIT"
         | 
| 1971 | 
            +
                  args.concat(limit)
         | 
| 1972 | 
            +
                end
         | 
| 1954 1973 |  | 
| 1955 1974 | 
             
                synchronize do |client|
         | 
| 1956 | 
            -
                  client.call( | 
| 1975 | 
            +
                  client.call(args, &block)
         | 
| 1957 1976 | 
             
                end
         | 
| 1958 1977 | 
             
              end
         | 
| 1959 1978 |  | 
| @@ -1971,21 +1990,21 @@ class Redis | |
| 1971 1990 | 
             
              #     # => [["b", 64.0], ["a", 32.0]]
         | 
| 1972 1991 | 
             
              #
         | 
| 1973 1992 | 
             
              # @see #zrangebyscore
         | 
| 1974 | 
            -
              def zrevrangebyscore(key, max, min,  | 
| 1975 | 
            -
                args = []
         | 
| 1976 | 
            -
             | 
| 1977 | 
            -
                with_scores = options[:with_scores] || options[:withscores]
         | 
| 1993 | 
            +
              def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
         | 
| 1994 | 
            +
                args = [:zrevrangebyscore, key, max, min]
         | 
| 1978 1995 |  | 
| 1979 1996 | 
             
                if with_scores
         | 
| 1980 | 
            -
                  args <<  | 
| 1997 | 
            +
                  args << "WITHSCORES"
         | 
| 1981 1998 | 
             
                  block = FloatifyPairs
         | 
| 1982 1999 | 
             
                end
         | 
| 1983 2000 |  | 
| 1984 | 
            -
                 | 
| 1985 | 
            -
             | 
| 2001 | 
            +
                if limit
         | 
| 2002 | 
            +
                  args << "LIMIT"
         | 
| 2003 | 
            +
                  args.concat(limit)
         | 
| 2004 | 
            +
                end
         | 
| 1986 2005 |  | 
| 1987 2006 | 
             
                synchronize do |client|
         | 
| 1988 | 
            -
                  client.call( | 
| 2007 | 
            +
                  client.call(args, &block)
         | 
| 1989 2008 | 
             
                end
         | 
| 1990 2009 | 
             
              end
         | 
| 1991 2010 |  | 
| @@ -2005,7 +2024,7 @@ class Redis | |
| 2005 2024 | 
             
              # @param [String] max
         | 
| 2006 2025 | 
             
              #   - inclusive maximum score is specified verbatim
         | 
| 2007 2026 | 
             
              #   - exclusive maximum score is specified by prefixing `(`
         | 
| 2008 | 
            -
              # @return [ | 
| 2027 | 
            +
              # @return [Integer] number of members that were removed
         | 
| 2009 2028 | 
             
              def zremrangebyscore(key, min, max)
         | 
| 2010 2029 | 
             
                synchronize do |client|
         | 
| 2011 2030 | 
             
                  client.call([:zremrangebyscore, key, min, max])
         | 
| @@ -2028,7 +2047,7 @@ class Redis | |
| 2028 2047 | 
             
              # @param [String] max
         | 
| 2029 2048 | 
             
              #   - inclusive maximum score is specified verbatim
         | 
| 2030 2049 | 
             
              #   - exclusive maximum score is specified by prefixing `(`
         | 
| 2031 | 
            -
              # @return [ | 
| 2050 | 
            +
              # @return [Integer] number of members in within the specified range
         | 
| 2032 2051 | 
             
              def zcount(key, min, max)
         | 
| 2033 2052 | 
             
                synchronize do |client|
         | 
| 2034 2053 | 
             
                  client.call([:zcount, key, min, max])
         | 
| @@ -2048,18 +2067,19 @@ class Redis | |
| 2048 2067 | 
             
              #   - `:weights => [Float, Float, ...]`: weights to associate with source
         | 
| 2049 2068 | 
             
              #   sorted sets
         | 
| 2050 2069 | 
             
              #   - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
         | 
| 2051 | 
            -
              # @return [ | 
| 2052 | 
            -
              def zinterstore(destination, keys,  | 
| 2053 | 
            -
                args = []
         | 
| 2070 | 
            +
              # @return [Integer] number of elements in the resulting sorted set
         | 
| 2071 | 
            +
              def zinterstore(destination, keys, weights: nil, aggregate: nil)
         | 
| 2072 | 
            +
                args = [:zinterstore, destination, keys.size, *keys]
         | 
| 2054 2073 |  | 
| 2055 | 
            -
                 | 
| 2056 | 
            -
             | 
| 2074 | 
            +
                if weights
         | 
| 2075 | 
            +
                  args << "WEIGHTS"
         | 
| 2076 | 
            +
                  args.concat(weights)
         | 
| 2077 | 
            +
                end
         | 
| 2057 2078 |  | 
| 2058 | 
            -
                aggregate  | 
| 2059 | 
            -
                args.concat(["AGGREGATE", aggregate]) if aggregate
         | 
| 2079 | 
            +
                args << "AGGREGATE" << aggregate if aggregate
         | 
| 2060 2080 |  | 
| 2061 2081 | 
             
                synchronize do |client|
         | 
| 2062 | 
            -
                  client.call( | 
| 2082 | 
            +
                  client.call(args)
         | 
| 2063 2083 | 
             
                end
         | 
| 2064 2084 | 
             
              end
         | 
| 2065 2085 |  | 
| @@ -2075,40 +2095,46 @@ class Redis | |
| 2075 2095 | 
             
              #   - `:weights => [Float, Float, ...]`: weights to associate with source
         | 
| 2076 2096 | 
             
              #   sorted sets
         | 
| 2077 2097 | 
             
              #   - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
         | 
| 2078 | 
            -
              # @return [ | 
| 2079 | 
            -
              def zunionstore(destination, keys,  | 
| 2080 | 
            -
                args = []
         | 
| 2098 | 
            +
              # @return [Integer] number of elements in the resulting sorted set
         | 
| 2099 | 
            +
              def zunionstore(destination, keys, weights: nil, aggregate: nil)
         | 
| 2100 | 
            +
                args = [:zunionstore, destination, keys.size, *keys]
         | 
| 2081 2101 |  | 
| 2082 | 
            -
                 | 
| 2083 | 
            -
             | 
| 2102 | 
            +
                if weights
         | 
| 2103 | 
            +
                  args << "WEIGHTS"
         | 
| 2104 | 
            +
                  args.concat(weights)
         | 
| 2105 | 
            +
                end
         | 
| 2084 2106 |  | 
| 2085 | 
            -
                aggregate  | 
| 2086 | 
            -
                args.concat(["AGGREGATE", aggregate]) if aggregate
         | 
| 2107 | 
            +
                args << "AGGREGATE" << aggregate if aggregate
         | 
| 2087 2108 |  | 
| 2088 2109 | 
             
                synchronize do |client|
         | 
| 2089 | 
            -
                  client.call( | 
| 2110 | 
            +
                  client.call(args)
         | 
| 2090 2111 | 
             
                end
         | 
| 2091 2112 | 
             
              end
         | 
| 2092 2113 |  | 
| 2093 2114 | 
             
              # Get the number of fields in a hash.
         | 
| 2094 2115 | 
             
              #
         | 
| 2095 2116 | 
             
              # @param [String] key
         | 
| 2096 | 
            -
              # @return [ | 
| 2117 | 
            +
              # @return [Integer] number of fields in the hash
         | 
| 2097 2118 | 
             
              def hlen(key)
         | 
| 2098 2119 | 
             
                synchronize do |client|
         | 
| 2099 2120 | 
             
                  client.call([:hlen, key])
         | 
| 2100 2121 | 
             
                end
         | 
| 2101 2122 | 
             
              end
         | 
| 2102 2123 |  | 
| 2103 | 
            -
              # Set  | 
| 2124 | 
            +
              # Set one or more hash values.
         | 
| 2125 | 
            +
              #
         | 
| 2126 | 
            +
              # @example
         | 
| 2127 | 
            +
              #   redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
         | 
| 2128 | 
            +
              #   redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
         | 
| 2104 2129 | 
             
              #
         | 
| 2105 2130 | 
             
              # @param [String] key
         | 
| 2106 | 
            -
              # @param [String]  | 
| 2107 | 
            -
              # @ | 
| 2108 | 
            -
               | 
| 2109 | 
            -
             | 
| 2131 | 
            +
              # @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
         | 
| 2132 | 
            +
              # @return [Integer] The number of fields that were added to the hash
         | 
| 2133 | 
            +
              def hset(key, *attrs)
         | 
| 2134 | 
            +
                attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
         | 
| 2135 | 
            +
             | 
| 2110 2136 | 
             
                synchronize do |client|
         | 
| 2111 | 
            -
                  client.call([:hset, key,  | 
| 2137 | 
            +
                  client.call([:hset, key, *attrs])
         | 
| 2112 2138 | 
             
                end
         | 
| 2113 2139 | 
             
              end
         | 
| 2114 2140 |  | 
| @@ -2197,7 +2223,7 @@ class Redis | |
| 2197 2223 | 
             
              # @see #hmget
         | 
| 2198 2224 | 
             
              def mapped_hmget(key, *fields)
         | 
| 2199 2225 | 
             
                hmget(key, *fields) do |reply|
         | 
| 2200 | 
            -
                  if reply. | 
| 2226 | 
            +
                  if reply.is_a?(Array)
         | 
| 2201 2227 | 
             
                    Hash[fields.zip(reply)]
         | 
| 2202 2228 | 
             
                  else
         | 
| 2203 2229 | 
             
                    reply
         | 
| @@ -2209,7 +2235,7 @@ class Redis | |
| 2209 2235 | 
             
              #
         | 
| 2210 2236 | 
             
              # @param [String] key
         | 
| 2211 2237 | 
             
              # @param [String, Array<String>] field
         | 
| 2212 | 
            -
              # @return [ | 
| 2238 | 
            +
              # @return [Integer] the number of fields that were removed from the hash
         | 
| 2213 2239 | 
             
              def hdel(key, *fields)
         | 
| 2214 2240 | 
             
                synchronize do |client|
         | 
| 2215 2241 | 
             
                  client.call([:hdel, key, *fields])
         | 
| @@ -2231,8 +2257,8 @@ class Redis | |
| 2231 2257 | 
             
              #
         | 
| 2232 2258 | 
             
              # @param [String] key
         | 
| 2233 2259 | 
             
              # @param [String] field
         | 
| 2234 | 
            -
              # @param [ | 
| 2235 | 
            -
              # @return [ | 
| 2260 | 
            +
              # @param [Integer] increment
         | 
| 2261 | 
            +
              # @return [Integer] value of the field after incrementing it
         | 
| 2236 2262 | 
             
              def hincrby(key, field, increment)
         | 
| 2237 2263 | 
             
                synchronize do |client|
         | 
| 2238 2264 | 
             
                  client.call([:hincrby, key, field, increment])
         | 
| @@ -2290,20 +2316,21 @@ class Redis | |
| 2290 2316 |  | 
| 2291 2317 | 
             
              def subscribed?
         | 
| 2292 2318 | 
             
                synchronize do |client|
         | 
| 2293 | 
            -
                  client. | 
| 2319 | 
            +
                  client.is_a? SubscribedClient
         | 
| 2294 2320 | 
             
                end
         | 
| 2295 2321 | 
             
              end
         | 
| 2296 2322 |  | 
| 2297 2323 | 
             
              # Listen for messages published to the given channels.
         | 
| 2298 2324 | 
             
              def subscribe(*channels, &block)
         | 
| 2299 | 
            -
                synchronize do | | 
| 2325 | 
            +
                synchronize do |_client|
         | 
| 2300 2326 | 
             
                  _subscription(:subscribe, 0, channels, block)
         | 
| 2301 2327 | 
             
                end
         | 
| 2302 2328 | 
             
              end
         | 
| 2303 2329 |  | 
| 2304 | 
            -
              # Listen for messages published to the given channels. Throw a timeout error | 
| 2330 | 
            +
              # Listen for messages published to the given channels. Throw a timeout error
         | 
| 2331 | 
            +
              # if there is no messages for a timeout period.
         | 
| 2305 2332 | 
             
              def subscribe_with_timeout(timeout, *channels, &block)
         | 
| 2306 | 
            -
                synchronize do | | 
| 2333 | 
            +
                synchronize do |_client|
         | 
| 2307 2334 | 
             
                  _subscription(:subscribe_with_timeout, timeout, channels, block)
         | 
| 2308 2335 | 
             
                end
         | 
| 2309 2336 | 
             
              end
         | 
| @@ -2311,21 +2338,23 @@ class Redis | |
| 2311 2338 | 
             
              # Stop listening for messages posted to the given channels.
         | 
| 2312 2339 | 
             
              def unsubscribe(*channels)
         | 
| 2313 2340 | 
             
                synchronize do |client|
         | 
| 2314 | 
            -
                  raise  | 
| 2341 | 
            +
                  raise "Can't unsubscribe if not subscribed." unless subscribed?
         | 
| 2342 | 
            +
             | 
| 2315 2343 | 
             
                  client.unsubscribe(*channels)
         | 
| 2316 2344 | 
             
                end
         | 
| 2317 2345 | 
             
              end
         | 
| 2318 2346 |  | 
| 2319 2347 | 
             
              # Listen for messages published to channels matching the given patterns.
         | 
| 2320 2348 | 
             
              def psubscribe(*channels, &block)
         | 
| 2321 | 
            -
                synchronize do | | 
| 2349 | 
            +
                synchronize do |_client|
         | 
| 2322 2350 | 
             
                  _subscription(:psubscribe, 0, channels, block)
         | 
| 2323 2351 | 
             
                end
         | 
| 2324 2352 | 
             
              end
         | 
| 2325 2353 |  | 
| 2326 | 
            -
              # Listen for messages published to channels matching the given patterns. | 
| 2354 | 
            +
              # Listen for messages published to channels matching the given patterns.
         | 
| 2355 | 
            +
              # Throw a timeout error if there is no messages for a timeout period.
         | 
| 2327 2356 | 
             
              def psubscribe_with_timeout(timeout, *channels, &block)
         | 
| 2328 | 
            -
                synchronize do | | 
| 2357 | 
            +
                synchronize do |_client|
         | 
| 2329 2358 | 
             
                  _subscription(:psubscribe_with_timeout, timeout, channels, block)
         | 
| 2330 2359 | 
             
                end
         | 
| 2331 2360 | 
             
              end
         | 
| @@ -2333,7 +2362,8 @@ class Redis | |
| 2333 2362 | 
             
              # Stop listening for messages posted to channels matching the given patterns.
         | 
| 2334 2363 | 
             
              def punsubscribe(*channels)
         | 
| 2335 2364 | 
             
                synchronize do |client|
         | 
| 2336 | 
            -
                  raise  | 
| 2365 | 
            +
                  raise "Can't unsubscribe if not subscribed." unless subscribed?
         | 
| 2366 | 
            +
             | 
| 2337 2367 | 
             
                  client.punsubscribe(*channels)
         | 
| 2338 2368 | 
             
                end
         | 
| 2339 2369 | 
             
              end
         | 
| @@ -2378,7 +2408,7 @@ class Redis | |
| 2378 2408 | 
             
              # @see #multi
         | 
| 2379 2409 | 
             
              def watch(*keys)
         | 
| 2380 2410 | 
             
                synchronize do |client|
         | 
| 2381 | 
            -
                  res = client.call([:watch | 
| 2411 | 
            +
                  res = client.call([:watch, *keys])
         | 
| 2382 2412 |  | 
| 2383 2413 | 
             
                  if block_given?
         | 
| 2384 2414 | 
             
                    begin
         | 
| @@ -2408,7 +2438,7 @@ class Redis | |
| 2408 2438 | 
             
              end
         | 
| 2409 2439 |  | 
| 2410 2440 | 
             
              def pipelined
         | 
| 2411 | 
            -
                synchronize do | | 
| 2441 | 
            +
                synchronize do |_client|
         | 
| 2412 2442 | 
             
                  begin
         | 
| 2413 2443 | 
             
                    pipeline = Pipeline.new(@client)
         | 
| 2414 2444 | 
             
                    original, @client = @client, pipeline
         | 
| @@ -2608,18 +2638,12 @@ class Redis | |
| 2608 2638 | 
             
                _eval(:evalsha, args)
         | 
| 2609 2639 | 
             
              end
         | 
| 2610 2640 |  | 
| 2611 | 
            -
              def _scan(command, cursor, args,  | 
| 2641 | 
            +
              def _scan(command, cursor, args, match: nil, count: nil, &block)
         | 
| 2612 2642 | 
             
                # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
         | 
| 2613 2643 |  | 
| 2614 2644 | 
             
                args << cursor
         | 
| 2615 | 
            -
             | 
| 2616 | 
            -
                 | 
| 2617 | 
            -
                  args.concat(["MATCH", match])
         | 
| 2618 | 
            -
                end
         | 
| 2619 | 
            -
             | 
| 2620 | 
            -
                if count = options[:count]
         | 
| 2621 | 
            -
                  args.concat(["COUNT", count])
         | 
| 2622 | 
            -
                end
         | 
| 2645 | 
            +
                args << "MATCH" << match if match
         | 
| 2646 | 
            +
                args << "COUNT" << count if count
         | 
| 2623 2647 |  | 
| 2624 2648 | 
             
                synchronize do |client|
         | 
| 2625 2649 | 
             
                  client.call([command] + args, &block)
         | 
| @@ -2641,8 +2665,8 @@ class Redis | |
| 2641 2665 | 
             
              #   - `:count => Integer`: return count keys at most per iteration
         | 
| 2642 2666 | 
             
              #
         | 
| 2643 2667 | 
             
              # @return [String, Array<String>] the next cursor and all found keys
         | 
| 2644 | 
            -
              def scan(cursor, options | 
| 2645 | 
            -
                _scan(:scan, cursor, [], options)
         | 
| 2668 | 
            +
              def scan(cursor, **options)
         | 
| 2669 | 
            +
                _scan(:scan, cursor, [], **options)
         | 
| 2646 2670 | 
             
              end
         | 
| 2647 2671 |  | 
| 2648 2672 | 
             
              # Scan the keyspace
         | 
| @@ -2660,11 +2684,12 @@ class Redis | |
| 2660 2684 | 
             
              #   - `:count => Integer`: return count keys at most per iteration
         | 
| 2661 2685 | 
             
              #
         | 
| 2662 2686 | 
             
              # @return [Enumerator] an enumerator for all found keys
         | 
| 2663 | 
            -
              def scan_each(options | 
| 2664 | 
            -
                return to_enum(:scan_each, options) unless block_given?
         | 
| 2687 | 
            +
              def scan_each(**options, &block)
         | 
| 2688 | 
            +
                return to_enum(:scan_each, **options) unless block_given?
         | 
| 2689 | 
            +
             | 
| 2665 2690 | 
             
                cursor = 0
         | 
| 2666 2691 | 
             
                loop do
         | 
| 2667 | 
            -
                  cursor, keys = scan(cursor, options)
         | 
| 2692 | 
            +
                  cursor, keys = scan(cursor, **options)
         | 
| 2668 2693 | 
             
                  keys.each(&block)
         | 
| 2669 2694 | 
             
                  break if cursor == "0"
         | 
| 2670 2695 | 
             
                end
         | 
| @@ -2681,8 +2706,8 @@ class Redis | |
| 2681 2706 | 
             
              #   - `:count => Integer`: return count keys at most per iteration
         | 
| 2682 2707 | 
             
              #
         | 
| 2683 2708 | 
             
              # @return [String, Array<[String, String]>] the next cursor and all found keys
         | 
| 2684 | 
            -
              def hscan(key, cursor, options | 
| 2685 | 
            -
                _scan(:hscan, cursor, [key], options) do |reply|
         | 
| 2709 | 
            +
              def hscan(key, cursor, **options)
         | 
| 2710 | 
            +
                _scan(:hscan, cursor, [key], **options) do |reply|
         | 
| 2686 2711 | 
             
                  [reply[0], reply[1].each_slice(2).to_a]
         | 
| 2687 2712 | 
             
                end
         | 
| 2688 2713 | 
             
              end
         | 
| @@ -2698,11 +2723,12 @@ class Redis | |
| 2698 2723 | 
             
              #   - `:count => Integer`: return count keys at most per iteration
         | 
| 2699 2724 | 
             
              #
         | 
| 2700 2725 | 
             
              # @return [Enumerator] an enumerator for all found keys
         | 
| 2701 | 
            -
              def hscan_each(key, options | 
| 2702 | 
            -
                return to_enum(:hscan_each, key, options) unless block_given?
         | 
| 2726 | 
            +
              def hscan_each(key, **options, &block)
         | 
| 2727 | 
            +
                return to_enum(:hscan_each, key, **options) unless block_given?
         | 
| 2728 | 
            +
             | 
| 2703 2729 | 
             
                cursor = 0
         | 
| 2704 2730 | 
             
                loop do
         | 
| 2705 | 
            -
                  cursor, values = hscan(key, cursor, options)
         | 
| 2731 | 
            +
                  cursor, values = hscan(key, cursor, **options)
         | 
| 2706 2732 | 
             
                  values.each(&block)
         | 
| 2707 2733 | 
             
                  break if cursor == "0"
         | 
| 2708 2734 | 
             
                end
         | 
| @@ -2720,8 +2746,8 @@ class Redis | |
| 2720 2746 | 
             
              #
         | 
| 2721 2747 | 
             
              # @return [String, Array<[String, Float]>] the next cursor and all found
         | 
| 2722 2748 | 
             
              #   members and scores
         | 
| 2723 | 
            -
              def zscan(key, cursor, options | 
| 2724 | 
            -
                _scan(:zscan, cursor, [key], options) do |reply|
         | 
| 2749 | 
            +
              def zscan(key, cursor, **options)
         | 
| 2750 | 
            +
                _scan(:zscan, cursor, [key], **options) do |reply|
         | 
| 2725 2751 | 
             
                  [reply[0], FloatifyPairs.call(reply[1])]
         | 
| 2726 2752 | 
             
                end
         | 
| 2727 2753 | 
             
              end
         | 
| @@ -2737,11 +2763,12 @@ class Redis | |
| 2737 2763 | 
             
              #   - `:count => Integer`: return count keys at most per iteration
         | 
| 2738 2764 | 
             
              #
         | 
| 2739 2765 | 
             
              # @return [Enumerator] an enumerator for all found scores and members
         | 
| 2740 | 
            -
              def zscan_each(key, options | 
| 2741 | 
            -
                return to_enum(:zscan_each, key, options) unless block_given?
         | 
| 2766 | 
            +
              def zscan_each(key, **options, &block)
         | 
| 2767 | 
            +
                return to_enum(:zscan_each, key, **options) unless block_given?
         | 
| 2768 | 
            +
             | 
| 2742 2769 | 
             
                cursor = 0
         | 
| 2743 2770 | 
             
                loop do
         | 
| 2744 | 
            -
                  cursor, values = zscan(key, cursor, options)
         | 
| 2771 | 
            +
                  cursor, values = zscan(key, cursor, **options)
         | 
| 2745 2772 | 
             
                  values.each(&block)
         | 
| 2746 2773 | 
             
                  break if cursor == "0"
         | 
| 2747 2774 | 
             
                end
         | 
| @@ -2758,8 +2785,8 @@ class Redis | |
| 2758 2785 | 
             
              #   - `:count => Integer`: return count keys at most per iteration
         | 
| 2759 2786 | 
             
              #
         | 
| 2760 2787 | 
             
              # @return [String, Array<String>] the next cursor and all found members
         | 
| 2761 | 
            -
              def sscan(key, cursor, options | 
| 2762 | 
            -
                _scan(:sscan, cursor, [key], options)
         | 
| 2788 | 
            +
              def sscan(key, cursor, **options)
         | 
| 2789 | 
            +
                _scan(:sscan, cursor, [key], **options)
         | 
| 2763 2790 | 
             
              end
         | 
| 2764 2791 |  | 
| 2765 2792 | 
             
              # Scan a set
         | 
| @@ -2773,11 +2800,12 @@ class Redis | |
| 2773 2800 | 
             
              #   - `:count => Integer`: return count keys at most per iteration
         | 
| 2774 2801 | 
             
              #
         | 
| 2775 2802 | 
             
              # @return [Enumerator] an enumerator for all keys in the set
         | 
| 2776 | 
            -
              def sscan_each(key, options | 
| 2777 | 
            -
                return to_enum(:sscan_each, key, options) unless block_given?
         | 
| 2803 | 
            +
              def sscan_each(key, **options, &block)
         | 
| 2804 | 
            +
                return to_enum(:sscan_each, key, **options) unless block_given?
         | 
| 2805 | 
            +
             | 
| 2778 2806 | 
             
                cursor = 0
         | 
| 2779 2807 | 
             
                loop do
         | 
| 2780 | 
            -
                  cursor, keys = sscan(key, cursor, options)
         | 
| 2808 | 
            +
                  cursor, keys = sscan(key, cursor, **options)
         | 
| 2781 2809 | 
             
                  keys.each(&block)
         | 
| 2782 2810 | 
             
                  break if cursor == "0"
         | 
| 2783 2811 | 
             
                end
         | 
| @@ -2800,7 +2828,7 @@ class Redis | |
| 2800 2828 | 
             
              # union of the HyperLogLogs contained in the keys.
         | 
| 2801 2829 | 
             
              #
         | 
| 2802 2830 | 
             
              # @param [String, Array<String>] keys
         | 
| 2803 | 
            -
              # @return [ | 
| 2831 | 
            +
              # @return [Integer]
         | 
| 2804 2832 | 
             
              def pfcount(*keys)
         | 
| 2805 2833 | 
             
                synchronize do |client|
         | 
| 2806 2834 | 
             
                  client.call([:pfcount] + keys)
         | 
| @@ -2841,12 +2869,12 @@ class Redis | |
| 2841 2869 | 
             
                end
         | 
| 2842 2870 | 
             
              end
         | 
| 2843 2871 |  | 
| 2844 | 
            -
             | 
| 2845 2872 | 
             
              # Query a sorted set representing a geospatial index to fetch members matching a
         | 
| 2846 2873 | 
             
              # given maximum distance from a point
         | 
| 2847 2874 | 
             
              #
         | 
| 2848 2875 | 
             
              # @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
         | 
| 2849 | 
            -
              # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest | 
| 2876 | 
            +
              # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
         | 
| 2877 | 
            +
              #   or the farthest to the nearest relative to the center
         | 
| 2850 2878 | 
             
              # @param [Integer] count limit the results to the first N matching items
         | 
| 2851 2879 | 
             
              # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
         | 
| 2852 2880 | 
             
              # @return [Array<String>] may be changed with `options`
         | 
| @@ -2863,7 +2891,8 @@ class Redis | |
| 2863 2891 | 
             
              # given maximum distance from an already existing member
         | 
| 2864 2892 | 
             
              #
         | 
| 2865 2893 | 
             
              # @param [Array] args key, member, radius, unit(m|km|ft|mi)
         | 
| 2866 | 
            -
              # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest | 
| 2894 | 
            +
              # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
         | 
| 2895 | 
            +
              #   to the nearest relative to the center
         | 
| 2867 2896 | 
             
              # @param [Integer] count limit the results to the first N matching items
         | 
| 2868 2897 | 
             
              # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
         | 
| 2869 2898 | 
             
              # @return [Array<String>] may be changed with `options`
         | 
| @@ -2880,7 +2909,8 @@ class Redis | |
| 2880 2909 | 
             
              #
         | 
| 2881 2910 | 
             
              # @param [String] key
         | 
| 2882 2911 | 
             
              # @param [String, Array<String>] member one member or array of members
         | 
| 2883 | 
            -
              # @return [Array<Array<String>, nil>] returns array of elements, where each | 
| 2912 | 
            +
              # @return [Array<Array<String>, nil>] returns array of elements, where each
         | 
| 2913 | 
            +
              #   element is either array of longitude and latitude or nil
         | 
| 2884 2914 | 
             
              def geopos(key, member)
         | 
| 2885 2915 | 
             
                synchronize do |client|
         | 
| 2886 2916 | 
             
                  client.call([:geopos, key, member])
         | 
| @@ -2944,10 +2974,14 @@ class Redis | |
| 2944 2974 | 
             
              # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
         | 
| 2945 2975 | 
             
              #
         | 
| 2946 2976 | 
             
              # @return [String] the entry id
         | 
| 2947 | 
            -
              def xadd(key, entry,  | 
| 2977 | 
            +
              def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
         | 
| 2948 2978 | 
             
                args = [:xadd, key]
         | 
| 2949 | 
            -
                 | 
| 2950 | 
            -
             | 
| 2979 | 
            +
                if maxlen
         | 
| 2980 | 
            +
                  args << "MAXLEN"
         | 
| 2981 | 
            +
                  args << "~" if approximate
         | 
| 2982 | 
            +
                  args << maxlen
         | 
| 2983 | 
            +
                end
         | 
| 2984 | 
            +
                args << id
         | 
| 2951 2985 | 
             
                args.concat(entry.to_a.flatten)
         | 
| 2952 2986 | 
             
                synchronize { |client| client.call(args) }
         | 
| 2953 2987 | 
             
              end
         | 
| @@ -3002,8 +3036,8 @@ class Redis | |
| 3002 3036 | 
             
              # @param count [Integer] the number of entries as limit
         | 
| 3003 3037 | 
             
              #
         | 
| 3004 3038 | 
             
              # @return [Array<Array<String, Hash>>] the ids and entries pairs
         | 
| 3005 | 
            -
              def xrange(key, start = '-',  | 
| 3006 | 
            -
                args = [:xrange, key, start,  | 
| 3039 | 
            +
              def xrange(key, start = '-', range_end = '+', count: nil)
         | 
| 3040 | 
            +
                args = [:xrange, key, start, range_end]
         | 
| 3007 3041 | 
             
                args.concat(['COUNT', count]) if count
         | 
| 3008 3042 | 
             
                synchronize { |client| client.call(args, &HashifyStreamEntries) }
         | 
| 3009 3043 | 
             
              end
         | 
| @@ -3025,8 +3059,8 @@ class Redis | |
| 3025 3059 | 
             
              # @params count [Integer] the number of entries as limit
         | 
| 3026 3060 | 
             
              #
         | 
| 3027 3061 | 
             
              # @return [Array<Array<String, Hash>>] the ids and entries pairs
         | 
| 3028 | 
            -
              def xrevrange(key,  | 
| 3029 | 
            -
                args = [:xrevrange, key,  | 
| 3062 | 
            +
              def xrevrange(key, range_end = '+', start = '-', count: nil)
         | 
| 3063 | 
            +
                args = [:xrevrange, key, range_end, start]
         | 
| 3030 3064 | 
             
                args.concat(['COUNT', count]) if count
         | 
| 3031 3065 | 
             
                synchronize { |client| client.call(args, &HashifyStreamEntries) }
         | 
| 3032 3066 | 
             
              end
         | 
| @@ -3118,12 +3152,12 @@ class Redis | |
| 3118 3152 | 
             
              # @option opts [Boolean] :noack whether message loss is acceptable or not
         | 
| 3119 3153 | 
             
              #
         | 
| 3120 3154 | 
             
              # @return [Hash{String => Hash{String => Hash}}] the entries
         | 
| 3121 | 
            -
              def xreadgroup(group, consumer, keys, ids,  | 
| 3155 | 
            +
              def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
         | 
| 3122 3156 | 
             
                args = [:xreadgroup, 'GROUP', group, consumer]
         | 
| 3123 | 
            -
                args << 'COUNT' <<  | 
| 3124 | 
            -
                args << 'BLOCK' <<  | 
| 3125 | 
            -
                args << 'NOACK' if  | 
| 3126 | 
            -
                _xread(args, keys, ids,  | 
| 3157 | 
            +
                args << 'COUNT' << count if count
         | 
| 3158 | 
            +
                args << 'BLOCK' << block.to_i if block
         | 
| 3159 | 
            +
                args << 'NOACK' if noack
         | 
| 3160 | 
            +
                _xread(args, keys, ids, block)
         | 
| 3127 3161 | 
             
              end
         | 
| 3128 3162 |  | 
| 3129 3163 | 
             
              # Removes one or multiple entries from the pending entries list of a stream consumer group.
         | 
| @@ -3233,8 +3267,8 @@ class Redis | |
| 3233 3267 | 
             
                    when "get-master-addr-by-name"
         | 
| 3234 3268 | 
             
                      reply
         | 
| 3235 3269 | 
             
                    else
         | 
| 3236 | 
            -
                      if reply. | 
| 3237 | 
            -
                        if reply[0]. | 
| 3270 | 
            +
                      if reply.is_a?(Array)
         | 
| 3271 | 
            +
                        if reply[0].is_a?(Array)
         | 
| 3238 3272 | 
             
                          reply.map(&Hashify)
         | 
| 3239 3273 | 
             
                        else
         | 
| 3240 3274 | 
             
                          Hashify.call(reply)
         | 
| @@ -3258,12 +3292,17 @@ class Redis | |
| 3258 3292 | 
             
              def cluster(subcommand, *args)
         | 
| 3259 3293 | 
             
                subcommand = subcommand.to_s.downcase
         | 
| 3260 3294 | 
             
                block = case subcommand
         | 
| 3261 | 
            -
             | 
| 3262 | 
            -
             | 
| 3263 | 
            -
             | 
| 3264 | 
            -
             | 
| 3265 | 
            -
             | 
| 3266 | 
            -
             | 
| 3295 | 
            +
                when 'slots'
         | 
| 3296 | 
            +
                  HashifyClusterSlots
         | 
| 3297 | 
            +
                when 'nodes'
         | 
| 3298 | 
            +
                  HashifyClusterNodes
         | 
| 3299 | 
            +
                when 'slaves'
         | 
| 3300 | 
            +
                  HashifyClusterSlaves
         | 
| 3301 | 
            +
                when 'info'
         | 
| 3302 | 
            +
                  HashifyInfo
         | 
| 3303 | 
            +
                else
         | 
| 3304 | 
            +
                  Noop
         | 
| 3305 | 
            +
                end
         | 
| 3267 3306 |  | 
| 3268 3307 | 
             
                # @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
         | 
| 3269 3308 | 
             
                block = Noop unless @cluster_mode
         | 
| @@ -3298,21 +3337,21 @@ class Redis | |
| 3298 3337 | 
             
                return @original_client.connection_info if @cluster_mode
         | 
| 3299 3338 |  | 
| 3300 3339 | 
             
                {
         | 
| 3301 | 
            -
                  host: | 
| 3302 | 
            -
                  port: | 
| 3303 | 
            -
                  db: | 
| 3304 | 
            -
                  id: | 
| 3340 | 
            +
                  host: @original_client.host,
         | 
| 3341 | 
            +
                  port: @original_client.port,
         | 
| 3342 | 
            +
                  db: @original_client.db,
         | 
| 3343 | 
            +
                  id: @original_client.id,
         | 
| 3305 3344 | 
             
                  location: @original_client.location
         | 
| 3306 3345 | 
             
                }
         | 
| 3307 3346 | 
             
              end
         | 
| 3308 3347 |  | 
| 3309 | 
            -
              def method_missing(command, *args)
         | 
| 3348 | 
            +
              def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
         | 
| 3310 3349 | 
             
                synchronize do |client|
         | 
| 3311 3350 | 
             
                  client.call([command] + args)
         | 
| 3312 3351 | 
             
                end
         | 
| 3313 3352 | 
             
              end
         | 
| 3314 3353 |  | 
| 3315 | 
            -
            private
         | 
| 3354 | 
            +
              private
         | 
| 3316 3355 |  | 
| 3317 3356 | 
             
              # Commands returning 1 for true and 0 for false may be executed in a pipeline
         | 
| 3318 3357 | 
             
              # where the method call will return nil. Propagate the nil instead of falsely
         | 
| @@ -3384,18 +3423,21 @@ private | |
| 3384 3423 | 
             
                end
         | 
| 3385 3424 | 
             
              }
         | 
| 3386 3425 |  | 
| 3426 | 
            +
              EMPTY_STREAM_RESPONSE = [nil].freeze
         | 
| 3427 | 
            +
              private_constant :EMPTY_STREAM_RESPONSE
         | 
| 3428 | 
            +
             | 
| 3387 3429 | 
             
              HashifyStreamEntries = lambda { |reply|
         | 
| 3388 | 
            -
                reply.map do |entry_id, values|
         | 
| 3430 | 
            +
                reply.compact.map do |entry_id, values|
         | 
| 3389 3431 | 
             
                  [entry_id, values.each_slice(2).to_h]
         | 
| 3390 3432 | 
             
                end
         | 
| 3391 3433 | 
             
              }
         | 
| 3392 3434 |  | 
| 3393 3435 | 
             
              HashifyStreamPendings = lambda { |reply|
         | 
| 3394 3436 | 
             
                {
         | 
| 3395 | 
            -
                  'size' | 
| 3437 | 
            +
                  'size' => reply[0],
         | 
| 3396 3438 | 
             
                  'min_entry_id' => reply[1],
         | 
| 3397 3439 | 
             
                  'max_entry_id' => reply[2],
         | 
| 3398 | 
            -
                  'consumers' | 
| 3440 | 
            +
                  'consumers' => reply[3].nil? ? {} : reply[3].to_h
         | 
| 3399 3441 | 
             
                }
         | 
| 3400 3442 | 
             
              }
         | 
| 3401 3443 |  | 
| @@ -3404,8 +3446,8 @@ private | |
| 3404 3446 | 
             
                  {
         | 
| 3405 3447 | 
             
                    'entry_id' => arr[0],
         | 
| 3406 3448 | 
             
                    'consumer' => arr[1],
         | 
| 3407 | 
            -
                    'elapsed' | 
| 3408 | 
            -
                    'count' | 
| 3449 | 
            +
                    'elapsed' => arr[2],
         | 
| 3450 | 
            +
                    'count' => arr[3]
         | 
| 3409 3451 | 
             
                  }
         | 
| 3410 3452 | 
             
                end
         | 
| 3411 3453 | 
             
              }
         | 
| @@ -3413,15 +3455,15 @@ private | |
| 3413 3455 | 
             
              HashifyClusterNodeInfo = lambda { |str|
         | 
| 3414 3456 | 
             
                arr = str.split(' ')
         | 
| 3415 3457 | 
             
                {
         | 
| 3416 | 
            -
                  'node_id' | 
| 3417 | 
            -
                  'ip_port' | 
| 3418 | 
            -
                  'flags' | 
| 3458 | 
            +
                  'node_id' => arr[0],
         | 
| 3459 | 
            +
                  'ip_port' => arr[1],
         | 
| 3460 | 
            +
                  'flags' => arr[2].split(','),
         | 
| 3419 3461 | 
             
                  'master_node_id' => arr[3],
         | 
| 3420 | 
            -
                  'ping_sent' | 
| 3421 | 
            -
                  'pong_recv' | 
| 3422 | 
            -
                  'config_epoch' | 
| 3423 | 
            -
                  'link_state' | 
| 3424 | 
            -
                  'slots' | 
| 3462 | 
            +
                  'ping_sent' => arr[4],
         | 
| 3463 | 
            +
                  'pong_recv' => arr[5],
         | 
| 3464 | 
            +
                  'config_epoch' => arr[6],
         | 
| 3465 | 
            +
                  'link_state' => arr[7],
         | 
| 3466 | 
            +
                  'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
         | 
| 3425 3467 | 
             
                }
         | 
| 3426 3468 | 
             
              }
         | 
| 3427 3469 |  | 
| @@ -3432,9 +3474,9 @@ private | |
| 3432 3474 | 
             
                  replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
         | 
| 3433 3475 | 
             
                  {
         | 
| 3434 3476 | 
             
                    'start_slot' => first_slot,
         | 
| 3435 | 
            -
                    'end_slot' | 
| 3436 | 
            -
                    'master' | 
| 3437 | 
            -
                    'replicas' | 
| 3477 | 
            +
                    'end_slot' => last_slot,
         | 
| 3478 | 
            +
                    'master' => master,
         | 
| 3479 | 
            +
                    'replicas' => replicas
         | 
| 3438 3480 | 
             
                  }
         | 
| 3439 3481 | 
             
                end
         | 
| 3440 3482 | 
             
              }
         |