/
delegator.ex
103 lines (82 loc) · 2.22 KB
/
delegator.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
defmodule ExActor.Delegator do
@moduledoc """
Provides `delegate_to/2` macro that can be used to simplify cases when
call/cast operations delegate to another module.
"""
@doc """
Creates wrapper operations around the `target_module`.
For example:
defmodule HashDictActor do
use ExActor.GenServer
import ExActor.Delegator
delegate_to HashDict do
init
query get/2
trans put/3
end
end
This is the same as:
defmodule HashDictActor do
use ExActor.GenServer
definit do: HashDict.new
defcall get(k), state: state do
HashDict.get(state, k)
|> reply
end
defcast put(k, v), state:state do
HashDict.put(state, k, v)
|> new_state
end
end
"""
defmacro delegate_to(target_module, opts) do
statements(opts[:do])
|> Enum.map(&(parse_instruction(target_module, &1)))
end
defp statements({:__block__, _, statements}), do: statements
defp statements(statement), do: [statement]
defp parse_instruction(target_module, {:init, _, _}) do
quote do
definit do
unquote(target_module).new
|> initial_state
end
end
end
defp parse_instruction(target_module, {:query, _, [{:/, _, [{fun, _, _}, arity]}]}) do
make_delegate(:defcall, fun, arity,
quote do
unquote(forward_call(target_module, fun, arity))
|> reply
end
)
end
defp parse_instruction(target_module, {:trans, _, [{:/, _, [{fun, _, _}, arity]}]}) do
make_delegate(:defcast, fun, arity,
quote do
unquote(forward_call(target_module, fun, arity))
|> new_state
end
)
end
defp make_delegate(type, fun, arity, code) do
quote do
unquote(type)(
unquote(fun)(unquote_splicing(make_args(arity))),
state: state,
do: unquote(code)
)
end
end
defp forward_call(target_module, fun, arity) do
full_args = [quote(do: state) | make_args(arity)]
quote do
unquote(target_module).unquote(fun)(unquote_splicing(full_args))
end
end
defp make_args(arity) when arity > 0 do
1..arity
|> Enum.map(&{:"arg#{&1}", [], nil})
|> tl
end
end