aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlthms <contact@thomasletan.fr>2017-09-22 15:07:48 +0000
committerThomas Letan <contact@thomasletan.fr>2018-01-24 08:10:43 +0100
commit1c9fdbfc0e77efd8c27a99676ce9de83db30105d (patch)
tree0b14934227ca5ad832401d81619a321b592d2ebd
parentinstance: Remove some private functions of Instance (diff)
puppeteer: Allow a Puppeteer impl to expose its own cast functions
Before, a given Puppeteer implementation could only expose the functions of its Specifictaion. It was quite limited and prevented several scenario where a Puppeteer could be influenced by some master, e.g. a Player Puppeteer by network events, an Instance logger by the Instance itself, etc. This patch addresses this limitation.
-rw-r--r--lib/lkn/core/puppeteer.ex21
-rw-r--r--lib/lkn/core/specs.ex36
-rw-r--r--test/core_test.exs27
3 files changed, 75 insertions, 9 deletions
diff --git a/lib/lkn/core/puppeteer.ex b/lib/lkn/core/puppeteer.ex
index a164e1a..521e256 100644
--- a/lib/lkn/core/puppeteer.ex
+++ b/lib/lkn/core/puppeteer.ex
@@ -43,7 +43,18 @@ defmodule Lkn.Core.Puppeteer do
defmodule unquote(name) do
unquote(Specs.gen_server_from_specs(block, key_type, key_to_name, state_type))
- defmacro __using__(_) do
+ defmacro __using__(args) do
+ plugin_clients = case args do
+ [do: use_block] ->
+ Specs.gen_server_plugin_entry_point(use_block,
+ quote do Lkn.Core.Puppeteer.k end,
+ &(Lkn.Core.Name.puppeteer(&1)),
+ quote do Lkn.Core.Puppeteer.state end)
+ _ ->
+ quote do
+ end
+ end
+
quote do
@behaviour unquote(__MODULE__)
@behaviour Lkn.Core.Puppeteer
@@ -102,6 +113,12 @@ defmodule Lkn.Core.Puppeteer do
{:noreply, %State{state|state: s}}
end
+ def handle_cast({:plugin, {name, args}}, state) do
+ name = String.to_atom(String.replace_suffix(Atom.to_string(name), "", "_plugin"))
+ s = :erlang.apply(__MODULE__, name, [state.puppeteer_key|args]++[state.state])
+
+ {:noreply, %State{state|state: s}}
+ end
def handle_call({:find_instance, map_key}, _from, state) do
instance_key = Lkn.Core.Pool.register_puppeteer(map_key, state.puppeteer_key, __MODULE__)
@@ -115,6 +132,8 @@ defmodule Lkn.Core.Puppeteer do
{:reply, res, %State{state|state: s}}
end
+
+ unquote(plugin_clients)
end
end
end
diff --git a/lib/lkn/core/specs.ex b/lib/lkn/core/specs.ex
index 9c3b305..99ac28a 100644
--- a/lib/lkn/core/specs.ex
+++ b/lib/lkn/core/specs.ex
@@ -16,6 +16,27 @@ defmodule Lkn.Core.Specs do
end))
end
+ def gen_server_plugin_entry_point(block, key_type, key_to_name, state_type) do
+ block = case block do
+ {:__block__, _, x} -> x
+ x -> [x]
+ end
+
+ {casts, calls, legit} = parse_specs(block, [], [], [])
+
+ check_specs(casts, calls, legit, [allow_impl: true, allow_specs: false])
+
+ plugin = quote do :plugin end
+
+ casts_client = Enum.map(casts, &(cast_client(plugin, var_name("key"), key_type, key_to_name, &1)))
+ casts_behaviour = Enum.map(casts, &(cast_server(&1, var_name("key"), key_type, [], state_type, "_plugin")))
+
+ quote do
+ unquote(casts_client)
+ unquote(casts_behaviour)
+ end
+ end
+
def gen_server_from_specs(block, key_type, key_to_name, state_type, keywords \\ []) do
block = case block do
{:__block__, _, x} -> x
@@ -30,8 +51,10 @@ defmodule Lkn.Core.Specs do
key_name = Keyword.get(keywords, :key_name, var_name("key"))
additional_args = Keyword.get(keywords, :additional_args, [])
- casts_client = Enum.map(casts, &(cast_client(key_name, key_type, key_to_name, &1)))
- calls_client = Enum.map(calls, &(call_client(key_name, key_type, key_to_name, &1)))
+ spec = quote do :spec end
+
+ casts_client = Enum.map(casts, &(cast_client(spec, key_name, key_type, key_to_name, &1)))
+ calls_client = Enum.map(calls, &(call_client(spec, key_name, key_type, key_to_name, &1)))
casts_behaviour = Enum.map(casts, &(cast_server(&1, key_name, key_type, additional_args, state_type, impl_suffix)))
calls_behaviour = Enum.map(calls, &(call_behaviour(&1, key_name, key_type, additional_args, state_type)))
@@ -45,7 +68,7 @@ defmodule Lkn.Core.Specs do
end
end
- defp cast_client(key_name, key_type, key_to_name, cast) do
+ defp cast_client(namespace, key_name, key_type, key_to_name, cast) do
cast = case cast do
{cast, _} -> cast
cast -> cast
@@ -68,12 +91,12 @@ defmodule Lkn.Core.Specs do
@spec unquote({name, [], argtypes}) :: :ok
def unquote({name, [], arglistcl}) do
GenServer.cast(unquote(key_to_name).(unquote(key_name)),
- {:spec, {unquote(name), unquote(arglist)}})
+ {unquote(namespace), {unquote(name), unquote(arglist)}})
end
end
end
- defp call_client(key_name, key_type, key_to_name, call) do
+ defp call_client(namespace, key_name, key_type, key_to_name, call) do
name = call.fun.name
call_doc = if call.doc != :none do
@@ -91,7 +114,7 @@ defmodule Lkn.Core.Specs do
@spec unquote({name, [], argtypes}) :: unquote(call.ret)
def unquote({name, [], arglistcl}) do
GenServer.call(unquote(key_to_name).(unquote(key_name)),
- {:spec, {unquote(name), unquote(arglist)}})
+ {unquote(namespace), {unquote(name), unquote(arglist)}})
end
end
end
@@ -145,7 +168,6 @@ defmodule Lkn.Core.Specs do
end
end
-
defp parse_specs(block, casts, calls, legit) do
case block do
[{:@, _, [{:doc, _, [docstring]}]},
diff --git a/test/core_test.exs b/test/core_test.exs
index 286a7cf..50039a0 100644
--- a/test/core_test.exs
+++ b/test/core_test.exs
@@ -68,6 +68,24 @@ defmodule Lkn.Core.Test do
Option.some(4) = Lkn.Core.Entity.read(entity_key, :level)
end
+ test "spawning puppeteer and testing its private cast" do
+ puppeteer_key = UUID.uuid4()
+
+ {:ok, _} = Test.Puppeteer.start_link(puppeteer_key)
+
+ Test.Puppeteer.wizz(puppeteer_key, 2)
+
+ receive do
+ {:wizz, n, x} -> assert x == puppeteer_key && n == 0
+ after 100 -> raise "Waiting for 100ms the message {:wizz, 0, #{inspect puppeteer_key}}"
+ end
+
+ receive do
+ {:wizz, n, x} -> assert x == puppeteer_key && n == 1
+ after 100 -> raise "Waiting for 100ms the message {:wizz, 1, #{inspect puppeteer_key}}"
+ end
+ end
+
test "spawning everything" do
map_key = UUID.uuid4()
puppeteer_key = UUID.uuid4()
@@ -415,7 +433,14 @@ defpuppeteer Test.Puppeteer.Specs do
end
defmodule Test.Puppeteer do
- use Test.Puppeteer.Specs, state: pid()
+ use Test.Puppeteer.Specs do
+ cast wizz(n :: number) do
+ for i <- 0..(n-1) do
+ send state, {:wizz, i, key}
+ end
+ state
+ end
+ end
def start_link(puppeteer_key) do
Puppeteer.start_link(__MODULE__, puppeteer_key, self())