Current section

Files

Jump to
agala lib bot supervisor.ex
Raw

lib/bot/supervisor.ex

defmodule Agala.Bot.Supervisor do
use Supervisor
# These default can be filled by some default parameters in the future
@defaults []
@doc """
Retrieves the compile time configuration for the bot.
"""
@spec compile_config(:poller | :plug | :handler, bot :: Agala.Bot.t(), opts :: Keyword.t()) ::
{atom, Agala.Provider.t(), Keyword.t()}
def compile_config(:poller, bot, opts) do
otp_app = Keyword.fetch!(opts, :otp_app)
config = Application.get_env(otp_app, bot, [])
config = Keyword.merge(opts, config)
# Provider checking section
provider = opts[:provider] || config[:provider]
unless provider do
raise ArgumentError,
"missing :provider configuration in " <> "config #{inspect(otp_app)}, #{inspect(bot)}"
end
unless Code.ensure_compiled?(provider) do
raise ArgumentError,
"provider #{inspect(provider)} was not compiled, " <>
"ensure it is correct and it is included as a project dependency"
end
# Handler checking section
chain = opts[:chain] || config[:chain]
unless chain do
raise ArgumentError,
"missing :chain configuration in " <> "config #{inspect(otp_app)}, #{inspect(bot)}"
end
unless Code.ensure_compiled?(chain) do
raise ArgumentError,
"chain #{inspect(chain)} was not compiled, " <>
"ensure it is correct and it is included as a module in the project"
end
unless Agala.Chain in Agala.Util.behaviours_list(chain) do
raise ArgumentError,
"chain #{inspect(chain)} does not implement Agala.Chain behaviour, " <>
"ensure it is correct and it is included as a module in the project"
end
{otp_app, provider, config}
end
@doc """
Retrieves the runtime configuration for the bot.
"""
@spec runtime_config(
mode :: :poller | :plug | :handler,
type :: :dry_run | :supervisor,
bot :: Agala.Bot.t(),
otp_app :: atom(),
opts :: Keyword.t()
) :: {:ok, Keyword.t()} | :ignore
def runtime_config(:poller, type, bot, otp_app, opts) do
if config = Application.get_env(otp_app, bot, []) do
config =
[otp_app: otp_app, bot: bot]
|> Keyword.merge(@defaults)
|> Keyword.merge(config)
|> Enum.into(%{})
|> Map.merge(opts)
case bot_init(type, bot, config) do
{:ok, config} -> {:ok, config}
:ignore -> :ignore
end
else
raise ArgumentError,
"configuration for #{inspect(bot)} not specified in #{inspect(otp_app)} environment"
end
end
### Start section
@spec start_link(
mode :: :poller | :plug | :handler,
bot :: Agala.Bot.t(),
otp_app :: atom(),
provider :: Agala.Provider.t(),
opts :: Keyword.t()
) :: Supervisor.on_start()
def start_link(:plug, bot, _, _, _) do
raise RuntimeError,
"Plugs should not be started as supervisors. Check out your #{inspect bot} plug"
end
def start_link(mode, bot, otp_app, provider, opts) do
IO.inspect "Opts coming: #{inspect opts}"
Supervisor.start_link(__MODULE__, {mode, bot, otp_app, provider, opts}, [name: bot])
end
@spec bot_init(type :: :dry_run | :supervisor, bot :: Agala.Bot.t(), config :: Keyword.t()) ::
{:ok, Keyword.t()} | :ignore
defp bot_init(type, bot, config) do
if Code.ensure_loaded?(bot) and function_exported?(bot, :init, 2) do
bot.init(type, config)
else
{:ok, config}
end
end
## Callbacks
@spec init({
mode :: :poller, :plug, :handler,
bot :: Agala.Bot.t(),
otp_app :: atom(),
provider :: Agala.Provider.t(),
opts :: Keyword.t()
}) :: {:ok, tuple()} | :ignore
def init({:poller, bot, otp_app, provider, opts}) do
case runtime_config(:poller, :supervisor, bot, otp_app, opts) do
{:ok, opts} ->
children = [
{Agala.Bot.Storage, name: Module.concat(bot, Storage)},
{provider.get_bot(:poller), opts}
]
Supervisor.init(children, strategy: :one_for_one)
:ignore ->
:ignore
end
end
end