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() :: {: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