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
|
defmodule Lkn.Core.Map do
@moduledoc """
A specialized module to easily implement Map, one of the two kind of Entities
used by lkn.
This module provides the `defmap/2` macro, to easily define a module which
implements the `Lkn.Core.Entity` behaviour module.
Defining a new map is pretty easy, at it can be seen as some kind of key-value
store. First, the `@components` annotation has to be used to define the set of
components the map can be abstracted away with. Then, the
`Lkn.Core.Entity.init_properties/1` behaviour function needs to be
implemented.
Finally, the `defmap/2` macro user needs to implement a `start_link`
functions, that is only a proxy to `Lkn.Core.Entity.start_link/3`; the last
argument being the arguments for `init_properties`.
Here is an example of a proper use of `defmap/2`.
defmap Test.Map do
@components [Test.Map.Component1, Test.Map.Component2]
def start_link(key, args) do
Lkn.Core.Entity.start_link(__MODULE__, key, args)
end
def init_properties(args) do
# ...
end
end
"""
@typedoc """
A key to identify and reach a Map.
"""
@type k :: any
@doc """
An helper macro to define a Map module.
The main idea of this macro is to hide the boilerplate required to define a
Map. Its counterpart`Lkn.Core.Puppet.defpuppet/2` can be used to define a
Puppet.
"""
defmacro defmap(name, do: block) do
lines = case block do
{:__block__, _, x} -> x
x -> [x]
end
quote do
defmodule unquote(name) do
@after_compile __MODULE__
unquote(lines)
def components do
@components
end
use Lkn.Core.Entity, components: @components
def __after_compile__(_env, _bytecode) do
# check if the components are effectively valid
Enum.map(components(), fn cx ->
c = Macro.expand(cx, __MODULE__)
gold = c.specs().system().component(:map)
if gold != c.specs() do
raise "Module #{inspect c} implements #{inspect c.specs()} which is not the map component of #{inspect c.specs().system()}"
end
end)
end
end
end
end
end
|