lib/redis.rb in redis-4.4.0 vs lib/redis.rb in redis-4.5.0
- old
+ new
@@ -2,10 +2,12 @@
require "monitor"
require_relative "redis/errors"
class Redis
+ @exists_returns_integer = true
+
class << self
attr_reader :exists_returns_integer
def exists_returns_integer=(value)
unless value
@@ -832,21 +834,27 @@
# @param [String] key
# @param [String] value
# @param [Hash] options
# - `:ex => Integer`: Set the specified expire time, in seconds.
# - `:px => Integer`: Set the specified expire time, in milliseconds.
+ # - `:exat => Integer` : Set the specified Unix time at which the key will expire, in seconds.
+ # - `:pxat => Integer` : Set the specified Unix time at which the key will expire, in milliseconds.
# - `:nx => true`: Only set the key if it does not already exist.
# - `:xx => true`: Only set the key if it already exist.
# - `:keepttl => true`: Retain the time to live associated with the key.
+ # - `:get => true`: Return the old string stored at key, or nil if key did not exist.
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
- def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
+ def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
args = [:set, key, value.to_s]
args << "EX" << ex if ex
args << "PX" << px if px
+ args << "EXAT" << exat if exat
+ args << "PXAT" << pxat if pxat
args << "NX" if nx
args << "XX" if xx
args << "KEEPTTL" if keepttl
+ args << "GET" if get
synchronize do |client|
if nx || xx
client.call(args, &BoolifySet)
else
@@ -1108,10 +1116,49 @@
synchronize do |client|
client.call([:getset, key, value.to_s])
end
end
+ # Get the value of key and delete the key. This command is similar to GET,
+ # except for the fact that it also deletes the key on success.
+ #
+ # @param [String] key
+ # @return [String] the old value stored in the key, or `nil` if the key
+ # did not exist
+ def getdel(key)
+ synchronize do |client|
+ client.call([:getdel, key])
+ end
+ end
+
+ # Get the value of key and optionally set its expiration. GETEX is similar to
+ # GET, but is a write command with additional options. When no options are
+ # provided, GETEX behaves like GET.
+ #
+ # @param [String] key
+ # @param [Hash] options
+ # - `:ex => Integer`: Set the specified expire time, in seconds.
+ # - `:px => Integer`: Set the specified expire time, in milliseconds.
+ # - `:exat => true`: Set the specified Unix time at which the key will
+ # expire, in seconds.
+ # - `:pxat => true`: Set the specified Unix time at which the key will
+ # expire, in milliseconds.
+ # - `:persist => true`: Remove the time to live associated with the key.
+ # @return [String] The value of key, or nil when key does not exist.
+ def getex(key, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
+ args = [:getex, key]
+ args << "EX" << ex if ex
+ args << "PX" << px if px
+ args << "EXAT" << exat if exat
+ args << "PXAT" << pxat if pxat
+ args << "PERSIST" if persist
+
+ synchronize do |client|
+ client.call(args)
+ end
+ end
+
# Get the length of the value stored in a key.
#
# @param [String] key
# @return [Integer] the length of the value stored in the key, or 0
# if the key does not exist
@@ -1129,10 +1176,63 @@
synchronize do |client|
client.call([:llen, key])
end
end
+ # Remove the first/last element in a list, append/prepend it to another list and return it.
+ #
+ # @param [String] source source key
+ # @param [String] destination destination key
+ # @param [String, Symbol] where_source from where to remove the element from the source list
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
+ # @param [String, Symbol] where_destination where to push the element to the source list
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
+ #
+ # @return [nil, String] the element, or nil when the source key does not exist
+ #
+ # @note This command comes in place of the now deprecated RPOPLPUSH.
+ # Doing LMOVE RIGHT LEFT is equivalent.
+ def lmove(source, destination, where_source, where_destination)
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
+
+ synchronize do |client|
+ client.call([:lmove, source, destination, where_source, where_destination])
+ end
+ end
+
+ # Remove the first/last element in a list and append/prepend it
+ # to another list and return it, or block until one is available.
+ #
+ # @example With timeout
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
+ # # => nil on timeout
+ # # => "element" on success
+ # @example Without timeout
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT")
+ # # => "element"
+ #
+ # @param [String] source source key
+ # @param [String] destination destination key
+ # @param [String, Symbol] where_source from where to remove the element from the source list
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
+ # @param [String, Symbol] where_destination where to push the element to the source list
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
+ # @param [Hash] options
+ # - `:timeout => Numeric`: timeout in seconds, defaults to no timeout
+ #
+ # @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
+ #
+ def blmove(source, destination, where_source, where_destination, timeout: 0)
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
+
+ synchronize do |client|
+ command = [:blmove, source, destination, where_source, where_destination, timeout]
+ timeout += client.timeout if timeout > 0
+ client.call_with_timeout(command, timeout)
+ end
+ end
+
# Prepend one or more values to a list, creating the list if it doesn't exist
#
# @param [String] key
# @param [String, Array<String>] value string value, or array of string values to push
# @return [Integer] the length of the list after the push operation
@@ -1477,10 +1577,23 @@
synchronize do |client|
client.call([:sismember, key, member], &Boolify)
end
end
+ # Determine if multiple values are members of a set.
+ #
+ # @param [String] key
+ # @param [String, Array<String>] members
+ # @return [Array<Boolean>]
+ def smismember(key, *members)
+ synchronize do |client|
+ client.call([:smismember, key, *members]) do |reply|
+ reply.map(&Boolify)
+ end
+ end
+ end
+
# Get all the members in a set.
#
# @param [String] key
# @return [Array<String>]
def smembers(key)
@@ -1581,10 +1694,14 @@
# @param [Hash] options
# - `:xx => true`: Only update elements that already exist (never
# add elements)
# - `:nx => true`: Don't update already existing elements (always
# add new elements)
+ # - `:lt => true`: Only update existing elements if the new score
+ # is less than the current score
+ # - `:gt => true`: Only update existing elements if the new score
+ # is greater than the current score
# - `:ch => true`: Modify the return value from the number of new
# elements added, to the total number of elements changed (CH is an
# abbreviation of changed); changed elements are new elements added
# and elements already existing for which the score was updated
# - `:incr => true`: When this option is specified ZADD acts like
@@ -1595,14 +1712,16 @@
# **added** to the sorted set.
# - `Integer` when an array of pairs is specified, holding the number of
# pairs that were **added** to the sorted set.
# - `Float` when option :incr is specified, holding the score of the member
# after incrementing it.
- def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
+ def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
command = [:zadd, key]
command << "NX" if nx
command << "XX" if xx
+ command << "LT" if lt
+ command << "GT" if gt
command << "CH" if ch
command << "INCR" if incr
synchronize do |client|
if args.size == 1 && args[0].is_a?(Array)
@@ -1761,10 +1880,67 @@
synchronize do |client|
client.call([:zscore, key, member], &Floatify)
end
end
+ # Get the scores associated with the given members in a sorted set.
+ #
+ # @example Get the scores for members "a" and "b"
+ # redis.zmscore("zset", "a", "b")
+ # # => [32.0, 48.0]
+ #
+ # @param [String] key
+ # @param [String, Array<String>] members
+ # @return [Array<Float>] scores of the members
+ def zmscore(key, *members)
+ synchronize do |client|
+ client.call([:zmscore, key, *members]) do |reply|
+ reply.map(&Floatify)
+ end
+ end
+ end
+
+ # Get one or more random members from a sorted set.
+ #
+ # @example Get one random member
+ # redis.zrandmember("zset")
+ # # => "a"
+ # @example Get multiple random members
+ # redis.zrandmember("zset", 2)
+ # # => ["a", "b"]
+ # @example Gem multiple random members with scores
+ # redis.zrandmember("zset", 2, with_scores: true)
+ # # => [["a", 2.0], ["b", 3.0]]
+ #
+ # @param [String] key
+ # @param [Integer] count
+ # @param [Hash] options
+ # - `:with_scores => true`: include scores in output
+ #
+ # @return [nil, String, Array<String>, Array<[String, Float]>]
+ # - when `key` does not exist or set is empty, `nil`
+ # - when `count` is not specified, a member
+ # - when `count` is specified and `:with_scores` is not specified, an array of members
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
+ def zrandmember(key, count = nil, withscores: false, with_scores: withscores)
+ if with_scores && count.nil?
+ raise ArgumentError, "count argument must be specified"
+ end
+
+ args = [:zrandmember, key]
+ args << count if count
+
+ if with_scores
+ args << "WITHSCORES"
+ block = FloatifyPairs
+ end
+
+ synchronize do |client|
+ client.call(args, &block)
+ end
+ end
+
# Return a range of members in a sorted set, by index.
#
# @example Retrieve all members from a sorted set
# redis.zrange("zset", 0, -1)
# # => ["a", "b"]
@@ -3516,11 +3692,11 @@
EMPTY_STREAM_RESPONSE = [nil].freeze
private_constant :EMPTY_STREAM_RESPONSE
HashifyStreamEntries = lambda { |reply|
reply.compact.map do |entry_id, values|
- [entry_id, values.each_slice(2).to_h]
+ [entry_id, values&.each_slice(2)&.to_h]
end
}
HashifyStreamAutoclaim = lambda { |reply|
{
@@ -3632,9 +3808,24 @@
else
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
client.call_with_timeout(args, timeout, &HashifyStreams)
end
end
+ end
+
+ def _normalize_move_wheres(where_source, where_destination)
+ where_source = where_source.to_s.upcase
+ where_destination = where_destination.to_s.upcase
+
+ if where_source != "LEFT" && where_source != "RIGHT"
+ raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
+ end
+
+ if where_destination != "LEFT" && where_destination != "RIGHT"
+ raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
+ end
+
+ [where_source, where_destination]
end
end
require_relative "redis/version"
require_relative "redis/connection"