defmodule CodeNameRaven.HandlerDisplay do @moduledoc """ Behaviour for alert handler display modules. Renders a handler's own dashboard panel (e.g. a firing timeline). Paired with a `CodeNameRaven.Handler` module the same way `CodeNameRaven.Display` pairs with `CodeNameRaven.Monitor` — self-bundled in one module, or as a separate package-sibling module declaring `compatible_handlers/0`. `display_name/0` is shared with `CodeNameRaven.Handler`'s own callback of the same name — combining both behaviours in a single (self-bundled) module means whichever `use` runs last wins, the same "conflicting behaviours" compiler warning `CodeNameRaven.Monitor`/`CodeNameRaven.Display` would hit if combined without their `display_params_*` naming split. No module combines Handler and HandlerDisplay today, so this hasn't been disambiguated — worth revisiting if that ever changes in practice. """ @callback display_name() :: String.t() @callback size_hint() :: :small | :medium | :large | :full @callback compatible_handlers() :: [module()] @callback prepare_assigns(map()) :: map() @callback render(map()) :: Phoenix.LiveView.Rendered.t() defmacro __using__(opts) do quote do @behaviour CodeNameRaven.HandlerDisplay use Phoenix.Component @impl true def display_name do __MODULE__ |> Module.split() |> List.last() |> Macro.underscore() |> String.replace("_", " ") |> String.split() |> Enum.map_join(" ", &String.capitalize/1) end @impl true def size_hint, do: unquote(opts[:size_hint] || :medium) @impl true def prepare_assigns(assigns), do: assigns defoverridable display_name: 0, size_hint: 0, prepare_assigns: 1 end end @doc "Returns true if the module implements the HandlerDisplay behaviour." def handler_display_module?(module) do Code.ensure_loaded?(module) and function_exported?(module, :compatible_handlers, 0) and function_exported?(module, :render, 1) end end