{"id":13562509,"url":"https://github.com/gmtprime/skogsra","last_synced_at":"2025-04-03T18:33:49.834Z","repository":{"id":21487901,"uuid":"93067092","full_name":"gmtprime/skogsra","owner":"gmtprime","description":"Library to manage OS environment variables and application configuration options with ease","archived":false,"fork":false,"pushed_at":"2023-11-24T10:51:17.000Z","size":197,"stargazers_count":105,"open_issues_count":0,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-01T12:07:34.815Z","etag":null,"topics":["application-configuration","elixir","environment-variables"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gmtprime.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":["alexdesousa"]}},"created_at":"2017-06-01T14:39:04.000Z","updated_at":"2024-06-20T11:38:44.000Z","dependencies_parsed_at":"2022-08-08T16:00:03.756Z","dependency_job_id":"59822052-d923-464b-bdfd-d2b1761752ea","html_url":"https://github.com/gmtprime/skogsra","commit_stats":{"total_commits":99,"total_committers":5,"mean_commits":19.8,"dds":0.2525252525252525,"last_synced_commit":"55bb577731e71c0d77dcb87c3b262fdf32558ae3"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gmtprime%2Fskogsra","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gmtprime%2Fskogsra/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gmtprime%2Fskogsra/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gmtprime%2Fskogsra/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gmtprime","download_url":"https://codeload.github.com/gmtprime/skogsra/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246922243,"owners_count":20855345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["application-configuration","elixir","environment-variables"],"created_at":"2024-08-01T13:01:09.383Z","updated_at":"2025-04-03T18:33:44.823Z","avatar_url":"https://github.com/gmtprime.png","language":"Elixir","funding_links":["https://github.com/sponsors/alexdesousa"],"categories":["Configuration","Elixir"],"sub_categories":[],"readme":"# Skogsrå\n\n[![Build Status](https://travis-ci.org/gmtprime/skogsra.svg?branch=master)](https://travis-ci.org/gmtprime/skogsra) [![Hex pm](http://img.shields.io/hexpm/v/skogsra.svg?style=flat)](https://hex.pm/packages/skogsra) [![hex.pm downloads](https://img.shields.io/hexpm/dt/skogsra.svg?style=flat)](https://hex.pm/packages/skogsra) [![Coverage Status](https://coveralls.io/repos/github/gmtprime/skogsra/badge.svg?branch=master)](https://coveralls.io/github/gmtprime/skogsra?branch=master)\n\n\u003e The _Skogsrå_ was a mythical creature of the forest that appears in the form\n\u003e of a small, beautiful woman with a seemingly friendly temperament. However,\n\u003e those who are enticed into following her into the forest are never seen\n\u003e again.\n\nThis library attempts to improve the use of OS environment variables for\napplication configuration:\n\n* Variable defaults.\n* Automatic type casting of values.\n* Automatic docs and spec generation.\n* OS environment template generation.\n* Posibility to override Elixir configuration.\n* Runtime reloading.\n* Setting variable's values at runtime.\n* Fast cached values access by using `:persistent_term` as temporal storage.\n* Custom variable bindings.\n* YAML and JSON configuration providers for Elixir releases.\n\n## Small example\n\nYou would create a config module e.g:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"My hostname\"\n  app_env :my_hostname, :myapp, :hostname,\n    default: \"localhost\",\n    env_overrides: [\n      prod: [default: \"example.com\"]\n    ]\nend\n```\n\nCalling `MyApp.Config.my_hostname()` will retrieve the value for the\nhostname in the following order:\n\n1. From the OS environment variable `$MYAPP_HOSTNAME` (can be overriden by the\n   option `os_env`).\n2. From the configuration file e.g:\n   ```elixir\n   config :myapp,\n     hostname: \"my.custom.host\"\n   ```\n3. From the default value if it exists. In this case, it would return\n   `\"localhost\"` for all environments except `prod`. For `prod`, it will\n   return `example.com`.\n\n## Available options\n\nThere are several options for configuring an environment variables:\n\nOption          | Type                                                 | Default              | Description\n:-------------- | :--------------------------------------------------- | :------------------- | :----------\n`default`       | `any`                                                | `nil`                | Sets the Default value for the variable.\n`type`          | [`Skogsra.Env.type()`](#types)                       | `:any`               | Sets the explicit type for the variable.\n`os_env`        | `binary`                                             | autogenerated        | Overrides automatically generated OS environment variable name.\n`binding_order` | [`Skogra.Env.bindings()`](#custom-variable-bindings) | `[:system, :config]` | Sets the load order for variable binding.\n`binding_skip`  | [`Skogra.Env.bindings()`](#custom-variable-bindings) | `[]`                 | Which variable bindings should be skipped.\n`required`      | `boolean`                                            | `false`              | Whether the variable is required or not.\n`cached`        | `boolean`                                            | `true`               | Whether the variable should be cached or not.\n`env_overrides` | `keyword`                                            | `[]`                 | Overrides `default` and `required` properties for a specific environment.\n\n\u003e **IMPORTANT**: Options `skip_system: true` and `skip_config: true` have been\n\u003e deprecated in favour of `binding_skip: [:system]` and `binding_skip: [:config]`\n\u003e respectively.\n\nAdditional topics:\n\n- [Types](#types)\n- [Automatic type casting](#automatic-type-casting).\n- [Explicit type casting](#explicit-type-casting).\n- [Custom types](#custom-types).\n- [Explicit OS environment variable names](#explicit-os-environment-variable-name).\n- [Required variables](#required-variables).\n- [Overriding Elixir Configuration](#overriding-elixir-configuration).\n- [Overriding Environment Configuration](#overriding-environment-configuration).\n- [Caching variables](#caching-variables).\n- [Handling different environments](#handling-different-environments).\n- [Setting and reloading variables](#setting-and-reloading-variables).\n- [Automatic docs generation](#automatic-docs-generation).\n- [Automatic spec generation](#automatic-spec-generation).\n- [Automatic template generation](#automatic-template.generation).\n- [Custom variable bindings](#custom-variable-bindings).\n- [Elixir release YAML and JSON config providers](#elixir-release-yaml-and-json-config-providers).\n- [Using with Hab](#using-with-hab).\n- [Installation](#installation).\n\n## Types\n\nSkogsrå implements several built-in types. Some of them can be\n[inferred from the default value](#automatic-type-casting) while others must be\n[set explicitly](#explicit-type-casting):\n\nType                          | Can be inferred? | Description\n:---------------------------- | :--------------- | :---------------\n`:any`                        | Yes              | Any Elixir term.\n`:binary`                     | Yes              | A string e.g. `\"localhost\"`.\n`:integer`                    | Yes              | An integer e.g. `42`.\n`:neg_integer`                | No               | A negative integer.\n`:non_neg_integer`            | No               | A non negative integer.\n`:pos_integer`                | No               | A positive integer.\n`:float`                      | Yes              | A float e.g. `42.0`.\n`:boolean`                    | Yes              | A boolean e.g. `false`.\n`:atom`                       | Yes              | An atom e.g. `:ok`.\n`:module`                     | No               | An existent module e.g. `Enum`.\n`:unsafe_module`              | No               | A module e.g. `SomeModule`.\n`Skogsra.Type` implementation | No               | See [Custom types](#custom-types) section.\n\n## Automatic type casting\n\nIf the `default` value is set (and no explicit `type` is found), the variable\nvalue will be casted as the same type of the default value. For this to work,\nthe default value should be of the following types:\n\n- `:any`\n- `:binary`\n- `:integer`\n- `:float`\n- `:boolean`\n- `:atom`\n\ne.g. in the following example, the value will be casted to `:atom`\nautomatically:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"My environment\"\n  app_env :my_environment, :myapp, :environment,\n    default: :prod\nend\n```\n\nIf either of the system OS or the application environment variables are defined,\nSkogsrå will try to cast their values to the default value's type which it\n`atom` e.g:\n\n```elixir\niex(1)\u003e System.get_env(\"MYAPP_ENVIRONMENT\")\n\"staging\"\niex(2)\u003e MyApp.Config.my_environment()\n{:ok, :staging}\n```\n\nor\n\n```elixir\niex(1)\u003e Application.get_env(:myapp, :environment)\n\"staging\"\niex(2)\u003e MyApp.Config.my_environment()\n{:ok, :staging}\n```\n\n\u003e **Note**: If the variable is already of the desired type, it won't be casted.\n\n## Explicit type casting\n\nA type can be explicitly set. The available types are the following:\n\n - `:any` (default).\n - `:binary`.\n - `:integer`.\n - `:neg_integer`.\n - `:non_neg_integer`.\n - `:pos_integer`.\n - `:float`.\n - `:boolean`.\n - `:atom`.\n - `:module` (modules loaded in the system).\n - `:unsafe_module` (modules that might or might not be loaded in the system)\n - A module name with an implementation for the behaviour `Skogsra.Type`.\n\ne.g. in the following example, the value will be casted to `:pos_integer`\naccording to the variable definition:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"Port\"\n  app_env :my_port, :myapp, :port,\n    default: 4000,\n    type: :pos_integer\nend\n```\n\nIf either of the system OS or the application environment variables are defined,\nSkogsrå will try to cast their values to a positive integer.\n\n```elixir\niex(1)\u003e System.get_env(\"MYAPP_PORT\")\n\"42\"\niex(2)\u003e MyApp.Config.my_port()\n{:ok, 42}\n```\n\nor\n\n```elixir\niex(1)\u003e Application.get_env(:myapp, :port)\n42\niex(2)\u003e MyApp.Config.my_port()\n{:ok, 42}\n```\n\n## Custom types\n\nSkogsrå's types can be extented by implementing `Skogra.Type` behaviour e.g. a\npossible implementation for casting `\"1, 2, 3, 4\"` to `[integer()]` would be:\n\n```elixir\ndefmodule MyList do\n  use Skogsra.Type\n\n  @impl Skogsra.Type\n  def cast(value)\n\n  def cast(value) when is_binary(value) do\n    list =\n      value\n      |\u003e String.split(~r/,/)\n      |\u003e Stream.map(\u0026String.trim/1)\n      |\u003e Enum.map(\u0026String.to_integer/1)\n\n    {:ok, list}\n  end\n\n  def cast(value) when is_list(value) do\n    if Enum.all?(value, \u0026is_integer/1), do: {:ok, value}, else: :error\n  end\n\n  def cast(_) do\n    :error\n  end\nend\n```\n\nIf then we define the following enviroment variable with Skogsrå:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  app_env :my_integers, :myapp, :integers,\n    type: MyList\nend\n```\n\nIf either of the system OS or the application environment variables are defined,\nSkogsrå will try to cast their values using our implementation e.g:\n\n```elixir\niex(1)\u003e System.get_env(\"MYAPP_INTEGERS\")\n\"1, 2, 3\"\niex(2)\u003e MyApp.Config.my_integers()\n{:ok, [1, 2, 3]}\n```\n\nor\n\n```elixir\niex(1)\u003e Application.get_env(:myapp, :integers)\n[1, 2, 3]\niex(2)\u003e MyApp.Config.my_integers()\n{:ok, [1, 2, 3]}\n```\n\n\u003e **Important**: The `default` value is not cast according to `type`.\n\n## Explicit OS environment variable names\n\nThough Skogsrå automatically generates the names for the OS environment\nvariables, they can be overriden by using the option `os_env` e.g:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  app_env :db_hostname, :myapp, [:postgres, :hostname],\n    os_env: \"PGHOST\"\nend\n```\n\nThis will override the value `$MYAPP_POSTGRES_HOSTNAME` with `$PGHOST` e.g:\n\n```elixir\niex(1)\u003e System.get_env(\"MYAPP_POSTGRES_HOSTNAME\")\n\"unreachable\"\niex(2)\u003e System.get_env(\"PGHOST\")\n\"reachable\"\niex(3)\u003e MyApp.Config.db_hostname()\n{:ok, \"reachable\"}\n```\n\n## Required variables\n\nIt is possible to set a environment variable as required with the `required`\noption e.g:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"My port\"\n  app_env :my_port, :myapp, :port,\n    required: true\nend\n```\n\nIf none of the system OS or the application environment variables are defined,\nSkogsrå will return an error e.g:\n\n```elixir\niex(1)\u003e System.get_env(\"MYAPP_PORT\")\nnil\niex(2)\u003e Application.get_env(:myapp, :port)\nnil\niex(3)\u003e MyApp.Config.my_port()\n{:error, \"Variable port in app myapp is undefined\"}\n```\n\nThe module will also provide `validate` and `validate!` functions that can be\nused in your application startup phase to verify that *all* required variables\nare present e.g:\n\n```elixir\niex(1)\u003e System.get_env(\"MYAPP_PORT\")\nnil\niex(2)\u003e Application.get_env(:myapp, :port)\nnil\niex(3)\u003e MyApp.Config.validate!()\n** (RuntimeError) Variable port in app myapp is undefined\n```\n\n## Handling different environments\n\nIf it's necessary to keep several environments, it's possible to use a\n`namespace` e.g. given the following variable:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"My port\"\n  app_env :my_port, :myapp, :port,\n    default: 4000\nend\n```\n\nCalling `MyApp.Config.my_port(Test)` will retrieve the value for the hostname\nin the following order:\n\n1. From the OS environment variable `$TEST_MYAPP_PORT`.\n2. From the configuration file e.g:\n   ```elixir\n   config :myapp, Test,\n     port: 4001\n   ```\n3. From the OS environment variable `$MYAPP_PORT`.\n4. From the configuraton file e.g:\n   ```elixir\n   config :myapp,\n     port: 80\n   ```\n5. From the default value if it exists. In our example, `4000`.\n\nThe ability of loading different environments allows us to do the following\nwith our configuration file:\n\n```elixir\n# file: config/config.exs\nuse Mix.Config\n\nconfig :myapp, Prod\n  port: 80\n\nconfig :myapp, Test,\n  port: 4001\n\nconfig :myapp,\n  port: 4000\n```\n\nWhile our Skogsrå module would look like:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"Application environment\"\n  app_env :env, :myapp, :environment,\n    type: :unsafe_module\n\n  @envdoc \"Application port\"\n  app_env :port, :myapp, :port,\n    default: 4000\nend\n```\n\nThe we can retrieve the values depending on the value of the OS environment\nvariable `$MYAPP_ENVIRONMENT`:\n\n```elixir\n...\nwith {:ok, env} \u003c- MyApp.Config.env(),\n     {:ok, port} \u003c- Myapp.Config.port(env) do\n  ... do something with the port ...\nend\n...\n```\n\n## Overriding Elixir Configuration\n\nNot all the libraries will use Skogsrå and, though is a shame, we can always\noverride a config value using the function `preload/0` e.g.\n\nLet's say we have a Phoenix application and we want to configure our endpoint\nwith the following variables.\n\n- The OS environment variable `PORT` for our app's HTTP port.\n- The OS environment variable `SECRET_KEY_BASE` for our secret key base.\n\nA regular Phoenix configuration will look like the following:\n\n```elixir\nuse Mix.Config\n\nsecret_key_base =\n  System.get_env(\"SECRET_KEY_BASE\") ||\n    raise \"\"\"\n    environment variable SECRET_KEY_BASE is missing.\n    You can generate one by calling: mix phx.gen.secret\n    \"\"\"\n\n\nconfig :myapp_web, MyappWeb.Endpoint,\n  http: [\n    port: String.to_integer(System.get_env(\"PORT\") || \"4000\"),\n    transport_options: [socket_opts: [:inet6]]\n  ],\n  secret_key_base: secret_key_base\n```\n\nWith Skogsrå, we can reduce the config to the following:\n\n```elixir\nuse Mix.Config\n\nconfig :myapp_web, Myapp.Endpoint,\n  http: [\n    transport_options: [socket_opts: [:inet6]]\n  ]\n```\n\nThen, handle our variables in our Skogsrå config module:\n\n```elixir\ndefmodule MyappWeb.Config do\n  use Skogsra\n\n  @envdoc \"\"\"\n  App port.\n  \"\"\"\n  app_env :port, :myapp, [:http, :port],\n    os_env: \"PORT\",\n    default: 4000\n\n  @envdoc \"\"\"\n  Secret key base.\n  \"\"\"\n  app_env :secret_key_base, :myapp, :secret_key_base,\n    os_env: \"SECRET_KEY_BASE\",\n    default: \"+/JwMGGtsTVOoX5gQrCMn8aHKfKDdUK8GeAKJ2fIUabUnmWTwg+zsCy4pAOmOdTs\"\nend\n```\n\nAnd finally calling the preload function in our application init:\n\n```elixir\ndefmodule MyappWeb.Application do\n  use Application\n\n  def start(_type, _args) do\n    MyappWeb.Config.preload(MyappWeb.Endpoint)\n\n    children = [\n      MyappWeb.Endpoint\n    ]\n\n    ...\n  end\nend\n```\n\nThe function `MyappWeb.Config.preload/1` will override any configuration for\n`MyappWeb.Endpoint` as long as is a runtime config.\n\nOur app now will try to get the port and secret key from the OS environment\nvariables first and it will fallback to the defaults.\n\n## Overriding Environment Configuration\n\nSometimes we need different requirements for different environments (e.g. `dev`,\n`test`, `prod`). With Skogsrå you can define both `default` and `required`\nattributes for each environment using `env_overrides`:\n\n1. First searches the environment using `Mix.env()`.\n2. If `Mix` is not present, it tries to get the value from  the `MIX_ENV` OS\n   environment variable.\n3. Finally, if all fails, defaults to `prod`.\n\nIf for certain environment there are no `default` and/or `required` attributes\ndefined, then it'll use the global `default` and/or `required` attributes.\n\nE.g. let's say our database configuration requires `username`, `password`,\n`hostname`, `port` and `database` for `dev` and `test` environments, but not for\nour `prod` environment where we use a `url` instead. Then we would do the\nfollowing:\n\n```elixir\ndefmodule Myapp.Config do\n  use Skogsra\n\n  @envdoc \"DB username\"\n  app_env :db_username, :myapp, [Myapp.Repo, :username],\n    env_overrides: [\n      dev: [default: \"postgres\"],\n      test: [default: \"postgres\"]\n    ]\n\n  @envdoc \"DB password\"\n  app_env :db_password, :myapp, [Myapp.Repo, :password],\n    env_overrides: [\n      dev: [default: \"postgres\"],\n      test: [default: \"postgres\"]\n    ]\n\n  @envdoc \"DB hostname\"\n  app_env :db_hostname, :myapp, [Myapp.Repo, :hostname],\n    env_overrides: [\n      dev: [default: \"localhost\"],\n      test: [default: \"localhost\"]\n    ]\n\n  @envdoc \"DB port\"\n  app_env :db_port, :myapp, [Myapp.Repo, :port],\n    env_overrides: [\n      dev: [default: 5432],\n      test: [default: 5432]\n    ]\n\n  @envdoc \"DB name\"\n  app_env :db_name, :myapp, [Myapp.Repo, :database],\n    env_overrides: [\n      dev: [default: \"myapp_dev\"],\n      test: [default: \"myapp_test\"]\n    ]\n\n  @envdoc \"DB URL\"\n  app_env :db_url, :myapp, [Myapp.Repo, :url],\n    os_env: \"DATABASE_URL\",\n    env_overrides: [\n      prod: [required: true]\n    ]\nend\n```\n\nBoth `dev` and `test` environments have default values for the `username`,\n`password`, `hostname`, `port` and `database` configuration variables while\nthe `url` value is `nil` by default.\n\nHowever, for `prod` enviroment, there are no defaults and all of them are\nactually `nil`. Instead, `prod` requires for the `url` to be defined and it\ndoesn't provide any `default` value.\n\nIf the OS environment variable `DATABASE_URL` is not defined and the following\nis called before our application start:\n\n```elixir\nMyapp.Config.preload()\nMyapp.Config.validate()\n```\n\nthen our application will fail to start on `prod` environment, but not for `dev`\nor `test`.\n\n## Caching variables\n\nBy default, Skogsrå caches the values of the variables using\n`:persistent_term` Erlang module. This makes reads very fast, but **writes are\nvery slow**.\n\nSo avoid setting or reloading values to avoid performance issues (see\n[Setting and reloading variables](#setting-and-reloading-variables)).\n\nIf you don't want to cache the values, you can set it to `false`:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  app_env :value, :myapp, :value,\n    cached: false\nend\n```\n\n## Setting and reloading variables\n\nEvery variable definition will generate two additional functions for setting\nand reloading the values e.g:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"My port\"\n  app_env :my_port, :myapp, :port,\n    default: 4000\nend\n```\n\nwill have the functions:\n\n- `MyApp.Config.put_my_port/1` for setting the value of the variable at\n  runtime.\n- `MyApp.Config.reload_my_port/` for reloading the value of the variable at\n  runtime.\n\n## Automatic docs generation\n\nIt's possible to document a configuration variables using the module attribute\n`@envdoc` e.g:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"My port\"\n  app_env :my_port, :myapp, :port,\n    default: 4000\nend\n```\n\nSkogsra then will automatically generate instructions on how to use the\nvariable. This extra documentation can be disable with the following:\n\n```elixir\nconfig :skogsra,\n  generate_docs: false\n```\n\n\u003e **Note**: for \"private\" configuration variables you can use `@envdoc false`.\n\n## Automatic spec generation\n\nSkogsra will try to generate the appropriate spec for every function generated.\nIn our example, given the default value is an integer, the function spec will be\nthe following:\n\n```elixir\n@spec my_port() :: {:ok, integer()} | {:error, binary()}\n@spec my_port(Skogsra.Env.namespace()) ::\n        {:ok, integer()} | {:error, binary()}\n```\n\n\u003e **Note**: The same applies for `my_port!/0`, `reload_my_port/0` and\n\u003e `put_my_port/1`.\n\nSpecs are generated following the following table:\n\nAssuming the type is set to `:integer` explicitly (`type: :integer`) or\nimplicitly (`default: 4000`), the specs should follow the following table:\n\nRequired     | Has default     | Spec\n:----------: | :-------------: | :----\n`true`       | `true`          | `@spec my_port() :: {:ok, integer()} \\| {:error, binary()}`\n`true`       | `false`         | `@spec my_port() :: {:ok, integer()} \\| {:error, binary()}`\n`false`      | `true`          | `@spec my_port() :: {:ok, integer()} \\| {:error, binary()}`\n`false`      | `false`         | `@spec my_port() :: {:ok, nil \\| integer()} \\| {:error, binary()}`\n\n## Automatic template generation\n\nEvery Skogsra module includes the functions `template/1` and `template/2` for\ngenerating OS environment variable files e.g. continuing our example:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"My port\"\n  app_env :my_port, :myapp, :port,\n    default: 4000\nend\n```\n\n- For Elixir releases:\n\n  ```elixir\n  iex(1)\u003e Myapp.Config.template(\"env\")\n  :ok\n  ```\n\n  will generate the file `env` with the following contents:\n\n  ```bash\n  # DOCS My port\n  # TYPE integer\n  MYAPP_PORT=\"Elixir.Application\"\n  ```\n- For Unix:\n\n  ```elixir\n  iex(1)\u003e Myapp.Config.template(\"env\", type: :unix)\n  :ok\n  ```\n\n  will generate the file `env` with the following contents:\n\n  ```bash\n  # DOCS My port\n  # TYPE integer\n  export MYAPP_PORT='Elixir.Application'\n  ```\n\n- For Windows:\n\n  ```elixir\n  iex(1)\u003e Myapp.Config.template(\"env.bat\", type: :windows)\n  :ok\n  ```\n\n  will generate the file `env.bat` with the following contents:\n\n  ```bat\n  :: DOCS My port\n  :: TYPE integer\n  SET MYAPP_PORT=\"Elixir.Application\"\n  ```\n\n## Custom variable bindings\n\nBy default, Skogsrå loads variables in the following order:\n\n- OS environment variables or `:system`.\n- Application configuratuon or `:config`.\n\nThis means that every variable has the following defaults:\n\n- For `binding_order` is `[:system, :config]`.\n- For `binding_skip` is `[]`.\n\nThese two values can be modified globally via configuration e.g:\n\n- For globally changing the variable binding order:\n\n   ```elixir\n   config :skogsra,\n     binding_order: [:config, :system]\n   ```\n\n- For globally skipping `:system`:\n\n   ```elixir\n   config :skogsra,\n     binding_skip: [:system]\n   ```\n\nOr be modified per `app_env` e.g:\n\n- For changing `MyApp.Config.my_port/0` binding order:\n\n   ```elixir\n   defmodule MyApp.Config do\n     use Skogsra\n\n     @envdoc \"My port\"\n     app_env :my_port, :myapp, :port,\n       binding_order: [:config, :system]\n   end\n   ```\n\n- For skipping `:system` in `MyApp.Config.my_port/0`:\n\n   ```elixir\n   defmodule MyApp.Config do\n     use Skogsra\n\n     @envdoc \"My port\"\n     app_env :my_port, :myapp, :port,\n       binding_skip: [:system]\n   end\n   ```\n\nAdditionally, we can create new variable bindings by implementing\n`Skogsra.Binding` behaviour e.g. an implementation for loading JSON\nconfiguration files would be:\n\n```elixir\ndefmodule MyApp.Json do\n  use Skogsra.Binding\n\n  alias Skogsra.Env\n\n  @impl true\n  def init(%Env{} = env) do\n    options = Env.extra_options(env)\n\n    case options[:config_path] do\n      nil -\u003e\n        {:error, \"JSON config path not specified\"}\n\n      path -\u003e\n        load(path)\n    end\n  end\n\n  @impl true\n  def get_env(%Env{} = env, config) when is_map(config) do\n    name = Env.os_env(env)\n    value = config[name]\n\n    {:ok, value}\n  end\n\n  # Helpers\n\n  # Loads JSON once and caches it in a :persistent_term using the path\n  # as the key.\n  @spec load(binary()) :: {:ok, map()} | {:error, term()}\n  defp load(path) do\n    with nil \u003c- :persistent_term.get(path, nil),\n         {:ok, contents} \u003c- File.read(path),\n         {:ok, config} \u003c- Jason.decode(contents),\n         :ok \u003c- :persistent_term.put(path, config) do\n      {:ok, config}\n    else\n      {:error, reason} -\u003e\n        {:error, \"Cannot load #{path} due to #{inspect(reason)}\"}\n\n      config -\u003e\n        {:ok, config}\n    end\n  end\nend\n```\n\nThe previous implementation expects variables to be named the same as the\nOS environment variables e.g:\n\n```json\n{\n  \"MYAPP_PORT\": 5000\n}\n```\n\nThen our variable declaration would look something like the following:\n\n```elixir\ndefmodule MyApp.Config do\n  use Skogsra\n\n  @envdoc \"My port\"\n  app_env :my_port, :myapp, :port,\n    binding_order: [:system, :config, MyApp.Json],\n    config_path: \"#{Path.cwd!()}/priv/config.json\",\n    default: 4000\nend\n```\n\nIf no `:system` or `:config` is found, it will try to load the variable from\nthe JSON file e.g:\n\n```elixir\niex\u003e MyApp.Config.port()\n{:ok, 5000}\n```\n\n\u003e **Note**: The same casting rules apply for all bindings.\n\n## Elixir release YAML and JSON config providers\n\nSkogsrå includes two simple configuration providers compatible with\n`mix release` for Elixir ≥ 1.9:\n\n- YAML configuration provider:\n\n   ```yaml\n   - app: \"my_app\"              # Name of the application.\n     module: \"MyApp.Repo\"       # Optional module/namespace.\n     config:                    # Actual configuration.\n       database: \"my_app_db\"\n       username: \"postgres\"\n       password: \"postgres\"\n       hostname: \"localhost\"\n       port: 5432\n   ```\n\n- JSON configuration provider:\n\n   ```json\n   [\n     {\n       \"app\": \"my_app\",\n       \"module\": \"MyApp.Repo\",\n       \"config\": {\n         \"database\": \"my_app_db\",\n         \"username\": \"postgres\",\n         \"password\": \"postgres\",\n         \"hostname\": \"localhost\",\n         \"port\": 5432\n       }\n     }\n   ]\n   ```\n\nBoth configurations would be equivalent to:\n\n```elixir\nconfig :my_app, MyApp.Repo,\n  database: \"my_App_db\",\n  username: \"postgres\"\n  password: \"postgres\"\n  hostname: \"localhost\"\n  port: 5432\n```\n\nFor using these config providers, just add the following to your release\nconfiguration:\n\n- For YAML configurations:\n\n   ```elixir\n   config_providers: [{Skogsra.Provider.Yaml, [\"/path/to/config/file.yml\"]}]\n   ```\n\n- For JSON configurations:\n\n   ```elixir\n   config_providers: [{Skogsra.Provider.Json, [\"/path/to/config/file.json\"]}]\n   ```\n\n\u003e **Note**: If the `module` you're using in you're config does not exist, then\n\u003e change it to `namespace` e.g: `namespace: \"MyApp.Repo\"`. Otherwise, it will\n\u003e fail loading it.\n\n## Using with `Hab`\n\n[_Hab_](https://github.com/alexdesousa/hab) is an\n[Oh My ZSH](https://github.com/robbyrussell/oh-my-zsh) plugin for loading OS\nenvironment variables automatically.\n\nBy default, `Hab` will try to load `.envrc` file, but it's possible to have\nseveral of those files for different purposes e.g:\n\n- `.envrc.prod` for production OS variables.\n- `.envrc.test` for testing OS variables.\n- `.envrc` for development variables.\n\n`Hab` will load the development variables by default, but it can load the\nother files using the command `hab_load \u003cextension\u003e` e.g. loading\n`.envrc.prod` would be as follows:\n\n```bash\n~/my_project $ hab_load prod\n[SUCCESS]  Loaded hab [/home/user/my_project/.envrc.prod]\n```\n\n## Installation\n\nThe package can be installed by adding `skogsra` to your list of dependencies\nin `mix.exs` (Elixir ≥ 1.11 and Erlang ≥ 22):\n\n```elixir\ndef deps do\n  [\n    {:skogsra, \"~\u003e 2.5\"}\n  ]\nend\n```\n\nIf you need YAML config provider support, add the following:\n\n```elixir\ndef deps do\n  [\n    {:skogsra, \"~\u003e 2.5\"},\n    {:yamerl, \"~\u003e 0.10\"}\n  ]\nend\n```\n\nIf you need JSON config provider support, add  the following:\n\n```elixir\ndef deps do\n  [\n    {:skogsra, \"~\u003e 2.5\"},\n    {:jason, \"~\u003e 1.4\"}\n  ]\nend\n```\n\n## Author\n\nAlexander de Sousa.\n\n## License\n\n_Skogsrå_ is released under the MIT License. See the LICENSE file for further\ndetails.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgmtprime%2Fskogsra","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgmtprime%2Fskogsra","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgmtprime%2Fskogsra/lists"}