From 391726003009f283c6b6d97e2b95587c10be4f71 Mon Sep 17 00:00:00 2001 From: sloane <1699281+sloanelybutsurely@users.noreply.github.com> Date: Thu, 13 Jun 2024 17:22:59 -0400 Subject: [PATCH] feat: initial implementation --- lib/init.ex | 17 +++++++++++++ test/init_test.exs | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 test/init_test.exs diff --git a/lib/init.ex b/lib/init.ex index fcb7baa..53a4957 100644 --- a/lib/init.ex +++ b/lib/init.ex @@ -1,2 +1,19 @@ defmodule Init do + @moduledoc """ + Run a function synchronously as part of a `Supervisor` + """ + use GenServer, restart: :transient + + def start_link(arg, opts \\ []) do + GenServer.start_link(__MODULE__, mfa!(arg), opts) + end + + @impl true + def init({mod, fun, args}) do + apply(mod, fun, args) + :ignore + end + + defp mfa!({_m, _f, _a} = mfa), do: mfa + defp mfa!(fun) when is_function(fun, 0), do: {Kernel, :apply, [fun, []]} end diff --git a/test/init_test.exs b/test/init_test.exs new file mode 100644 index 0000000..1894aee --- /dev/null +++ b/test/init_test.exs @@ -0,0 +1,60 @@ +defmodule Echo do + def ping(pid, value, delay) do + Process.sleep(delay) + send(pid, value) + end +end + +defmodule InitTest do + use ExUnit.Case, async: true + + describe "when passed a module-function-arguments tuple" do + test "it runs to completion before other children of a Supervisor" do + children = [ + Supervisor.child_spec({Init, {Echo, :ping, [self(), :foo, 20]}}, id: {Init, 1}), + Supervisor.child_spec({Init, {Echo, :ping, [self(), :bar, 10]}}, id: {Init, 2}) + ] + + start_supervised!(%{ + id: Supervisor, + start: {Supervisor, :start_link, [children, [strategy: :one_for_one]]} + }) + + assert_receive :foo + assert_receive :bar + end + end + + describe "when passed a 0-arity anonymous function" do + test "it runs to completion before other children of a Supervisor" do + self = self() + + children = [ + Supervisor.child_spec( + {Init, + fn -> + Process.sleep(20) + send(self, :foo) + end}, + id: {Init, 1} + ), + Supervisor.child_spec( + {Init, + fn -> + Process.sleep(10) + send(self, :bar) + end}, + id: {Init, 2} + ) + ] + + start_supervised!(%{ + id: Supervisor, + start: {Supervisor, :start_link, [children, [strategy: :one_for_one]]} + }) + + assert_receive :foo + assert_receive :bar + end + end +end