Current section

Files

Jump to
mouth lib messenger.ex
Raw

lib/messenger.ex

defmodule Mouth.Messenger do
@moduledoc """
"""
@cannot_call_directly_error """
cannot call Mouth.Messenger directly. Instead implement your own Messenger module
with: use Mouth.Messenger, otp_app: :my_app
"""
require Logger
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
@behaviour Mouth.Messenger
@spec deliver(Mouth.Message.t) :: Mouth.Message.t
def deliver(message) do
config = build_config()
Mouth.Messenger.deliver(message, config)
end
def status(id) do
config = build_config()
Mouth.Messenger.status(id, config)
end
otp_app = Keyword.fetch!(opts, :otp_app)
defp build_config, do: Mouth.Messenger.build_config(__MODULE__, unquote(otp_app))
end
end
@optional_callbacks init: 0
@callback init() :: {:ok, Keyword.t} | :ignore
@doc false
def deliver(_message) do
raise @cannot_call_directly_error
end
def status(_) do
raise @cannot_call_directly_error
end
@doc false
def deliver(message, config) do
message = validate_and_normalize(message, config.adapter)
result =
if message.to == [] do
debug_unsent(message)
{:error, "Empty recipient"}
else
debug_sent(message, config.adapter)
config.adapter.deliver(message, config)
end
result
end
@doc false
def status(id, config) do
config.adapter.status(id, config)
end
defp debug_sent(message, adapter) do
Logger.debug """
Sending message with #{inspect adapter}:
#{inspect message, limit: :infinity}
"""
end
defp debug_unsent(message) do
Logger.debug """
Message was not sent because recipient is empty.
Full message - #{inspect message, limit: :infinity}
"""
end
defp validate_and_normalize(message, adapter) do
validate(message, adapter)
end
defp validate(message, _adapter) do
validate_recipients(message)
end
defp validate_recipients(%Mouth.Message{} = message) do
if Enum.all?(
Enum.map([:to], &Map.get(message, &1)),
&nil_recipient?/1
) do
raise Mouth.NilRecipientsError, message
else
message
end
end
defp nil_recipient?(nil), do: true
defp nil_recipient?([]), do: false
defp nil_recipient?([_] = recipients) do
Enum.all?(recipients, &nil_recipient?/1)
end
defp nil_recipient?(_), do: false
def build_config(messenger, otp_app) do
otp_app
|> get_application_config(messenger)
|> Map.new
|> handle_adapter_config
end
defp handle_adapter_config(base_config = %{adapter: adapter}) do
adapter.handle_config(base_config)
end
defp get_application_config(otp_app, messenger) do
{:ok, config} =
if Code.ensure_loaded?(messenger) and function_exported?(messenger, :init, 0) do
messenger.init
else
{:ok, Application.get_env(otp_app, messenger)}
end
config
end
end