Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Redis-rb v5 #314

Merged
merged 1 commit into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/mock_redis.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'set'

require 'mock_redis/assertions'
require 'mock_redis/error'
require 'mock_redis/database'
require 'mock_redis/expire_wrapper'
require 'mock_redis/future'
Expand Down
22 changes: 19 additions & 3 deletions lib/mock_redis/assertions.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
require 'mock_redis/error'

class MockRedis
DUMP_TYPES = RedisClient::RESP3::DUMP_TYPES

module Assertions
private

def assert_has_args(args, command)
unless args.any?
raise Redis::CommandError,
"ERR wrong number of arguments for '#{command}' command"
if args.empty?
raise Error.command_error(
"ERR wrong number of arguments for '#{command}' command",
self
)
end
end

def assert_type(*args)
args.each do |arg|
DUMP_TYPES.fetch(arg.class) do |unexpected_class|
unless DUMP_TYPES.keys.find { |t| t > unexpected_class }
raise TypeError, "Unsupported command argument type: #{unexpected_class}"
end
end
end
end
end
Expand Down
39 changes: 17 additions & 22 deletions lib/mock_redis/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,32 +95,33 @@ def echo(msg)
end

def expire(key, seconds, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
assert_valid_integer(seconds)
seconds = Integer(seconds)
Copy link
Contributor Author

@hieuk09 hieuk09 Nov 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes mimic how redis-rb handles the number conversion


pexpire(key, seconds.to_i * 1000, nx: nx, xx: xx, lt: lt, gt: gt)
end

def pexpire(key, ms, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
assert_valid_integer(ms)
ms = Integer(ms)

now, miliseconds = @base.now
now_ms = (now * 1000) + miliseconds
pexpireat(key, now_ms + ms.to_i, nx: nx, xx: xx, lt: lt, gt: gt)
end

def expireat(key, timestamp, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
assert_valid_integer(timestamp)
timestamp = Integer(timestamp)

pexpireat(key, timestamp.to_i * 1000, nx: nx, xx: xx, lt: lt, gt: gt)
end

def pexpireat(key, timestamp_ms, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
assert_valid_integer(timestamp_ms)
timestamp_ms = Integer(timestamp_ms)

if nx && gt || gt && lt || lt && nx || nx && xx
raise Redis::CommandError, <<~TXT.chomp
ERR NX and XX, GT or LT options at the same time are not compatible
TXT
raise Error.command_error(
'ERR NX and XX, GT or LT options at the same time are not compatible',
self
)
end

return false unless exists?(key)
Expand Down Expand Up @@ -157,7 +158,7 @@ def dump(key)

def restore(key, ttl, value, replace: false)
if !replace && exists?(key)
raise Redis::CommandError, 'BUSYKEY Target key name already exists.'
raise Error.command_error('BUSYKEY Target key name already exists.', self)
Comment on lines -160 to +161
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is tested only with MockRedis and not with the multiplexer.

end
data[key] = Marshal.load(value) # rubocop:disable Security/MarshalLoad
if ttl > 0
Expand Down Expand Up @@ -211,7 +212,7 @@ def randomkey

def rename(key, newkey)
unless data.include?(key)
raise Redis::CommandError, 'ERR no such key'
raise Error.command_error('ERR no such key', self)
end

if key != newkey
Expand All @@ -227,7 +228,7 @@ def rename(key, newkey)

def renamenx(key, newkey)
unless data.include?(key)
raise Redis::CommandError, 'ERR no such key'
raise Error.command_error('ERR no such key', self)
end

if exists?(newkey)
Expand Down Expand Up @@ -301,19 +302,13 @@ def eval(*args); end

private

def assert_valid_integer(integer)
unless looks_like_integer?(integer.to_s)
raise Redis::CommandError, 'ERR value is not an integer or out of range'
end
integer
end

def assert_valid_timeout(timeout)
if !looks_like_integer?(timeout.to_s)
raise Redis::CommandError, 'ERR timeout is not an integer or out of range'
elsif timeout < 0
raise Redis::CommandError, 'ERR timeout is negative'
timeout = Integer(timeout)

if timeout < 0
raise ArgumentError, 'time interval must not be negative'
end

timeout
end

Expand Down Expand Up @@ -347,7 +342,7 @@ def has_expiration?(key)
end

def looks_like_integer?(str)
str =~ /^-?\d+$/
!!Integer(str) rescue false
end

def looks_like_float?(str)
Expand Down
27 changes: 27 additions & 0 deletions lib/mock_redis/error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class MockRedis
module Error
module_function

def build(error_class, message, database)
connection = database.connection
url = "redis://#{connection[:host]}:#{connection[:port]}"
error_class.new("#{message} (#{url})")
end

def wrong_type_error(database)
build(
Redis::WrongTypeError,
'WRONGTYPE Operation against a key holding the wrong kind of value',
database
)
end

def syntax_error(database)
command_error('ERR syntax error', database)
end

def command_error(message, database)
build(Redis::CommandError, message, database)
end
end
end
16 changes: 9 additions & 7 deletions lib/mock_redis/geospatial_methods.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'mock_redis/error'

class MockRedis
module GeospatialMethods
LNG_RANGE = (-180..180)
Expand Down Expand Up @@ -81,8 +83,7 @@ def parse_points(args)
points = args.each_slice(3).to_a

if points.last.size != 3
raise Redis::CommandError,
"ERR wrong number of arguments for 'geoadd' command"
raise Error.command_error("ERR wrong number of arguments for 'geoadd' command", self)
end

points.map do |point|
Expand All @@ -97,13 +98,15 @@ def parse_point(point)
unless LNG_RANGE.include?(lng) && LAT_RANGE.include?(lat)
lng = format('%<long>.6f', long: lng)
lat = format('%<lat>.6f', lat: lat)
raise Redis::CommandError,
"ERR invalid longitude,latitude pair #{lng},#{lat}"
raise Error.command_error(
"ERR invalid longitude,latitude pair #{lng},#{lat}",
self
)
end

{ key: point[2], lng: lng, lat: lat }
rescue ArgumentError
raise Redis::CommandError, 'ERR value is not a valid float'
raise Error.command_error('ERR value is not a valid float', self)
end

# Returns ZSET score for passed coordinates
Expand Down Expand Up @@ -212,8 +215,7 @@ def parse_unit(unit)
unit = unit.to_sym
return UNITS[unit] if UNITS[unit]

raise Redis::CommandError,
'ERR unsupported unit provided. please use m, km, ft, mi'
raise Error.command_error('ERR unsupported unit provided. please use m, km, ft, mi', self)
end

def geohash_distance(lng1d, lat1d, lng2d, lat2d)
Expand Down
Loading
Loading