Skip to content

Commit da9dbcc

Browse files
committed
[#1] Nebulex.Adapter behaviour
1 parent 099bbcc commit da9dbcc

12 files changed

Lines changed: 511 additions & 5 deletions

File tree

.formatter.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Used by "mix format"
2+
[
3+
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
4+
]

.github/workflows/ci.yml

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
nebulex_test:
13+
name: 'NebulexCachexAdapter Test (Elixir ${{ matrix.elixir }} OTP ${{ matrix.otp }})'
14+
runs-on: ubuntu-latest
15+
16+
strategy:
17+
matrix:
18+
include:
19+
- elixir: 1.10.x
20+
otp: 23.x
21+
- elixir: 1.10.x
22+
otp: 22.x
23+
- elixir: 1.9.x
24+
otp: 22.x
25+
26+
env:
27+
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
28+
MIX_ENV: test
29+
NBX_TEST: true
30+
31+
steps:
32+
- uses: actions/checkout@v2
33+
34+
- uses: actions/setup-elixir@v1
35+
with:
36+
otp-version: '${{ matrix.otp }}'
37+
elixir-version: '${{ matrix.elixir }}'
38+
39+
- uses: actions/cache@v1
40+
with:
41+
path: deps
42+
key: >-
43+
${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-${{
44+
hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
45+
restore-keys: |
46+
${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-mix-
47+
48+
- uses: actions/cache@v1
49+
with:
50+
path: _build
51+
key: >-
52+
${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-build-${{
53+
hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
54+
restore-keys: |
55+
${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-build-
56+
57+
- name: Install Dependencies
58+
run: |
59+
mix local.hex --force
60+
mix local.rebar --force
61+
mix deps.get
62+
63+
- name: Run style and code consistency checks
64+
run: |
65+
mix compile --warnings-as-errors
66+
mix format --check-formatted
67+
mix credo --strict
68+
69+
- name: Run tests
70+
run: |
71+
epmd -daemon
72+
mix coveralls.github
73+
74+
- uses: actions/cache@v1
75+
with:
76+
path: priv/plts
77+
key: '${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-plt-v1'
78+
restore-keys: |
79+
${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-plt-v1
80+
81+
- name: Run static analysis checks
82+
run: |
83+
mix sobelow --exit --skip
84+
mix dialyzer --format short

.gitignore

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,39 @@
1+
# The directory Mix will write compiled artifacts to.
12
/_build
3+
4+
# If you run "mix test --cover", coverage assets end up here.
25
/cover
6+
7+
# The directory Mix downloads your dependencies sources to.
38
/deps
9+
10+
# Where 3rd-party dependencies like ExDoc output generated docs.
411
/doc
12+
/docs
13+
/benchmarks/output
14+
15+
# Ignore .fetch files in case you like to edit your project deps locally.
516
/.fetch
17+
18+
# Dialyzer
19+
/priv
20+
21+
# If the VM crashes, it generates a dump, let's ignore it too.
622
erl_crash.dump
23+
24+
# Also ignore archive artifacts (built via "mix archive.build").
725
*.ez
26+
27+
# Others
28+
*.o
829
*.beam
9-
/config/*.secret.exs
10-
.elixir_ls/
30+
*.plt
31+
erl_crash.dump
32+
.DS_Store
33+
._*
34+
/tmp*
35+
.elixir*
36+
.vs*
37+
/priv
38+
.sobelow*
39+
mix.lock

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21-
SOFTWARE.
21+
SOFTWARE.

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,20 @@
1-
# nebulex_cachex_adapter
2-
Nebulex adapter for Cachex
1+
# NebulexCachexAdapter
2+
> ### Nebulex adapter for [Cachex][Cachex].
3+
4+
[Cachex]: https://github.com/whitfin/cachex
5+
6+
![CI](https://github.com/cabol/nebulex_cachex_adapter/workflows/CI/badge.svg)
7+
8+
*Still WIP*
9+
10+
## Installation
11+
12+
Add `nebulex_cachex_adapter` to your list of dependencies in `mix.exs`:
13+
14+
```elixir
15+
def deps do
16+
[
17+
{:nebulex_cachex_adapter, "~> 0.1.0"}
18+
]
19+
end
20+
```

coveralls.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"coverage_options": {
3+
"minimum_coverage": 100
4+
},
5+
6+
"skip_files": [
7+
"test/support/*"
8+
]
9+
}

lib/nebulex_cachex_adapter.ex

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
defmodule NebulexCachexAdapter do
2+
@moduledoc """
3+
Nebulex adapter for Cachex.
4+
"""
5+
6+
# Provide Cache Implementation
7+
@behaviour Nebulex.Adapter
8+
9+
import Nebulex.Helpers
10+
11+
@compile {:inline, to_ttl: 1}
12+
13+
## Adapter
14+
15+
@impl true
16+
defmacro __before_compile__(_), do: :ok
17+
18+
@impl true
19+
def init(opts) do
20+
name =
21+
normalize_module_name([
22+
opts[:name] || Keyword.fetch!(opts, :cache),
23+
Cachex
24+
])
25+
26+
child_spec =
27+
opts
28+
|> Keyword.put(:name, name)
29+
|> Cachex.child_spec()
30+
31+
{:ok, child_spec, %{name: name}}
32+
end
33+
34+
@impl true
35+
def get(%{name: name}, key, _opts) do
36+
Cachex.get!(name, key)
37+
end
38+
39+
@impl true
40+
def get_all(%{name: name}, keys, _opts) do
41+
Enum.reduce(keys, %{}, fn key, acc ->
42+
if value = Cachex.get!(name, key) do
43+
Map.put(acc, key, value)
44+
else
45+
acc
46+
end
47+
end)
48+
end
49+
50+
@impl true
51+
def put(%{name: name}, key, value, ttl, :put, _opts) do
52+
Cachex.put!(name, key, value, ttl: to_ttl(ttl))
53+
end
54+
55+
def put(%{name: name}, key, value, ttl, :replace, _opts) do
56+
Cachex.update!(name, key, value, ttl: to_ttl(ttl))
57+
end
58+
59+
def put(%{name: name}, key, value, ttl, :put_new, _opts) do
60+
if Cachex.get!(name, key) do
61+
false
62+
else
63+
Cachex.put!(name, key, value, ttl: to_ttl(ttl))
64+
end
65+
end
66+
67+
@impl true
68+
def put_all(adapter_meta, entries, ttl, on_write, opts) when is_map(entries) do
69+
put_all(adapter_meta, :maps.to_list(entries), ttl, on_write, opts)
70+
end
71+
72+
def put_all(%{name: name}, entries, ttl, :put, _opts) when is_list(entries) do
73+
Cachex.put_many!(name, entries, ttl: to_ttl(ttl))
74+
end
75+
76+
def put_all(%{name: name}, entries, ttl, :put_new, _opts) when is_list(entries) do
77+
{keys, _} = Enum.unzip(entries)
78+
79+
Cachex.transaction!(name, keys, fn worker ->
80+
if Enum.any?(keys, &(worker |> Cachex.exists?(&1) |> elem(1))) do
81+
false
82+
else
83+
Cachex.put_many!(worker, entries, ttl: to_ttl(ttl))
84+
end
85+
end)
86+
end
87+
88+
@impl true
89+
def delete(%{name: name}, key, _opts) do
90+
true = Cachex.del!(name, key)
91+
:ok
92+
end
93+
94+
@impl true
95+
def take(%{name: name}, key, _opts) do
96+
Cachex.take!(name, key)
97+
end
98+
99+
@impl true
100+
def has_key?(%{name: name}, key) do
101+
{:ok, bool} = Cachex.exists?(name, key)
102+
bool
103+
end
104+
105+
@impl true
106+
def ttl(%{name: name}, key) do
107+
cond do
108+
ttl = Cachex.ttl!(name, key) ->
109+
ttl
110+
111+
Cachex.get!(name, key) ->
112+
:infinity
113+
114+
true ->
115+
nil
116+
end
117+
end
118+
119+
@impl true
120+
def expire(%{name: name}, key, ttl) do
121+
Cachex.expire!(name, key, to_ttl(ttl))
122+
end
123+
124+
@impl true
125+
def touch(%{name: name}, key) do
126+
Cachex.touch!(name, key)
127+
end
128+
129+
@impl true
130+
def incr(%{name: name}, key, incr, :infinity, opts) do
131+
Cachex.incr!(name, key, incr, initial: opts[:default] || 0)
132+
end
133+
134+
def incr(%{name: name}, key, incr, ttl, opts) do
135+
Cachex.transaction!(name, [key], fn worker ->
136+
counter = Cachex.incr!(worker, key, incr, initial: opts[:default] || 0)
137+
if ttl = to_ttl(ttl), do: Cachex.expire!(worker, key, ttl)
138+
counter
139+
end)
140+
end
141+
142+
@impl true
143+
def size(%{name: name}) do
144+
Cachex.size!(name)
145+
end
146+
147+
@impl true
148+
def flush(%{name: name}) do
149+
size = Cachex.size!(name)
150+
true = Cachex.reset!(name)
151+
size
152+
end
153+
154+
## Private Functions
155+
156+
defp to_ttl(:infinity), do: nil
157+
defp to_ttl(ttl), do: ttl
158+
end

0 commit comments

Comments
 (0)