Current section
Files
Jump to
Current section
Files
lib/temporary_env.ex
defmodule TemporaryEnv do
@moduledoc """
Temporarily set Application environment variables.
Quite handy for testing, but remember to set `async: false` for tests suites
it is used in.
"""
defmodule RemovedAPI do
defexception [:message]
end
@default :temporary_env_default_value_for_unset_var
@doc false
defmacro __using__(_) do
quote do
require TemporaryEnv
test "TemporaryEnv test case async status", context do
refute context.async, """
TemporaryEnv cannot be used in async tests.
To make this test case synchronous, modify your use of
ExUnit.Case like so:
use ExUnit.Case, async: false
"""
end
end
end
@spec put(atom, atom, any, [{:do, any}]) :: any
@doc """
Temporarily set an Application environment value within a block.
TemporaryEnv.put :my_app, :greeting, "Hello!" do
# :greeting for :my_app is now "Hello!"
end
# :greeting for :my_app is back to its original value
This *does* modify global state, so do not use it in an async situation.
"""
defmacro put(app, key, value, do: block) do
quote do
# Get original state
temporary_env_original_value = Application.get_env(
unquote(app), unquote(key), unquote(@default)
)
try do
# Set temporary state
Application.put_env unquote(app), unquote(key), unquote(value)
unquote(block)
after
# Restore original state
case temporary_env_original_value do
# when the value was previously unset
unquote(@default) ->
Application.delete_env( unquote(app), unquote(key) )
# when the value was previously set
_ ->
Application.put_env(
unquote(app), unquote(key), temporary_env_original_value
)
end
end
end
end
@doc false
defmacro set(app, [{key, value}], do: _block) do
quote do
raise TemporaryEnv.RemovedAPI, """
The function TemporaryEnv.set/3 has been removed in favour of
TemporaryEnv.put/4. Please update your code like so:
TemporaryEnv.set :#{unquote(app)}, #{unquote(key)}: #{unquote(inspect(value))} do
# body...
end
becomes...
TemporaryEnv.put :#{unquote(app)}, :#{unquote(key)}, #{unquote(inspect(value))} do
# body...
end
Note the key and value to be set are not a keyword pair, but
instead two arguments, one after the other. This is to match
`Application.put_env/4` and `System.put_env/2`.
"""
end
end
@spec set(atom, atom, [{:do, any}]) :: any
@doc """
Temporarily unset an Application environment value within a block.
TemporaryEnv.delete :my_app, :greeting do
# :greeting for :my_app now has no value
end
# :greeting for :my_app is back to its original value
This *does* modify global state, so do not use it in an async situation.
"""
defmacro delete(app, key, do: block) when key |> is_atom do
quote do
# Get original state
unquote(app)
|> Application.get_env( unquote(key), unquote(@default) )
|> case do
# when the value was previously unset
unquote(@default) ->
unquote(block)
# when the value was previously set
val ->
try do
Application.delete_env unquote(app), unquote(key)
unquote(block)
after
Application.put_env unquote(app), unquote(key), val
end
end
end
end
end