Files
parrhesia/lib/parrhesia/storage.ex

68 lines
2.1 KiB
Elixir

defmodule Parrhesia.Storage do
@moduledoc """
Storage boundary entrypoint.
Domain/runtime code should resolve behavior modules through this module instead of
depending on concrete adapter implementations directly.
"""
@default_modules [
events: Parrhesia.Storage.Adapters.Postgres.Events,
acl: Parrhesia.Storage.Adapters.Postgres.ACL,
moderation: Parrhesia.Storage.Adapters.Postgres.Moderation,
groups: Parrhesia.Storage.Adapters.Postgres.Groups,
admin: Parrhesia.Storage.Adapters.Postgres.Admin
]
@spec events() :: module()
def events, do: fetch_module!(:events, Parrhesia.Storage.Events)
@spec moderation() :: module()
def moderation, do: fetch_module!(:moderation, Parrhesia.Storage.Moderation)
@spec acl() :: module()
def acl, do: fetch_module!(:acl, Parrhesia.Storage.ACL)
@spec groups() :: module()
def groups, do: fetch_module!(:groups, Parrhesia.Storage.Groups)
@spec admin() :: module()
def admin, do: fetch_module!(:admin, Parrhesia.Storage.Admin)
defp fetch_module!(key, behavior) do
module =
:parrhesia
|> Application.get_env(:storage, [])
|> Keyword.get(key, Keyword.fetch!(@default_modules, key))
ensure_behavior!(module, behavior, key)
end
defp ensure_behavior!(module, behavior, key) do
case Code.ensure_loaded(module) do
{:module, _loaded_module} ->
if implements_behavior?(module, behavior) do
module
else
raise ArgumentError,
"configured storage module #{inspect(module)} for #{inspect(key)} " <>
"does not implement #{inspect(behavior)}"
end
{:error, reason} ->
raise ArgumentError,
"configured storage module #{inspect(module)} for #{inspect(key)} could not be loaded: " <>
"#{inspect(reason)}"
end
end
defp implements_behavior?(module, behavior) do
module
|> module_info_attributes()
|> Keyword.get(:behaviour, [])
|> Enum.member?(behavior)
end
defp module_info_attributes(module), do: module.module_info(:attributes)
end