223 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
defmodule Envar do
 | 
						|
  @moduledoc """
 | 
						|
  Docs for `Envar`; Environment Variable checker/getter.
 | 
						|
  Variable names are logged to improve developer experience.
 | 
						|
  The _values_ of Environment Variables should _never_ be logged here.
 | 
						|
  If an App needs to debug a variable, it can log it locally.
 | 
						|
 | 
						|
  """
 | 
						|
 | 
						|
  require Logger
 | 
						|
 | 
						|
  @doc """
 | 
						|
  `get/2` gets an environment variable by name
 | 
						|
  with an _optional_ second argument `default_value`
 | 
						|
  which, as it's name suggests, defines the default value
 | 
						|
  for the evironment variable if it is not set.
 | 
						|
 | 
						|
  ## Examples
 | 
						|
 | 
						|
      iex> System.put_env("HELLO", "world")
 | 
						|
      iex> Envar.get("HELLO")
 | 
						|
      "world"
 | 
						|
 | 
						|
      iex> Envar.get("FOO", "bar")
 | 
						|
      "bar"
 | 
						|
  """
 | 
						|
  @spec get(binary, binary | nil) :: binary | nil
 | 
						|
  def get(varname, default \\ nil) do
 | 
						|
    val = System.get_env(varname, default)
 | 
						|
 | 
						|
    if is_nil(val) do
 | 
						|
      Logger.error("ERROR: #{varname} Environment Variable is not set")
 | 
						|
    end
 | 
						|
 | 
						|
    val
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  `is_set/1` binary check that an environment variable is defined by name
 | 
						|
  e.g: `Envar.is_set?("HEROKU")` will return `false`
 | 
						|
  if the `HEROKU` environment variable is not set.
 | 
						|
  When a particular variable is set, it will return `true`.
 | 
						|
 | 
						|
  ## Examples
 | 
						|
      iex> Envar.is_set?("HEROKU")
 | 
						|
      false
 | 
						|
 | 
						|
      iex> System.put_env("HELLO", "world")
 | 
						|
      iex> Envar.is_set?("HELLO")
 | 
						|
      true
 | 
						|
 | 
						|
  """
 | 
						|
  @spec is_set?(binary) :: boolean
 | 
						|
  def is_set?(varname) do
 | 
						|
    case System.get_env(varname) do
 | 
						|
      nil ->
 | 
						|
        Logger.debug("#{varname} Environment Variable is not set")
 | 
						|
        false
 | 
						|
 | 
						|
      _ ->
 | 
						|
        true
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  `is_set_all/1` binary check that ***ALL***
 | 
						|
  environment variable in a `List` are defined.
 | 
						|
  e.g: `Envar.is_set_all?(~w/HEROKU FLYIO/)` will return `false`
 | 
						|
  if _both_ the `HEROKU` and `FLYIO` environment variables are _not_ set.
 | 
						|
  When _all_ of the environment variables in the list are set,
 | 
						|
  it will return `true`.
 | 
						|
  It's the equivalent of writing:
 | 
						|
  `Envar.is_set?("HEROKU") && Envar.is_set?("FLYIO")`.
 | 
						|
 | 
						|
  ## Examples
 | 
						|
      iex> Envar.is_set_all?(["HEROKU", "AWS"])
 | 
						|
      false
 | 
						|
 | 
						|
      iex> Envar.set("HELLO", "world")
 | 
						|
      iex> Envar.set("GOODBYE", "au revoir")
 | 
						|
      iex> Envar.is_set_all?(["HELLO",  "GOODBYE"])
 | 
						|
      true
 | 
						|
 | 
						|
  """
 | 
						|
  @spec is_set_all?(list) :: boolean
 | 
						|
  def is_set_all?(list) do
 | 
						|
    Enum.all?(list, fn var -> is_set?(var) end)
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  `is_set_any/1` binary check that any
 | 
						|
  environment variable in a `List` is defined.
 | 
						|
  e.g: `Envar.is_set_any?(["HEROKU", "FLYIO"])` will return `false`
 | 
						|
  if _both_ the `HEROKU` and `FLYIO` environment variables are _not_ set.
 | 
						|
  When any of the environment variables in the list are set,
 | 
						|
  it will return `true`.
 | 
						|
  It's the equivalent of writing:
 | 
						|
  `Envar.is_set?("HEROKU") || Envar.is_set?("FLYIO")`.
 | 
						|
 | 
						|
  ## Examples
 | 
						|
      iex> Envar.is_set_any?(["HEROKU", "AWS"])
 | 
						|
      false
 | 
						|
 | 
						|
      iex> System.put_env("HELLO", "world")
 | 
						|
      iex> Envar.is_set_any?(["HELLO",  "GOODBYE"])
 | 
						|
      true
 | 
						|
 | 
						|
  """
 | 
						|
  @spec is_set_any?(list) :: boolean
 | 
						|
  def is_set_any?(list) do
 | 
						|
    Enum.any?(list, fn var -> is_set?(var) end)
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  `set/2` set the `value` of an environment variable `varname`.
 | 
						|
  Accepts two `String` parameters: `varname` and `value`.
 | 
						|
 | 
						|
  ## Examples
 | 
						|
      iex> Envar.set("API_KEY", "YourSuperLongAPIKey")
 | 
						|
      :ok
 | 
						|
 | 
						|
  """
 | 
						|
  @spec set(binary, binary) :: :ok
 | 
						|
  def set(varname, value) do
 | 
						|
    System.put_env(varname, value)
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  `load/1` load a file containing a line-separated list
 | 
						|
  of environment variables e.g: `.env`
 | 
						|
  Set the `value` of each environment variable.
 | 
						|
 | 
						|
  ## Examples
 | 
						|
      iex> Envar.load(".env")
 | 
						|
      :ok
 | 
						|
 | 
						|
  """
 | 
						|
  @spec load(binary) :: :ok
 | 
						|
  def load(filename) do
 | 
						|
    read(filename) |> Enum.each(fn {k, v} -> set(k, v) end)
 | 
						|
 | 
						|
    :ok
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  `require_env_file/1` load a file containing a line-separated list
 | 
						|
  of environment variables e.g: `.env`
 | 
						|
  Set the `value` of each environment variable.
 | 
						|
  Log an Error if the file is not available
 | 
						|
 | 
						|
  ## Examples
 | 
						|
      iex> Envar.require_env_file(".env")
 | 
						|
      :ok
 | 
						|
 | 
						|
      iex> Envar.require_env_file(".env_not_there")
 | 
						|
      :error
 | 
						|
 | 
						|
  """
 | 
						|
  @spec require_env_file(binary) :: :ok
 | 
						|
  def require_env_file(filename) do
 | 
						|
    # check if the file exists:
 | 
						|
    path = Path.join(File.cwd!(), filename)
 | 
						|
    case File.exists?(path) do
 | 
						|
      true ->
 | 
						|
        load(filename)
 | 
						|
      false ->
 | 
						|
        Logger.error("Required .env file does not exist at path: #{path}")
 | 
						|
        :error
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  @spec keys(binary) :: list
 | 
						|
  def keys(filename) do
 | 
						|
    read(filename) |> Map.keys()
 | 
						|
  end
 | 
						|
 | 
						|
  @spec values(binary) :: list
 | 
						|
  def values(filename) do
 | 
						|
    read(filename) |> Map.values()
 | 
						|
  end
 | 
						|
 | 
						|
  @doc """
 | 
						|
  `read/1` reads a file containing a line-separated list
 | 
						|
  of environment variables e.g: `.env`
 | 
						|
  Returns a Map in the form %{ KEY: value, MYVAR: value2 }
 | 
						|
 | 
						|
  ## Examples
 | 
						|
      iex> Envar.read(".env")
 | 
						|
      %{
 | 
						|
        "ADMIN_EMAIL" => "alex@gmail.com",
 | 
						|
        "EVERYTHING" => "awesome!",
 | 
						|
        "SECRET" => "master plan"
 | 
						|
      }
 | 
						|
 | 
						|
  """
 | 
						|
  @spec read(binary) :: map
 | 
						|
  def read(filename) do
 | 
						|
    path = Path.join(File.cwd!(), filename)
 | 
						|
 | 
						|
    Logger.debug(".env file path: #{path}")
 | 
						|
 | 
						|
    data = File.read!(path)
 | 
						|
 | 
						|
    data
 | 
						|
    |> String.trim()
 | 
						|
    |> String.split("\n")
 | 
						|
    |> Enum.reduce(%{}, fn line, acc ->
 | 
						|
      line = String.trim(line)
 | 
						|
 | 
						|
      with line <- String.replace(line, ["export ", "'"], ""),
 | 
						|
           [key | rest] <- String.split(line, "="),
 | 
						|
           value <- Enum.join(rest, "=") do
 | 
						|
        if String.length(value) > 0 do
 | 
						|
          Map.put(acc, key, value)
 | 
						|
        else
 | 
						|
          acc
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end)
 | 
						|
  end
 | 
						|
end
 |