defmodule Doex.Config do use FnExpr @moduledoc""" There are a few ways to configure access to your Digital Ocean account. First, you should go and find your [API TOKEN](https://www.digitalocean.com/community/tutorials/how-to-use-the-digitalocean-api-v2#how-to-generate-a-personal-access-token) Let's say your token is ABC123, then you can configure you application through Mix Tasks, as follows: mix doex.init mix doex.config token ABC123 And to confirm it's set, run mix doex.config And the output should look similar to: ssh_keys: [] token: "ABC123" url: "https://api.digitalocean.com/v2" You can achieve similar behvarious through an iEX session `iex -S mix` Doex.Config.init "/Users/aforward/.doex" Doex.Config.put(:token, "ABC123") :ok Doex.Config.read %{ssh_keys: [], token: "ABC123", url: "https://api.digitalocean.com/v2"} The information above is cached in the Doex.Worker, so if you are making changes in iEX, you can reload your configs using iex> Doex.reload :ok And you can see the currently cached values with Doex.config %{ssh_keys: [], token: "ABC123", url: "https://api.digitalocean.com/v2"} The order of preference for locating the appropriate configs are #1 Environment variable storing the path to the file DOEX_CONFIG=/tmp/my.doex #2 Elixir built in Mix.Config use Mix.Config config :doex, config: %{token: "SHHHHH"} #3 A file within "myproject" called .doex /src/myproject/.doex # A file within the home directory called .doex ~/.doex You could overwrite the location, but that's mostly for testing, so unless you have a really valid reason do to so, please don't. """ @default_filename "~/.doex" def filename do case Application.get_env(:doex, :config) do nil -> case System.get_env("DOEX_CONFIG") do nil -> lookup_filename() f -> f end |> Path.expand _ -> :config end end def init(), do: filename() |> init def init(:config), do: :config def init(filename) do :ok = filename |> Path.dirname |> File.mkdir_p! unless File.exists?(filename) do filename |> reinit end filename end def reinit(), do: filename() |> reinit def reinit(:config), do: :config def reinit(filename) do :ok = write(filename, default_config()) filename end def get(key), do: filename() |> get(key) def get(:config, key), do: read() |> Map.get(key) def get(filename, key) do filename |> Path.expand |> init |> read |> Map.get(key) end def put(key, value), do: filename() |> put(key, value) def put(:config, _key, _value), do: {:error, :readonly} def put(filename, key, value) do filename |> Path.expand |> init |> read |> Map.merge(%{key => value}) |> invoke(fn map -> write(filename, map) end) end def remove(key), do: filename() |> remove(key) def remove(:config, _key), do: {:error, :readonly} def remove(filename, key) do filename |> Path.expand |> init |> read |> Map.delete(key) |> invoke(fn map -> write(filename, map) end) end def read(), do: filename() |> read def read(:config), do: Application.get_env(:doex, :config) def read(filename) do filename |> Path.expand |> File.read |> invoke(fn result -> case result do {:ok, content} -> :erlang.binary_to_term(content) {:error, _} -> default_config() end end) end defp lookup_filename do [ ".doex", ] |> Enum.filter(&File.exists?/1) |> Enum.fetch(0) |> case do :error -> @default_filename {:ok, f} -> f end end defp write(filename, map) do filename |> Path.expand |> File.write!(:erlang.term_to_binary(map)) end defp default_config do %{ token: "FILL_ME_IN", ssh_keys: [], url: "https://api.digitalocean.com/v2" } end end