Giter Club home page Giter Club logo

confex's Introduction

Confex

Deps Status Hex.pm Downloads Latest Version License Build Status Coverage Status Ebert

Confex simplifies reading configuration at run-time with adapter-based system for fetch values from any source. It's inspired by Phoenix {:system, value} definition for HTTP port.

Installation

It's available on hex.pm and can be installed as project dependency:

  1. Add confex to your list of dependencies in mix.exs:
def deps do
  [{:confex, "~> 3.2.2"}]
end
  1. Ensure confex is started before your application:
def application do
  [applications: [:confex]]
end

Usage

  1. Replace values with configuration tuples

Define configuration in your config.exs:

config :my_app, MyApp.MyQueue,
  queue: [
    name:        {:system, "OUT_QUEUE_NAME", "MyQueueOut"},
    error_name:  {:system, "OUT_ERROR_QUEUE_NAME", "MyQueueOut.Errors"},
    routing_key: {:system, "OUT_ROUTING_KEY", ""},
    durable:     {:system, "OUT_DURABLE", false},
    port:        {:system, :integer, "OUT_PORT", 1234},
  ]

Configuration tuples examples:

  • var - any bare values will be left as-is.
  • {:system, "ENV_NAME", "default"} - read string from system environment or fallback to "default" if it is not set.
  • {:system, "ENV_NAME"} - same as above, but raise error if ENV_NAME is not set.

Additionally you can cast string values to common types:

  • {:system, :string, "ENV_NAME", "default"} (string is a default type).
  • {:system, :string, "ENV_NAME"}.
  • {:system, :integer, "ENV_NAME", 123}.
  • {:system, :integer, "ENV_NAME"}.
  • {:system, :float, "ENV_NAME", 123.5}.
  • {:system, :float, "ENV_NAME"}.
  • {:system, :boolean, "ENV_NAME", true}.
  • {:system, :boolean, "ENV_NAME"}.
  • {:system, :atom, "ENV_NAME"}.
  • {:system, :atom, "ENV_NAME", :default}.
  • {:system, :module, "ENV_NAME"}.
  • {:system, :module, "ENV_NAME", MyDefault}.
  • {:system, :list, "ENV_NAME"}.
  • {:system, :list, "ENV_NAME", [1, 2, 3]}.

:system can be replaced with a {:via, adapter} tuple, where adapter is a module that implements Confex.Adapter behaviour.

  1. Read configuration by replacing Application.fetch_env/2, Application.fetch_env!/2 and Application.get_env/3 calls with Confex functions

Fetch string values:

iex> Confex.fetch_env(:myapp, MyKey)
{:ok, "abc"}

Fetch integer values:

iex> Confex.fetch_env(:myapp, MyIntKey)
{:ok, 123}

Fetch configuration from maps or keywords:

iex> Confex.fetch_env(:myapp, MyIntKey)
{:ok, [a: 123, b: "abc"]}

Integrating with Ecto

Ecto has a init/2 callback, you can use it with Confex to read environment variables. We used to have all our repos to look like this:

defmodule MyApp do
  use Ecto.Repo, otp_app: :my_app

  @doc """
  Dynamically loads the repository configuration from the environment variables.
  """
  def init(_, config) do
    url = System.get_env("DATABASE_URL")
    config =
      if url do
        Keyword.merge(config, Ecto.Repo.Supervisor.parse_url(url))
      else
        {:ok, config} = Confex.Resolver.resolve(config)
        config
      end

    unless config[:database] do
      raise "Set DB_NAME environment variable!"
    end

    unless config[:username] do
      raise "Set DB_USER environment variable!"
    end

    unless config[:password] do
      raise "Set DB_PASSWORD environment variable!"
    end

    unless config[:hostname] do
      raise "Set DB_HOST environment variable!"
    end

    unless config[:port] do
      raise "Set DB_PORT environment variable!"
    end

    {:ok, config}
  end
end

Integrating with Phoenix

  1. Set on_init callback in your prod.exs:

    config :my_app, MyApp.Web.Endpoint,
      on_init: {MyApp.Web.Endpoint, :load_from_system_env, []}
  2. Add load_from_system_env function to your endpoint:

    defmodule Mithril.Web.Endpoint do
    
      # Some code here
    
      @doc """
      Dynamically loads configuration from the system environment
      on startup.
    
      It receives the endpoint configuration from the config files
      and must return the updated configuration.
      """
      def load_from_system_env(config) do
        {:ok, config} = Confex.Resolver.resolve(config)
    
        unless config[:secret_key_base] do
          raise "Set SECRET_KEY environment variable!"
        end
    
        {:ok, config}
      end
    end

Using Confex macros

Confex is supplied with helper macros that allow to attach configuration to specific modules of your application.

defmodule Connection do
  use Confex, otp_app: :myapp
end

It will add config/0 function to Connection module that reads configuration at run-time for :myapp OTP application with key Connection.

You can add defaults by extending macro options:

defmodule Connection do
  use Confex,
    otp_app: :myapp,
    some_value: {:system, "ENV_NAME", "this_will_be_default value"}
end

If application environment contains values in Keyword or Map structs, default values will be recursively merged with application configuration.

We recommend to avoid using tuples without default values in this case, since config/0 calls will raise exceptions if they are not resolved.

You can validate configuration by overriding validate_config!/1 function, which will receive configuration and must return it back to caller function. It will be evaluated each time config/1 is called.

defmodule Connection do
  use Confex, otp_app: :myapp

  def validate_config!(config) do
    unless config[:password] do
      raise "Password is not set!"
    end

    config
  end
end

Adapters

Currently Confex supports two embedded adapters:

  • :system - read configuration from system environment;
  • :system_file - read file path from system environment and read configuration from this file. Useful when you want to resolve Docker, Swarm or Kubernetes secrets that are stored in files.

You can create adapter by implementing Confex.Adapter behaviour with your own logic.

Helpful links

confex's People

Contributors

alexfilatov avatar andrewdryga avatar gmile avatar pavelvesnin avatar sebastianpoeplau avatar take-five avatar trenpixster avatar webdeb avatar zachdaniel avatar zorbash avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.