Current section
Files
Jump to
Current section
Files
lib/skogsra.ex
defmodule Skogsra do
@moduledoc """
This module defines the macros needed to use `Skogsra` e.g:
```elixir
defmodule MyApp.Settings do
use Skogsra
@envdoc "My hostname"
app_env :my_hostname, :myapp, :hostname,
default: "localhost"
end
```
"""
alias Skogsra.Core
alias Skogsra.Docs
alias Skogsra.Env
@doc """
For now is just equivalent to use `import Skogsra`.
"""
defmacro __using__(_) do
quote do
import Skogsra
end
end
@doc """
Creates a function to retrieve specific environment/application variables
values.
The function created is named `function_name` and will get the value
associated with an application called `app_name` and one or several
`parameters` keys. Optionally, receives a list of `options`.
Options:
- `default` - Default value for the variable in case is not present.
- `type` - Type of the variable. Used for casting the value. By default,
casts the value to the same type of the default value. If the default value
is not present, defaults to `:binary`. The available values are: `:binary`,
`:integer`, `:float`, :boolean, `:atom`. Additionally, you can provide
`{module, function}` for custom types. The function must receive the
binary and return the custom type.
- `os_env` - Alias for the variable in the OS. If the alias is `nil` will
use the default name. This option is ignored if the option `skip_system` is
`true` (default is `false`).
- `namespace` - Namespace of the variable.
- `skip_system` - If `true`, doesn't look for the variable value in the
system. Defaults to `false`.
- `skip_config` - If `true`, doesn't look for the variable value in the
configuration. Defaults to `false`.
- `required` - Errors when the value is `nil`. Defaults to `false`.
- `cached` - Caches the value on the first read. Defaults to `true`.
e.g:
For the following declaration:
```
app_env :db_password, :myapp, [:mydb, :password],
default: "password",
```
will generate:
- `db_password/0` and `db_password/1` for getting the variable's value
without or with namespace respectively. It returns `:ok` and `:error`
tuples.
- `db_password!/0` and `db_password!/1` for getting the variable's value
without or with namespace respectively. It fails on error.
- `reload_db_password/0` and `reload_db_password/1` for reloading the
variable's value in the cache without or with namespace respectively.
- `put_db_password/1` and `put_db_password/2` for settings a new value for
the variable directly to the cache without or with namespace respectively.
A call to `db_password/0` will try to:
1. Look for the value of `$MYAPP_MYDB_PASSWORD` OS environment variable. If
it's `nil`, then it will try 2.
2. Look for the value in the configuration e.g:
```
config :myapp,
mydb: [password: "some password"]
```
If it's `nil`, then it will try 3.
3. Return the value of the default value or `nil`.
A call to `db_password/1` with namespace `Test` will try to:
1. Look for the value of `$TEST_MYAPP_MYDB_PASSWORD` OS environment variable.
If it's `nil`, then it will try 2.
2. Look for the value in the configuration e.g:
```
config :myapp, Test,
mydb: [password: "some password"]
```
If it's `nil`, then it will try 3.
3. Return the value of the default value or `nil`.
"""
defmacro app_env(function_name, app_name, keys, options \\ []) do
definition = String.to_atom("__#{function_name}__")
bang! = String.to_atom("#{function_name}!")
reload = String.to_atom("reload_#{function_name}")
put = String.to_atom("put_#{function_name}")
quote do
@doc false
@spec unquote(definition)() :: Env.t()
@spec unquote(definition)(namespace :: Env.namespace()) :: Env.t()
def unquote(definition)(namespace \\ nil) do
app_name = unquote(app_name)
keys = unquote(keys)
options = unquote(options)
Env.new(namespace, app_name, keys, options)
end
# Function to get the variable's value. Errors when is required and does
# not exist.
@doc Docs.gen_full_docs(
__MODULE__,
unquote(function_name),
unquote(app_name),
unquote(keys),
unquote(options),
Module.get_attribute(__MODULE__, :envdoc)
)
@spec unquote(function_name)() :: {:ok, term()} | {:error, term()}
@spec unquote(function_name)(Env.namespace()) ::
{:ok, term()} | {:error, term()}
def unquote(function_name)(namespace \\ nil)
def unquote(function_name)(namespace) do
env = unquote(definition)(namespace)
Core.get_env(env)
end
# Function to get the variable's value. Fails when is required and does
# not exist,
@doc Docs.gen_short_docs(
__MODULE__,
unquote(function_name),
Module.get_attribute(__MODULE__, :envdoc)
)
@spec unquote(bang!)() :: term() | no_return()
@spec unquote(bang!)(Env.namespace()) :: term() | no_return()
def unquote(bang!)(namespace \\ nil)
def unquote(bang!)(namespace) do
env = unquote(definition)(namespace)
Core.get_env!(env)
end
# Reloads the variable.
@doc Docs.gen_reload_docs(__MODULE__, unquote(function_name))
@spec unquote(reload)() :: {:ok, term()} | {:error, term()}
@spec unquote(reload)(Env.namespace()) ::
{:ok, term()} | {:error, term()}
def unquote(reload)(namespace \\ nil)
def unquote(reload)(namespace) do
env = unquote(definition)(namespace)
Core.reload_env(env)
end
# Puts a new value to a variable.
@doc Docs.gen_put_docs(__MODULE__, unquote(function_name))
@spec unquote(put)(term()) :: :ok | {:error, term()}
@spec unquote(put)(term(), Env.namespace()) :: :ok | {:error, term()}
def unquote(put)(value, namespace \\ nil)
def unquote(put)(value, namespace) do
env = unquote(definition)(namespace)
Core.put_env(env, value)
end
end
end
end