class MockRedis::Database
Attributes
data[R]
expire_times[R]
Public Class Methods
new(base, *_args)
click to toggle source
# File lib/mock_redis/database.rb, line 34 def initialize(base, *_args) @base = base @data = MockRedis::IndifferentHash.new @expire_times = [] end
Public Instance Methods
auth(_)
click to toggle source
# File lib/mock_redis/database.rb, line 55 def auth(_) 'OK' end
bgrewriteaof()
click to toggle source
# File lib/mock_redis/database.rb, line 59 def bgrewriteaof 'Background append only file rewriting started' end
bgsave()
click to toggle source
# File lib/mock_redis/database.rb, line 63 def bgsave 'Background saving started' end
call(command, &_block)
click to toggle source
FIXME: Current implementation of ‘call` does not work propetly with kwarg-options. i.e. `call(“EXPIRE”, “foo”, 40, “NX”)` (which redis-rb will simply transmit to redis-server) will be passed to `#expire` without keywords transformation.
# File lib/mock_redis/database.rb, line 51 def call(command, &_block) public_send(command[0].downcase, *command[1..]) end
connected?()
click to toggle source
# File lib/mock_redis/database.rb, line 73 def connected? true end
dbsize()
click to toggle source
# File lib/mock_redis/database.rb, line 77 def dbsize data.keys.length end
del(*keys)
click to toggle source
# File lib/mock_redis/database.rb, line 81 def del(*keys) keys = keys.flatten.map(&:to_s) # assert_has_args(keys, 'del') # no longer errors in redis > v4.5 keys. find_all { |key| data[key] }. each { |k| persist(k) }. each { |k| data.delete(k) }. length end
Also aliased as: unlink
disconnect()
click to toggle source
# File lib/mock_redis/database.rb, line 67 def disconnect nil end
Also aliased as: close
dump(key)
click to toggle source
# File lib/mock_redis/database.rb, line 153 def dump(key) value = data[key] value ? Marshal.dump(value) : nil end
echo(msg)
click to toggle source
# File lib/mock_redis/database.rb, line 93 def echo(msg) msg.to_s end
eval(*args)
click to toggle source
# File lib/mock_redis/database.rb, line 300 def eval(*args); end
evalsha(*args)
click to toggle source
# File lib/mock_redis/database.rb, line 298 def evalsha(*args); end
exists(*keys)
click to toggle source
# File lib/mock_redis/database.rb, line 139 def exists(*keys) keys.count { |key| data.key?(key) } end
exists?(*keys)
click to toggle source
# File lib/mock_redis/database.rb, line 143 def exists?(*keys) keys.each { |key| return true if data.key?(key) } false end
expire(key, seconds, nx: nil, xx: nil, lt: nil, gt: nil)
click to toggle source
# File lib/mock_redis/database.rb, line 97 def expire(key, seconds, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists assert_valid_integer(seconds) pexpire(key, seconds.to_i * 1000, nx: nx, xx: xx, lt: lt, gt: gt) end
expire_keys()
click to toggle source
This method isn’t private, but it also isn’t a Redis command, so it doesn’t belong up above with all the Redis commands.
# File lib/mock_redis/database.rb, line 396 def expire_keys now_sec, miliseconds = now now_ms = now_sec * 1_000 + miliseconds to_delete = expire_times.take_while do |(time, _key)| (time.to_r * 1_000).to_i <= now_ms end to_delete.each do |(_time, key)| del(key) end end
expireat(key, timestamp, nx: nil, xx: nil, lt: nil, gt: nil)
click to toggle source
# File lib/mock_redis/database.rb, line 111 def expireat(key, timestamp, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists assert_valid_integer(timestamp) pexpireat(key, timestamp.to_i * 1000, nx: nx, xx: xx, lt: lt, gt: gt) end
flushdb()
click to toggle source
# File lib/mock_redis/database.rb, line 148 def flushdb data.each_key { |k| del(k) } 'OK' end
initialize_copy(_source)
click to toggle source
# File lib/mock_redis/database.rb, line 40 def initialize_copy(_source) @data = @data.clone @data.each_key { |k| @data[k] = @data[k].clone } @expire_times = @expire_times.map(&:clone) end
keys(format = '*')
click to toggle source
# File lib/mock_redis/database.rb, line 169 def keys(format = '*') data.keys.grep(redis_pattern_to_ruby_regex(format)) end
lastsave()
click to toggle source
# File lib/mock_redis/database.rb, line 187 def lastsave now.first end
now()
click to toggle source
# File lib/mock_redis/database.rb, line 269 def now current_time = @base.options[:time_class].now miliseconds = (current_time.to_r - current_time.to_i) * 1_000 [current_time.to_i, miliseconds.to_i] end
Also aliased as: time
persist(key)
click to toggle source
# File lib/mock_redis/database.rb, line 191 def persist(key) if exists?(key) && has_expiration?(key) remove_expiration(key) true else false end end
pexpire(key, ms, nx: nil, xx: nil, lt: nil, gt: nil)
click to toggle source
# File lib/mock_redis/database.rb, line 103 def pexpire(key, ms, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists assert_valid_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
pexpireat(key, timestamp_ms, nx: nil, xx: nil, lt: nil, gt: nil)
click to toggle source
# File lib/mock_redis/database.rb, line 117 def pexpireat(key, timestamp_ms, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists assert_valid_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 end return false unless exists?(key) expiry = expiration(key) new_expiry = @base.time_at(Rational(timestamp_ms.to_i, 1000)) if should_update_expiration?(expiry, new_expiry, nx: nx, xx: xx, lt: lt, gt: gt) set_expiration(key, new_expiry) true else false end end
ping(response = 'PONG')
click to toggle source
# File lib/mock_redis/database.rb, line 200 def ping(response = 'PONG') response end
pttl(key)
click to toggle source
# File lib/mock_redis/database.rb, line 256 def pttl(key) now, miliseconds = @base.now now_ms = now * 1000 + miliseconds if !exists?(key) -2 elsif has_expiration?(key) (expiration(key).to_r * 1000).to_i - now_ms else -1 end end
quit()
click to toggle source
# File lib/mock_redis/database.rb, line 204 def quit 'OK' end
randomkey()
click to toggle source
# File lib/mock_redis/database.rb, line 208 def randomkey data.keys[rand(data.length)] end
rename(key, newkey)
click to toggle source
# File lib/mock_redis/database.rb, line 212 def rename(key, newkey) unless data.include?(key) raise Redis::CommandError, 'ERR no such key' end if key != newkey data[newkey] = data.delete(key) if has_expiration?(key) set_expiration(newkey, expiration(key)) remove_expiration(key) end end 'OK' end
renamenx(key, newkey)
click to toggle source
# File lib/mock_redis/database.rb, line 228 def renamenx(key, newkey) unless data.include?(key) raise Redis::CommandError, 'ERR no such key' end if exists?(newkey) false else rename(key, newkey) true end end
restore(key, ttl, value, replace: false)
click to toggle source
# File lib/mock_redis/database.rb, line 158 def restore(key, ttl, value, replace: false) if !replace && exists?(key) raise Redis::CommandError, 'BUSYKEY Target key name already exists.' end data[key] = Marshal.load(value) # rubocop:disable Security/MarshalLoad if ttl > 0 pexpire(key, ttl) end 'OK' end
save()
click to toggle source
# File lib/mock_redis/database.rb, line 241 def save 'OK' end
scan(cursor, opts = {})
click to toggle source
# File lib/mock_redis/database.rb, line 173 def scan(cursor, opts = {}) common_scan(data.keys, cursor, opts) end
scan_each(opts = {}, &block)
click to toggle source
# File lib/mock_redis/database.rb, line 177 def scan_each(opts = {}, &block) return to_enum(:scan_each, opts) unless block_given? cursor = 0 loop do cursor, keys = scan(cursor, opts) keys.each(&block) break if cursor == '0' end end
script(subcommand, *args)
click to toggle source
# File lib/mock_redis/database.rb, line 296 def script(subcommand, *args); end
ttl(key)
click to toggle source
# File lib/mock_redis/database.rb, line 245 def ttl(key) if !exists?(key) -2 elsif has_expiration?(key) now, = @base.now expiration(key).to_i - now else -1 end end
type(key)
click to toggle source
# File lib/mock_redis/database.rb, line 276 def type(key) if !exists?(key) 'none' elsif hashy?(key) 'hash' elsif stringy?(key) 'string' elsif listy?(key) 'list' elsif sety?(key) 'set' elsif zsety?(key) 'zset' elsif streamy?(key) 'stream' else raise ArgumentError, "Not sure how #{data[key].inspect} got in here" end end
Private Instance Methods
assert_valid_integer(integer)
click to toggle source
# File lib/mock_redis/database.rb, line 304 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
assert_valid_timeout(timeout)
click to toggle source
# File lib/mock_redis/database.rb, line 311 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' end timeout end
can_incr?(value)
click to toggle source
# File lib/mock_redis/database.rb, line 320 def can_incr?(value) value.nil? || looks_like_integer?(value) end
can_incr_float?(value)
click to toggle source
# File lib/mock_redis/database.rb, line 324 def can_incr_float?(value) value.nil? || looks_like_float?(value) end
expiration(key)
click to toggle source
# File lib/mock_redis/database.rb, line 341 def expiration(key) expire_times.find { |(_, k)| k == key.to_s }&.first end
extract_timeout(arglist)
click to toggle source
# File lib/mock_redis/database.rb, line 328 def extract_timeout(arglist) options = arglist.last if options.is_a?(Hash) && options[:timeout] timeout = assert_valid_timeout(options[:timeout]) [arglist[0..-2], timeout] elsif options.is_a?(Integer) timeout = assert_valid_timeout(options) [arglist[0..-2], timeout] else [arglist, 0] end end
has_expiration?(key)
click to toggle source
# File lib/mock_redis/database.rb, line 345 def has_expiration?(key) expire_times.any? { |(_, k)| k == key.to_s } end
looks_like_float?(str)
click to toggle source
# File lib/mock_redis/database.rb, line 353 def looks_like_float?(str) !!Float(str) rescue false end
looks_like_integer?(str)
click to toggle source
# File lib/mock_redis/database.rb, line 349 def looks_like_integer?(str) str =~ /^-?\d+$/ end
redis_pattern_to_ruby_regex(pattern)
click to toggle source
# File lib/mock_redis/database.rb, line 365 def redis_pattern_to_ruby_regex(pattern) Regexp.new( "^#{pattern}$". gsub(/([+|(){}])/, '\\\\\1'). gsub(/(?<!\\)\?/, '\\1.'). gsub(/([^\\])\*/, '\\1.*') ) end
remove_expiration(key)
click to toggle source
# File lib/mock_redis/database.rb, line 374 def remove_expiration(key) expire_times.delete_if do |(_t, k)| key.to_s == k end end
set_expiration(key, time)
click to toggle source
# File lib/mock_redis/database.rb, line 380 def set_expiration(key, time) remove_expiration(key) found = expire_times.each_with_index.to_a.bsearch { |item, _| item.first >= time } index = found ? found.last : -1 expire_times.insert(index, [time, key.to_s]) end
should_update_expiration?(expiry, new_expiry, nx:, xx:, lt:, gt:)
click to toggle source
# File lib/mock_redis/database.rb, line 357 def should_update_expiration?(expiry, new_expiry, nx:, xx:, lt:, gt:) # rubocop:disable Metrics/ParameterLists return false if nx && expiry || xx && !expiry return false if lt && expiry && new_expiry > expiry return false if gt && (!expiry || new_expiry < expiry) true end
zero_pad(string, desired_length)
click to toggle source
# File lib/mock_redis/database.rb, line 387 def zero_pad(string, desired_length) padding = "\000" * [(desired_length - string.length), 0].max string + padding end