From 23565db8334d23f10a05c7db33838c50fefe1764 Mon Sep 17 00:00:00 2001 From: Michael Ries Date: Sat, 19 Oct 2024 13:35:36 +0900 Subject: [PATCH] introduce the Gnat.Supervisor macro --- config/config.exs | 8 ++++ lib/gnat/connection_supervisor.ex | 4 +- lib/gnat/supervisor.ex | 72 +++++++++++++++++++++++++++++++ test/gnat/supervisor_test.exs | 24 +++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 config/config.exs create mode 100644 lib/gnat/supervisor.ex create mode 100644 test/gnat/supervisor_test.exs diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..91b175f --- /dev/null +++ b/config/config.exs @@ -0,0 +1,8 @@ +import Config + +if Mix.env() == :test do + config :gnat, Gnat.SupervisorTest.MyApp.Gnat, + connection_settings: [ + %{} + ] +end diff --git a/lib/gnat/connection_supervisor.ex b/lib/gnat/connection_supervisor.ex index 9940bc2..072359d 100644 --- a/lib/gnat/connection_supervisor.ex +++ b/lib/gnat/connection_supervisor.ex @@ -13,8 +13,8 @@ defmodule Gnat.ConnectionSupervisor do name: :gnat, # (required) the registered named you want to give the Gnat connection backoff_period: 4_000, # number of milliseconds to wait between consecutive reconnect attempts (default: 2_000) connection_settings: [ - %{host: '10.0.0.100', port: 4222}, - %{host: '10.0.0.101', port: 4222}, + %{host: ~c"10.0.0.100", port: 4222}, + %{host: ~c"10.0.0.101", port: 4222}, ] } ``` diff --git a/lib/gnat/supervisor.ex b/lib/gnat/supervisor.ex new file mode 100644 index 0000000..161d844 --- /dev/null +++ b/lib/gnat/supervisor.ex @@ -0,0 +1,72 @@ +defmodule Gnat.Supervisor do + defmacro __using__(opts) do + quote do + @mod __MODULE__ + + use Supervisor + + def start_link(init_arg) do + Supervisor.start_link(__MODULE__, init_arg) + end + + @impl true + def init(_init_arg) do + otp_app = unquote(opts)[:otp_app] || raise ArgumentError, "otp_app option is required" + config = Application.get_env(otp_app, @mod, []) + + children = [ + {Gnat.ConnectionSupervisor, connection_opts(config)}, + consumer_supervisor(config) + ] + |> List.flatten() + + Supervisor.init(children, strategy: :one_for_one) + end + + defp connection_opts(config) do + %{ + name: @mod, + connection_settings: Keyword.get(config, :connection_settings) + } + end + + defp consumer_supervisor(config) do + case Keyword.get(config, :topics) do + nil -> [] + topics when is_list(topics) -> [{Gnat.ConsumerSupervisor, topics}] + _ -> raise ArgumentError, "Invalid :topics option. Expected a list of topics." + end + end + + ## Functions that forward to Gnat connection + + def active_subscriptions() do + Gnat.active_subscriptions(@mod) + end + + def pub(topic, message, opts \\ []) do + Gnat.pub(@mod, topic, message, opts) + end + + def request(topic, body, opts \\ []) do + Gnat.request(@mod, topic, body, opts) + end + + def request_multi(topic, body, opts \\ []) do + Gnat.request_multi(@mod, topic, body, opts) + end + + def server_info() do + Gnat.server_info(@mod) + end + + def sub(subscriber, topic, opts \\ []) do + Gnat.sub(@mod, subscriber, topic, opts) + end + + def unsub(sid, opts \\ []) do + Gnat.unsub(@mod, sid, opts) + end + end + end +end diff --git a/test/gnat/supervisor_test.exs b/test/gnat/supervisor_test.exs new file mode 100644 index 0000000..55c935e --- /dev/null +++ b/test/gnat/supervisor_test.exs @@ -0,0 +1,24 @@ +defmodule Gnat.SupervisorTest do + use ExUnit.Case, async: true + + defmodule MyApp.Gnat do + use Gnat.Supervisor, otp_app: :gnat + end + + test "it can be supervised" do + assert MyApp.Gnat.child_spec([]) == %{ + id: MyApp.Gnat, + start: {MyApp.Gnat, :start_link, [[]]}, + type: :supervisor + } + end + + test "when started - it provides an API that does not require a Gnat connection arg" do + {:ok, _pid} = MyApp.Gnat.start_link([]) + :timer.sleep(100) + {:ok, _sub} = MyApp.Gnat.sub(self(), "my_app.topic") + :ok = MyApp.Gnat.pub("my_app.topic", "ohai") + assert_receive {:msg, %{topic: "my_app.topic", body: "ohai"}} + assert Supervisor.stop(MyApp.Gnat) == :ok + end +end