-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcache.ex
130 lines (104 loc) · 2.94 KB
/
cache.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
defmodule Augur.Cache do
@moduledoc """
A development cache for text messages that were sent
"""
use GenServer
require Logger
defstruct message_ets_key: :augur_message_cache,
name: __MODULE__,
thread_ets_key: :augur_threads_cache
alias Augur.Thread
@doc """
Cache a text message
"""
def cache(config, text_message) do
GenServer.call(config.name, {:cache, text_message})
end
@doc """
Fetch all text messages in the cache
"""
def text_messages(config) do
config.message_ets_key
|> keys()
|> Enum.map(fn key ->
{:ok, text_message} = get(config.message_ets_key, key)
text_message
end)
|> Enum.sort_by(fn text_message -> text_message.sent_at end)
|> Enum.reverse()
end
@doc """
Fetch all threads in the cache
"""
def threads(config) do
config.thread_ets_key
|> keys()
|> Enum.map(fn involved ->
{:ok, thread_id} = get(config.thread_ets_key, involved)
%Thread{id: thread_id, involved: involved}
end)
end
@doc """
Fetch a thread in the cache
"""
def thread(config, thread_id) do
messages =
Enum.filter(text_messages(config), fn text_message ->
text_message.thread_id == thread_id
end)
Thread.messages_to_thread(thread_id, messages)
end
@doc false
def get(ets_key, key) do
case :ets.lookup(ets_key, key) do
[{^key, value}] ->
{:ok, value}
_ ->
{:error, :not_found}
end
end
@doc false
def keys(ets_key) do
key = :ets.first(ets_key)
keys(key, [], ets_key)
end
@doc false
def keys(:"$end_of_table", accumulator, _ets_key), do: accumulator
def keys(current_key, accumulator, ets_key) do
next_key = :ets.next(ets_key, current_key)
keys(next_key, [current_key | accumulator], ets_key)
end
@doc false
def start_link(config) do
GenServer.start_link(__MODULE__, config, name: config.name)
end
@impl true
def init(config) do
{:ok, config, {:continue, :start_cache}}
end
@impl true
def handle_continue(:start_cache, state) do
:ets.new(state.thread_ets_key, [:set, :protected, :named_table])
:ets.new(state.message_ets_key, [:set, :protected, :named_table])
{:noreply, state}
end
@impl true
def handle_call({:cache, text_message}, _from, state) do
{:ok, thread_id} = cache_thread(state, text_message)
text_message = %{text_message | thread_id: thread_id}
Logger.info("Caching sent text - #{inspect(text_message)}")
:ets.insert(state.message_ets_key, {text_message.id, text_message})
{:reply, :ok, state}
end
defp cache_thread(state, text_message) do
involved = MapSet.new([text_message.from, text_message.to])
case :ets.lookup(state.thread_ets_key, involved) do
[{_involved, thread_id}] ->
{:ok, thread_id}
[] ->
thread_id = Thread.generate_id()
:ets.insert(state.thread_ets_key, {involved, thread_id})
{:ok, thread_id}
end
end
end