aboutsummaryrefslogtreecommitdiffstats
path: root/lib/lkn/core/component.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lkn/core/component.ex')
-rw-r--r--lib/lkn/core/component.ex135
1 files changed, 33 insertions, 102 deletions
diff --git a/lib/lkn/core/component.ex b/lib/lkn/core/component.ex
index b79a6f6..51b5a63 100644
--- a/lib/lkn/core/component.ex
+++ b/lib/lkn/core/component.ex
@@ -15,110 +15,23 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-alias Lkn.Core.Specs
-
defmodule Lkn.Core.Component do
- defp cast_client(module_name, cast) do
- name = cast.fun.name
- entity_key_type = quote do
- Lkn.Core.Entity.k
- end
-
- cast_doc = if cast.doc != :none do
- quote do
- @doc unquote(cast.doc)
- end
- end
-
- arglist = Enum.map(cast.fun.arguments, &(&1.name))
- arglistcl = [{:entity_key, [], nil}|arglist]
- argtypes = [entity_key_type|Enum.map(cast.fun.arguments, &(&1.type))]
-
- quote do
- unquote(cast_doc)
- @spec unquote({name, [], argtypes}) :: :ok
- def unquote({name, [], arglistcl}) do
- GenServer.cast(Lkn.Core.Name.component(unquote({:entity_key, [], nil}), unquote(module_name)),
- {:spec, {unquote(name), unquote(arglist)}})
- end
- end
- end
+ alias Lkn.Core.Specs
- defp call_client(module_name, call) do
- name = call.fun.name
- entity_key_type = quote do
- Lkn.Core.Entity.k
- end
-
- call_doc = if call.doc != :none do
- quote do
- @doc unquote(call.doc)
- end
- end
-
- arglist = Enum.map(call.fun.arguments, &(&1.name))
- arglistcl = [{:entity_key, [], nil}|arglist]
- argtypes = [entity_key_type|Enum.map(call.fun.arguments, &(&1.type))]
+ @type state :: any
- quote do
- unquote(call_doc)
- @spec unquote({name, [], argtypes}) :: unquote(call.ret)
- def unquote({name, [], arglistcl}) do
- GenServer.call(Lkn.Core.Name.component(unquote({:entity_key, [], nil}), unquote(module_name)),
- {:spec, {unquote(name), unquote(arglist)}})
- end
- end
- end
-
- defp cast_behaviour(cast) do
- name = cast.fun.name
- entity_key_type = quote do
- Lkn.Core.Entity.k
- end
-
- arglistcl = [{:::, [], [{:entity_key, [], nil}, entity_key_type]}
- |Enum.map(cast.fun.arguments, &({:::, [], [&1.name, &1.type]}))]
-
- quote do
- @callback unquote({name, [], arglistcl}) :: :ok
- end
- end
-
- defp call_behaviour(call) do
- name = call.fun.name
- entity_key_type = quote do
- Lkn.Core.Entity.k
- end
-
- arglistcl = [{:::, [], [{:entity_key, [], nil}, entity_key_type]}
- |Enum.map(call.fun.arguments, &({:::, [], [&1.name, &1.type]}))]
-
- quote do
- @callback unquote({name, [], arglistcl}) :: unquote(call.ret)
- end
- end
+ @callback init_state(Lkn.Core.Entity.k) :: {:ok, state} | :error
defmacro defcomponent(name, do: block) do
- block = case block do
- {:__block__, _, x} -> x
- x -> [x]
- end
-
- {casts, calls, legit} = Specs.parse_specs(block, [], [], [])
-
- casts_client = Enum.map(casts, &(cast_client(name, &1)))
- calls_client = Enum.map(calls, &(call_client(name, &1)))
-
- casts_behaviour = Enum.map(casts, &(cast_behaviour(&1)))
- calls_behaviour = Enum.map(calls, &(call_behaviour(&1)))
+ state_type = quote do Lkn.Core.Component.state end
+ key_type = quote do Lkn.Core.Component.k end
+ key_to_name = quote do
+ &(Lkn.Core.Name.component(&1, unquote(name)))
+ end
quote do
defmodule unquote(name) do
- unquote(legit)
- unquote(casts_client)
- unquote(calls_client)
- unquote(casts_behaviour)
- unquote(calls_behaviour)
+ unquote(Specs.gen_server_from_specs(block, key_type, key_to_name, state_type))
def system do
@system
@@ -126,7 +39,20 @@ defmodule Lkn.Core.Component do
defmacro __using__(_) do
quote do
+ defmodule State do
+ defstruct [
+ :entity_key,
+ :state,
+ ]
+
+ @type t :: %State{
+ entity_key: Lkn.Core.Entity.k,
+ state: Lkn.Core.Component.state,
+ }
+ end
+
@behaviour unquote(__MODULE__)
+ @behaviour Lkn.Core.Component
use GenServer
@@ -143,6 +69,11 @@ defmodule Lkn.Core.Component do
GenServer.start_link(__MODULE__, entity_key, name: Name.component(entity_key, unquote(__MODULE__)))
end
+ def init(entity_key) do
+ {:ok, s} = init_state(entity_key)
+ {:ok, %State{entity_key: entity_key, state: s}}
+ end
+
@spec read(Entity.k, Properties.prop) :: Option.t(Properties.value)
defp read(entity_key, p) do
Properties.read(entity_key, p)
@@ -153,16 +84,16 @@ defmodule Lkn.Core.Component do
Properties.write(entity_key, p, v)
end
- def handle_cast({:spec, {name, args}}, entity_key) do
- :erlang.apply(__MODULE__, name, [entity_key|args])
+ def handle_cast({:spec, {name, args}}, state) do
+ s = :erlang.apply(__MODULE__, name, [state.entity_key|args] ++ [state.state])
- {:noreply, entity_key}
+ {:noreply, %State{state|state: s}}
end
- def handle_call({:spec, {name, args}}, _call, entity_key) do
- res = :erlang.apply(__MODULE__, name, [entity_key|args])
+ def handle_call({:spec, {name, args}}, _call, state) do
+ {res, s} = :erlang.apply(__MODULE__, name, [state.entity_key|args] ++ [state.state])
- {:reply, res, entity_key}
+ {:reply, res, %State{state|state: s}}
end
end
end