Current section
Files
Jump to
Current section
Files
lib/loader.ex
defmodule Weave.Loader do
@moduledoc """
This behaviour should be implemented by all loaders. This will be handled for you with:
```elixir
use Weave.Loader
```
"""
@callback load_configuration(atom()) :: {:ok, list()}
defmacro __using__(_) do
quote do
require Logger
@behaviour Weave.Loader
@spec apply_configuration(binary(), binary(), atom()) :: :ok
@spec configure({atom(), atom(), any()}) :: :ok
@spec configure([{atom(), atom(), any()}]) :: :ok | []
@spec merge(any(), any()) :: list() | map() | binary()
@spec handle_configuration(binary(), binary()) :: :ok
@spec sanitize(binary()) :: binary()
defp apply_configuration(name, contents, handler) do
name
|> sanitize()
|> handler.handle_configuration(contents)
|> configure()
:ok
rescue
error in [UndefinedFunctionError, FunctionClauseError] ->
Logger.info(fn -> inspect(error) end)
handle_configuration(name, contents)
end
# Allow handlers to return :ok
defp configure(:ok) do
:ok
end
# We're transforming this to a List, as I believe we'll
# eventually enforce all handle_configuration/1's return List
defp configure({app, key, value}) do
configure([{app, key, value}])
end
defp configure([{app, key, value} | tail]) do
Application.put_env(
app,
key,
merge(Application.get_env(app, key), value)
)
Logger.debug(fn ->
"Configuration for #{app}:#{key} loaded: " <>
"#{inspect(Application.get_env(app, key))}"
end)
configure(tail)
end
defp configure([]) do
:ok
end
defp merge(nil, new) when is_list(new) do
merge([], new)
end
defp merge(old, new) when is_list(new) do
if Keyword.keyword?(new) do
Logger.debug(fn -> "Merging keywords: #{inspect(old)} and #{inspect(new)}" end)
Keyword.merge(old, new)
else
Logger.debug(fn -> "Merging lists #{inspect(old)} and #{inspect(new)}" end)
new ++ old
end
end
defp merge(nil, new) when is_map(new) do
merge(%{}, new)
end
defp merge(old, new) when is_map(new) do
Map.merge(old, new)
end
defp merge(old, new) when is_binary(new) do
new
end
defp merge(old, new) when is_atom(new) do
new
end
defp handle_configuration(parameter_name, "{:auto," <> configuration) do
{{app, key, value}, []} = Code.eval_string(~s/{#{configuration}/)
Application.put_env(
app,
key,
merge(Application.get_env(app, key), value)
)
:ok
end
defp handle_configuration(file_name, configuration) do
Logger.warn(fn -> "External configuration (#{file_name}) provided, but not loaded" end)
Logger.debug(fn ->
"configuration value for ignored '#{file_name}' is '#{configuration}'"
end)
:ok
end
defp sanitize(name) do
String.downcase(name)
end
end
end
end