-
Notifications
You must be signed in to change notification settings - Fork 0
/
kob.ex
105 lines (89 loc) · 2.05 KB
/
kob.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
defmodule Kob do
@moduledoc """
Documentation for Kob.
"""
@type handler :: (Plug.Conn.t() -> Plug.Conn.t())
@type middleware :: (handler -> handler)
@doc """
Compose a list of middlewares into one middleware.
"""
@spec compose([middleware]) :: middleware
def compose(middlewares) when is_list(middlewares) do
fn next ->
middlewares
|> Enum.reverse()
|> Enum.reduce(next, fn prev, next ->
prev.(next)
end)
end
end
@type t :: %__MODULE__{
mws: [middleware()]
}
defstruct mws: []
@doc """
Create an empty Kob struct.
"""
@spec new() :: t
def new() do
%Kob{mws: []}
end
@doc """
Append a middleware to Kob struct.
"""
@spec use(t, middleware) :: t
def use(%Kob{mws: mws}, middleware) do
%Kob{mws: [middleware | mws]}
end
@doc """
Convert a Plug to Kob middleware.
"""
@spec plug(Plug.Builder.plug(), Plug.opts()) :: middleware
def plug(plug, opts \\ []) when is_atom(plug) do
fn next ->
x = apply(plug, :init, [opts])
fn conn ->
case apply(plug, :call, [conn, x]) do
%{halted: true} = new_conn ->
new_conn
new_conn ->
next.(new_conn)
end
end
end
end
@doc """
Create a Kob middleware from Kob struct.
"""
@spec to_middleware(t) :: middleware
def to_middleware(%Kob{mws: mws}) do
mws
|> Enum.reverse()
|> compose()
end
@doc """
Create a Kob handler from Kob struct.
"""
@spec to_handler(t) :: middleware
def to_handler(%Kob{} = kob) do
to_middleware(kob).(fn conn -> conn end)
end
@doc """
Convert a Kob struct to a Plug.
"""
@spec register_plug(t, Plug.Builder.plug()) :: :ok
def register_plug(%Kob{} = kob, plug) when is_atom(plug) do
:persistent_term.put(plug, to_handler(kob))
Code.eval_string("
defmodule #{plug} do
def init(_opt) do
:persistent_term.get(#{plug})
end
def call(conn, handler) do
handler.(conn)
end
end
")
:ok
end
end