Class: Ask::State::Memory

Inherits:
Adapter
  • Object
show all
Defined in:
lib/ask/state/memory.rb

Overview

In-process state backend backed by a Hash. All operations are thread-safe via a Mutex. Data is not persisted — lost on process exit.

Available automatically when using ask-state-providers. For lightweight use without the full providers gem, see Adapter for the abstract contract.

Instance Method Summary collapse

Constructor Details

#initializeMemory

Returns a new instance of Memory.



15
16
17
18
19
20
21
# File 'lib/ask/state/memory.rb', line 15

def initialize
  @data = {}
  @locks = {}
  @queues = {}
  @lists = {}
  @mutex = Mutex.new
end

Instance Method Details

#acquire_lock(key, ttl: 10) ⇒ Object

-- locking --



97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/ask/state/memory.rb', line 97

def acquire_lock(key, ttl: 10)
  @mutex.synchronize do
    existing = @locks[key]
    if existing.nil? || existing.expired?
      lock = Lock.new(
        id: key,
        token: SecureRandom.hex(16),
        expires_at: Time.now + ttl
      )
      @locks[key] = lock
      lock
    end
  end
end

#clearObject



63
64
65
66
67
68
69
70
# File 'lib/ask/state/memory.rb', line 63

def clear
  @mutex.synchronize do
    @data.clear
    @locks.clear
    @queues.clear
    @lists.clear
  end
end

#closeObject

-- lifecycle --



185
186
187
188
189
190
191
192
# File 'lib/ask/state/memory.rb', line 185

def close
  @mutex.synchronize do
    @data.clear
    @locks.clear
    @queues.clear
    @lists.clear
  end
end

#delete(key) ⇒ Object



43
44
45
# File 'lib/ask/state/memory.rb', line 43

def delete(key)
  @mutex.synchronize { @data.delete(key) }
end

#dequeue(queue) ⇒ Object



139
140
141
142
143
144
145
# File 'lib/ask/state/memory.rb', line 139

def dequeue(queue)
  @mutex.synchronize do
    q = @queues[queue]
    return nil unless q&.any?
    q.shift
  end
end

#enqueue(queue, value) ⇒ Object

-- queues --



126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ask/state/memory.rb', line 126

def enqueue(queue, value)
  @mutex.synchronize do
    @queues[queue] ||= []
    entry = QueueEntry.new(
      id: SecureRandom.uuid,
      value: value,
      enqueued_at: Time.now
    )
    @queues[queue] << entry
    entry
  end
end

#exists?(key) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
75
76
77
78
# File 'lib/ask/state/memory.rb', line 72

def exists?(key)
  @mutex.synchronize do
    expiry = @data[key]&.dig(:expires_at)
    return false if expiry && Time.now >= expiry
    @data.key?(key)
  end
end

#get(key) ⇒ Object

-- key-value --



25
26
27
28
29
30
31
32
# File 'lib/ask/state/memory.rb', line 25

def get(key)
  @mutex.synchronize do
    expiry = @data[key]&.dig(:expires_at)
    return nil if expiry && Time.now >= expiry

    @data[key]&.dig(:value)
  end
end

#keys(pattern: nil) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/ask/state/memory.rb', line 80

def keys(pattern: nil)
  @mutex.synchronize do
    active = @data.select do |_k, v|
      expiry = v[:expires_at]
      expiry.nil? || Time.now < expiry
    end

    keys = active.keys.map(&:to_s)
    return keys unless pattern

    regex = self.class.glob_to_regex(pattern)
    keys.select { |k| k.match?(regex) }
  end
end

#list_append(key, value, max_length: nil) ⇒ Object

-- lists --



155
156
157
158
159
160
161
# File 'lib/ask/state/memory.rb', line 155

def list_append(key, value, max_length: nil)
  @mutex.synchronize do
    @lists[key] ||= []
    @lists[key] << value
    @lists[key].shift if max_length && @lists[key].length > max_length
  end
end

#list_range(key, start = 0, stop = -1)) ⇒ Object



163
164
165
166
167
168
169
170
171
# File 'lib/ask/state/memory.rb', line 163

def list_range(key, start = 0, stop = -1)
  @mutex.synchronize do
    list = @lists[key] || []
    return list if start == 0 && stop == -1
    stop = list.length - 1 if stop == -1 || stop >= list.length
    return [] if start > stop
    list[start..stop] || []
  end
end

#list_remove(key, value) ⇒ Object



173
174
175
176
177
178
179
180
181
# File 'lib/ask/state/memory.rb', line 173

def list_remove(key, value)
  @mutex.synchronize do
    list = @lists[key]
    return 0 unless list
    before = list.length
    list.delete(value)
    before - list.length
  end
end

#queue_depth(queue) ⇒ Object



147
148
149
150
151
# File 'lib/ask/state/memory.rb', line 147

def queue_depth(queue)
  @mutex.synchronize do
    (@queues[queue] || []).length
  end
end

#release_lock(key, lock) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/ask/state/memory.rb', line 112

def release_lock(key, lock)
  @mutex.synchronize do
    current = @locks[key]
    if current && current.token == lock.token && !current.expired?
      @locks.delete(key)
      true
    else
      false
    end
  end
end

#set(key, value, ttl: nil) ⇒ Object



34
35
36
37
38
39
40
41
# File 'lib/ask/state/memory.rb', line 34

def set(key, value, ttl: nil)
  @mutex.synchronize do
    @data[key] = {
      value: value,
      expires_at: ttl ? Time.now + ttl : nil
    }
  end
end

#set_if_not_exists(key, value, ttl: nil) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/ask/state/memory.rb', line 47

def set_if_not_exists(key, value, ttl: nil)
  @mutex.synchronize do
    if @data.key?(key)
      expiry = @data[key][:expires_at]
      return false if expiry.nil? || Time.now < expiry
      @data.delete(key)
    end

    @data[key] = {
      value: value,
      expires_at: ttl ? Time.now + ttl : nil
    }
    true
  end
end