# vapor **Repository Path**: liketemple/vapor ## Basic Information - **Project Name**: vapor - **Description**: 来自 github.com/keathley/vapor.git - **Primary Language**: Elixir - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-03-06 - **Last Updated**: 2023-03-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Vapor [![Elixir CI](https://github.com/keathley/vapor/actions/workflows/elixir.yml/badge.svg)](https://github.com/keathley/vapor/actions/workflows/elixir.yml) [![Module Version](https://img.shields.io/hexpm/v/vapor.svg)](https://hex.pm/packages/vapor) [![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/vapor/) [![Total Download](https://img.shields.io/hexpm/dt/vapor.svg)](https://hex.pm/packages/vapor) [![License](https://img.shields.io/hexpm/l/vapor.svg)](https://github.com/keathley/vapor/blob/master/LICENSE) [![Last Updated](https://img.shields.io/github/last-commit/keathley/vapor.svg)](https://github.com/keathley/vapor/commits/master) Loads dynamic configuration at runtime. ## Sponsors This project is sponsored by these fine folks: smartlogic _If you're interested in sponsoring you can do so here: https://github.com/sponsors/keathley_ ## Why Vapor? Dynamically configuring elixir apps can be hard. There are major differences between configuring applications with mix and configuring applications in a release. Vapor wants to make all of that easy by providing an alternative to mix config for runtime configs. Specifically Vapor can: * Find and load configuration from files (JSON, YAML, TOML). * Read configuration from environment variables. * `.env` file support for easy local development. ## Example ```elixir defmodule VaporExample.Application do use Application alias Vapor.Provider.{File, Env} def start(_type, _args) do providers = [ %Env{bindings: [db_url: "DB_URL", db_name: "DB_NAME", port: "PORT"]}, %File{path: "config.toml", bindings: [kafka_brokers: "kafka.brokers"]}, ] # If values could not be found we raise an exception and halt the boot # process config = Vapor.load!(providers) children = [ {VaporExampleWeb.Endpoint, port: config.port}, {VaporExample.Repo, [db_url: config.db_url, db_name: config.db_name]}, {VaporExample.Kafka, brokers: config.kafka_brokers}, ] opts = [strategy: :one_for_one, name: VaporExample.Supervisor] Supervisor.start_link(children, opts) end end ``` ### Precedence Vapor merges the configuration based on the order that the providers are specified. ```elixir providers = [ %Dotenv{}, %File{path: "$HOME/.vapor/config.json", bindings: []}, %Env{bindings: []}, ] ``` Env will have the highest precedence, followed by File, and finally Dotenv. ### Reading config files Config files can be read from a number of different file types including JSON, TOML, and YAML. Vapor determines which file format to use based on the file extension. ### Options on bindings Bindings for `%Env{}` and `%File{}` providers support a number of options: * `:map` - Allows you to pass a "translation" function with the binding. * `:default` - If the value is not found then the default value will be returned instead. Defaults always skip the translations. * `:required` - Marks the binding a required or not required (defaults to true). If required values are missing, and there is no default present, then the provider will return an exception. If the binding is marked `required: false`, then the provider returns the key with a `nil` value. ```elixir providers = [ %Env{ bindings: [ {:db_name, "DB_NAME"}, {:db_port, "DB_PORT", default: 4369, map: &String.to_integer/1}, ] } ] ``` ## Adding configuration plans to modules Vapor provides a `Vapor.Plan` behaviour. This allows modules to describe a provider or set of providers. ```elixir defmodule VaporExample.Kafka do @behaviour Vapor.Plan @impl Vapor.Plan def config_plan do %Vapor.Provider.Env{ bindings: [ {:brokers, "KAFKA_BROKERS"}, {:group_id, "KAFKA_CONSUMER_GROUP_ID"}, ] } end end config = Vapor.load!(VaporExample.Kafka) ``` ## Planner DSL While using the structs directly is a perfectly reasonable option, it can often be verbose. Vapor provides a DSL for specifying configuration plans using less lines of code. ```elixir defmodule VaporExample.Config do use Vapor.Planner dotenv() config :db, env([ {:url, "DB_URL"}, {:name, "DB_NAME"}, {:pool_size, "DB_POOL_SIZE", default: 10, map: &String.to_integer/1}, ]) config :web, env([ {:port, "PORT", map: &String.to_integer/1}, ]) config :kafka, VaporExample.Kafka end defmodule VaporExample.Application do use Application def start(_type, _args) do config = Vapor.load!(VaporExample.Config) children = [ {VaporExampleWeb.Endpoint, config.web}, {VaporExample.Repo, config.db}, {VaporExample.Kafka, config.kafka}, ] opts = [strategy: :one_for_one, name: VaporExample.Supervisor] Supervisor.start_link(children, opts) end end ``` ## Custom Providers There are several built in providers - Environment - .env files - JSON - YAML - TOML If you need to create a new provider you can do so with the included `Vapor.Provider` protocol. ```elixir defmodule MyApp.DatabaseProvider do defstruct [id: nil] defimpl Vapor.Provider do def load(db_provider) do end end end ``` ## Why does this exist? While its possible to use Elixir's release configuration for some use cases, release configuration has some issues: * If configuration ends up in Application config then its still functioning as a global and is shared across all of your running applications. * Limited ability to recover from failures while fetching config from external providers. * Its difficult to layer configuration from different sources. Vapor is designed to solve these problems. ## Installing Add vapor to your mix dependencies: ```elixir def deps do [ {:vapor, "~> 0.10"}, ] end ``` ## Resources from the community [Configuring your Elixir Application at Runtime with Vapor](https://blog.appsignal.com/2020/04/28/configuring-your-elixir-application-at-runtime-with-vapor.html) ## Copyright and License Copyright (c) 2020 Christopher Keathley This library is released under the MIT License. See the [LICENSE.md](./LICENSE.md) file.