Class: CMDx::Stores::Redis

Inherits:
Object
  • Object
show all
Defined in:
lib/cmdx/stores/redis.rb

Overview

Delegates to a Redis-compatible client (redis-rb, MockRedis, etc.). Optional namespace prefixes keys when Redis is shared with other data.

The client must respond to get, set, incr, expire, ttl, del, and eval.

Constant Summary collapse

DEL_IF_SCRIPT =
<<~LUA
  if redis.call('GET', KEYS[1]) == ARGV[1] then
    return redis.call('DEL', KEYS[1])
  else
    return 0
  end
LUA

Instance Method Summary collapse

Constructor Details

#initialize(redis:, namespace: EMPTY_STRING) ⇒ Redis

Returns a new instance of Redis.

Parameters:

  • redis (#get, #set, #incr, #expire, #del, #eval) —

    backing client

  • namespace (String) (defaults to: EMPTY_STRING) —

    optional prefix for every key (trailing : added when missing; empty string stores logical keys as-is, matching Memory)



22
23
24
25
# File 'lib/cmdx/stores/redis.rb', line 22

def initialize(redis:, namespace: EMPTY_STRING)
  @redis = redis
  @namespace = namespace_prefix(namespace)
end

Instance Method Details

#decr(key, ttl: nil) ⇒ Integer

Parameters:

  • key (String, Symbol)

Returns:

  • (Integer)


81
82
83
84
85
86
# File 'lib/cmdx/stores/redis.rb', line 81

def decr(key, ttl: nil)
  key = prefixed(key)
  Stores.ttl!(ttl) if ttl

  @redis.decr(key)
end

#del(key) ⇒ Integer

Parameters:

  • key (String, Symbol)

Returns:

  • (Integer)


90
91
92
# File 'lib/cmdx/stores/redis.rb', line 90

def del(key)
  @redis.del(prefixed(key))
end

#del_if(key, expected) ⇒ Boolean

Returns whether the key was removed.

Parameters:

  • key (String, Symbol)
  • expected (#to_s)

Returns:

  • (Boolean) —

    whether the key was removed



97
98
99
100
101
# File 'lib/cmdx/stores/redis.rb', line 97

def del_if(key, expected) # rubocop:disable Naming/PredicateMethod -- store delete contract
  key = prefixed(key)
  deleted = @redis.eval(DEL_IF_SCRIPT, keys: [key], argv: [expected.to_s])
  deleted.to_i.positive?
end

#get(key) ⇒ String?

Parameters:

  • key (String, Symbol)

Returns:

  • (String, nil)


29
30
31
# File 'lib/cmdx/stores/redis.rb', line 29

def get(key)
  @redis.get(prefixed(key))
end

#incr(key, ttl: nil) ⇒ Integer

Parameters:

  • key (String, Symbol)
  • ttl (Integer, nil) (defaults to: nil)

Returns:

  • (Integer)

Raises:

  • (ArgumentError) —

    when ttl is invalid



62
63
64
65
66
67
68
69
# File 'lib/cmdx/stores/redis.rb', line 62

def incr(key, ttl: nil)
  key = prefixed(key)
  ttl = Stores.ttl!(ttl) if ttl

  count = @redis.incr(key)
  @redis.expire(key, ttl) if ttl
  count
end

#set(key, value, ttl: nil, nx: false, xx: false) ⇒ Boolean

Parameters:

  • key (String, Symbol)
  • value (#to_s)
  • ttl (Integer, nil) (defaults to: nil)
  • nx (Boolean) (defaults to: false)
  • xx (Boolean) (defaults to: false)

Returns:

  • (Boolean)

Raises:

  • (ArgumentError) —

    when nx and xx are both true, or ttl is invalid



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/cmdx/stores/redis.rb', line 40

def set(key, value, ttl: nil, nx: false, xx: false) # rubocop:disable Naming/PredicateMethod -- Redis SET contract
  key = prefixed(key)
  ttl = Stores.ttl!(ttl) if ttl
  Stores.nx_xx!(nx, xx)

  options = {}
  options[:ex] = ttl if ttl
  options[:nx] = true if nx
  options[:xx] = true if xx

  if options.empty?
    @redis.set(key, value.to_s)
    true
  else
    !!@redis.set(key, value.to_s, **options)
  end
end

#ttl(key) ⇒ Integer?

Returns seconds until expiry, or nil when missing or expired.

Parameters:

  • key (String, Symbol)

Returns:

  • (Integer, nil) —

    seconds until expiry, or nil when missing or expired



73
74
75
76
77
# File 'lib/cmdx/stores/redis.rb', line 73

def ttl(key)
  key = prefixed(key)
  seconds = @redis.ttl(key)
  seconds if seconds.is_a?(Integer) && seconds.positive?
end