/
ets.ex
153 lines (117 loc) · 4.01 KB
/
ets.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
defmodule Mnemonix.Stores.ETS do
@moduledoc """
A `Mnemonix.Store` that uses an ETS table to store state.
iex> {:ok, store} = Mnemonix.Stores.ETS.start_link
iex> Mnemonix.put(store, "foo", "bar")
iex> Mnemonix.get(store, "foo")
"bar"
iex> Mnemonix.delete(store, "foo")
iex> Mnemonix.get(store, "foo")
nil
This store supports the functions in `Mnemonix.Features.Enumerable`.
"""
defmodule Exception do
defexception [:message]
end
use Mnemonix.Store.Behaviour
use Mnemonix.Store.Translator.Raw
alias Mnemonix.Store
####
# Mnemonix.Store.Behaviours.Core
##
@doc """
Creates a new ETS table to store state using provided `opts`.
## Options
- `table`: Name of the table to create.
- *Default:* `#{__MODULE__ |> Inspect.inspect(%Inspect.Opts{})}.Table`
- `named`: ETS named table option
- *Default:* `false`
- *Notes:* If making a non-private table it's reccommened to give your table a name.
- `privacy`: ETS privacy option - `:public | :protected | :private`
- *Default:* `:private`
- `heir`: ETS heir option - `{pid, any} | nil`
- *Default:* nil
- `concurrent`: Whether or not to optimize access for concurrent reads or writes.
- *Allowed:* `:reads | :writes | :both | false`
- *Default:* `false`
- `compressed`: Whether or not to compress the values being stored.
- *Default:* `false`
- `initial`: A map of key/value pairs to ensure are set on the DETS table at boot.
- *Default:* `%{}`
"""
@spec setup(Mnemonix.Store.options)
:: {:ok, state :: term} | {:stop, reason :: any}
def setup(opts) do
table = Keyword.get(opts, :table) || Module.concat(__MODULE__, Table)
privacy = Keyword.get(opts, :privacy) || :private
heir = Keyword.get(opts, :heir) || :none
read = Keyword.get(opts, :concurrent, false) in [:reads, :both]
write = Keyword.get(opts, :concurrent, false) in [:writes, :both]
options = [:set, privacy,
heir: heir,
read_concurrency: read,
write_concurrency: write
]
options = if Keyword.get(opts, :named) do
[:named_table | options]
else
options
end
options = if Keyword.get(opts, :compressed) do
[:compressed | options]
else
options
end
case :ets.new(table, options) do
{:error, reason} -> {:stop, reason}
state -> {:ok, state}
end
end
####
# Mnemonix.Store.Behaviours.Map
##
@spec delete(Mnemonix.Store.t, Mnemonix.key)
:: {:ok, Mnemonix.Store.t} | Mnemonix.Store.Behaviour.exception
def delete(store = %Store{state: table}, key) do
if :ets.delete(table, key) do
{:ok, store}
else
{:raise, Exception,
message: "ETS operation failed: `:ets.delete(#{table}, #{key})`"
}
end
end
@spec fetch(Mnemonix.Store.t, Mnemonix.key)
:: {:ok, Mnemonix.Store.t, {:ok, Mnemonix.value} | :error} | Mnemonix.Store.Behaviour.exception
def fetch(store = %Store{state: table}, key) do
case :ets.lookup(table, key) do
[{^key, value} | []] -> {:ok, store, {:ok, value}}
[] -> {:ok, store, :error}
other -> {:raise, Exception, [reason: other]}
end
end
@spec put(Mnemonix.Store.t, Mnemonix.key, Store.value)
:: {:ok, Mnemonix.Store.t} | Mnemonix.Store.Behaviour.exception
def put(store = %Store{state: table}, key, value) do
if :ets.insert(table, {key, value}) do
{:ok, store}
else
{:raise, Exception,
message: "ETS operation failed: `:ets.insert(#{table}, {#{key}, #{value}})`"
}
end
end
####
# Mnemonix.Store.Behaviours.Enumerable
##
@spec enumerable?(Mnemonix.Store.t)
:: {:ok, Mnemonix.Store.t, boolean} | Mnemonix.Store.Behaviour.exception
def enumerable?(store) do
{:ok, store, true}
end
@spec to_enumerable(Mnemonix.Store.t)
:: {:ok, Mnemonix.Store.t, Enumerable.t} | Mnemonix.Store.Behaviour.exception
def to_enumerable(store = %Store{state: table}) do
{:ok, store, :ets.tab2list(table)}
end
end