From 325752764995b02f17c3e5240ea489f641911d7d Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 9 Jun 2020 11:04:37 +0200 Subject: [PATCH] Make `exists` variadic. `exists` now return an Integer if passed multiple keys. If passed a single key it will still return a Boolean, but print a deprecation warning. To get a Boolean use `exists?` instead. To opt-in to the new behavior, set `Redis.exists_returns_integer = true`. --- lib/redis.rb | 31 ++++++++++++++++++++++++++----- lib/redis/distributed.rb | 20 ++++++++++++++++++-- test/lint/value_types.rb | 24 ++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/lib/redis.rb b/lib/redis.rb index 1a3734c34..6ba0829f1 100644 --- a/lib/redis.rb +++ b/lib/redis.rb @@ -4,6 +4,10 @@ require_relative "redis/errors" class Redis + class << self + attr_accessor :exists_returns_integer + end + def self.current @current ||= Redis.new end @@ -550,13 +554,30 @@ def unlink(*keys) end end - # Determine if a key exists. + # Determine how many of the keys exists. # - # @param [String] key - # @return [Boolean] - def exists(key) + # @param [String, Array] keys + # @return [Integer] + def exists(*keys) + if !Redis.exists_returns_integer && keys.size == 1 + message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \ + "use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \ + "(#{::Kernel.caller(1, 1).first})\n" + + if defined?(::Warning) + ::Warning.warn(message) + else + $stderr.puts(message) + end + exists?(*keys) + else + _exists(*keys) + end + end + + def _exists(*keys) synchronize do |client| - client.call([:exists, key], &Boolify) + client.call([:exists, *keys]) end end diff --git a/lib/redis/distributed.rb b/lib/redis/distributed.rb index ab1b1acd9..9ede57a72 100644 --- a/lib/redis/distributed.rb +++ b/lib/redis/distributed.rb @@ -171,8 +171,24 @@ def unlink(*args) end # Determine if a key exists. - def exists(key) - node_for(key).exists(key) + def exists(*args) + if !Redis.exists_returns_integer && args.size == 1 + message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \ + "use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \ + "(#{::Kernel.caller(1, 1).first})\n" + + if defined?(::Warning) + ::Warning.warn(message) + else + $stderr.puts(message) + end + exists?(*args) + else + keys_per_node = args.group_by { |key| node_for(key) } + keys_per_node.inject(0) do |sum, (node, keys)| + sum + node._exists(*keys) + end + end end # Determine if any of the keys exists. diff --git a/test/lint/value_types.rb b/test/lint/value_types.rb index 2c1b0054e..295ee7d2d 100644 --- a/test/lint/value_types.rb +++ b/test/lint/value_types.rb @@ -11,6 +11,30 @@ def test_exists assert_equal true, r.exists("foo") end + def test_exists_integer + previous_exists_returns_integer = Redis.exists_returns_integer + Redis.exists_returns_integer = true + assert_equal 0, r.exists("foo") + + r.set("foo", "s1") + + assert_equal 1, r.exists("foo") + ensure + Redis.exists_returns_integer = previous_exists_returns_integer + end + + def test_variadic_exists + assert_equal 0, r.exists("{1}foo", "{1}bar") + + r.set("{1}foo", "s1") + + assert_equal 1, r.exists("{1}foo", "{1}bar") + + r.set("{1}bar", "s2") + + assert_equal 2, r.exists("{1}foo", "{1}bar") + end + def test_exists? assert_equal false, r.exists?("{1}foo", "{1}bar")