An open API service indexing awesome lists of open source software.

https://github.com/icedragon200/kuddle_config

Elixir Config Provider using Kuddle
https://github.com/icedragon200/kuddle_config

config configprovider elixir kdl

Last synced: about 2 months ago
JSON representation

Elixir Config Provider using Kuddle

Awesome Lists containing this project

README

        

# Kuddle Config

An Elixir [`Config.Provider`](https://hexdocs.pm/elixir/master/Config.Provider.html) using [kuddle](https://github.com/IceDragon200/kuddle), also general configuration helpers using Kuddle.

## Installation

To add `kuddle_config` to your project:

```elixir
def deps do
[
{:kuddle_config, "~> 0.3.0"}
]
end
```

## Usage

Kuddle provides 2 different config providers:

### Single File Providers

The default Config.Provider which will load a kdl file as config

#### Kuddle.Config.Provider

Used for elixir releases:

```elixir
# Format
defp releases do
[
application: [
config_providers: [
{Kuddle.Config.Provider, [
path: config_path()
]}
]
]
]
end

# Example
defp releases do
[
application: [
config_providers: [
{Kuddle.Config.Provider, [
path: {:system, "PATH_TO_CONFIG", "/default/path/to/kdl/config"}
]}
]
]
]
end
```

#### Kuddle.Config.Distillery.Provider

Used for distillery releases

```elixir
# Format
release :my_app do
set config_providers: [
{Kuddle.Config.Distillery.Provider, [
path: config_path()
]}
]
end

# Example
release :my_app do
set config_providers: [
{Kuddle.Config.Distillery.Provider, [
path: {:system, "PATH_TO_CONFIG", "/default/path/to/kdl/config"}
]}
]
end
```

### Directory Loaders

#### Kuddle.Config.DirectoryProvider

```elixir
# A special config provider that will load every kdl file in a directory as config

# Format
defp releases do
[
application: [
config_providers: [
{Kuddle.Config.DirectoryProvider, [
paths: [config_path()],
extensions: [String.t()]
]}
]
]
]
end

# Example
defp releases do
[
application: [
config_providers: [
{Kuddle.Config.DirectoryProvider, [
paths: [
{:system, "PATH_TO_CONFIG", "/default/path/to/kdl/config"}
],
extensions: [".kdl", ".kuddle"]
]}
]
]
]
end
```

#### Kuddle.Config.Distillery.DirectoryProvider

```elixir
# Format
release :my_app do
set config_providers: [
{Kuddle.Config.Distillery.DirectoryProvider, [
paths: [config_path()],
extensions: [String.t()]
]}
]
end

# Example
release :my_app do
set config_providers: [
{Kuddle.Config.Distillery.DirectoryProvider, [
paths: [
{:system, "PATH_TO_CONFIG", "/default/path/to/kdl/config"}
],
extensions: [".kdl", ".kuddle"]
]}
]
end
```

## Other Use Cases

Despite the original purpose of this library being the config providers, the config module itself is quite useful even without the providers:

```elixir
# Have a KDL blob you wish to load?
{:ok, config} =
Kuddle.Config.load_config_blob("""
application {
node "value"
}
""")

[
application: [
node: "value"
]
] = config

# Have a KDL file you'd like to load?
{:ok, config} = Kuddle.Config.load_config_file("my_kdl_config.kdl")

[
application: [
node2: "some_other_value"
]
] = config

# Have a directory filled with KDL files you'd like to load into one config?
{:ok, config} = Kuddle.Config.load_config_directory("/my/kdl/configs", [".kdl", ".kuddle"])

[
data: [
{MyRepo, [
database: "database",
host: "127.0.0.1",
port: 5432,
]},
],
web: [
http: [
port: 4000
]
],
workers: [
amqp: [
host: "127.0.0.1",
port: 5732,
virtual_host: "my_workers",
]
]
] = config

# Have the kuddle document already?
{:ok, config} = Kuddle.Config.load_config_document(document)

[
logger: [
console: [
level: :debug
]
]
] = config
```

## Config Format

Root nodes are application level config, while subsequent sub nodes will be one level deeper config

```kdl
application {
key "value"

key2 {
subkey "value"
}
}
```

Is equivalent to:

```elixir
config :application,
key: "value",
key2: [
subkey: "value"
]
```

Config is extracted from both attributes and sub nodes:

```kdl
application {
food {
bacon "1"
eggs "2"
}
}
```

Is equivalent to:

```kdl
application {
food bacon="1" eggs="2"
}
```

Either can be mixed and matched to achieve a comfortable format:

```kdl
application {
food bacon="1" {
eggs "2"
}
}
```

There is one tiny gotcha in regards to lists:

```kdl
application {
node "value"
}
```

Would evaluate to:

```elixir
[
application: [
node: "value"
]
]
```

As one would expect, however:

```kdl
application {
node "value" "value2"
}
```

Would evaluate to:

```elixir
[
application: [
node: ["value", "value2"]
]
]
```

Assuming the configuration requires a list, it can be coerced using the `(list)` annotation on the node:

```kdl
application {
(list)node "value"
}
```

```elixir
[
application: [
node: ["value"]
]
]
```

Sometimes it is useful to set a tuple, which is something most available configuration languages struggle with for elixir:

```kdl
application {
(tuple)thing "left" "right"
}
```

```elixir
[
application: [
thing: {"left", "right"}
]
]
```

You can also cast values into atoms:

```kdl
logger {
console {
level (atom)"debug"
}
}
```

```elixir
[
logger: [
console: [
level: :debug
]
]
]
```

A list of all available types and more information can be found in the Kuddle.Config.Types module, or the table below.

| Type Annotation | Example |
| ---------------- | ----------------------------------------------------------------------------- |
| `date` | `start_date (date)"2021-09-14"` |
| `utc_datetime` | `inserted_at (utc_datetime)"2021-09-14T18:00:00.000000Z"` |
| `naive_datetime` | `deleted_at (naive_datetime)"2021-09-14T18:00:00.000000Z"` |
| `time` | `start_time (time)"18:00:23"` |
| `decimal` | `cost (decimal)"0.002500"` |
| `atom` | `level (atom)"debug"` |
| `boolean` | `enable_polling (boolean)"YES"` |
| `tuple` | `(tuple)call_pair "New York" "12003004000"` |
| `list` | `(list)allow_list "117.27.222.122"` |

Additional types can be registered using `kuddle_config` `:types` config:

```elixir
config :kuddle_config,
types: [
geopoint: {MyGeoPoint, :cast},
]
```

```elixir
defmodule MyGeoPoint do
def cast(value) do
{:ok, String.split(value, ",", parts: 2) |> Enum.map(&Decimal.new/1) |> List.to_tuple()}
end
end
```

```kdl
application {
point (geopoint)"15.27,265.27"
}
```

```elixir
[
application: [
point: {%Decimal{coef: "1527", exp: -2, sign: 1}, %Decimal{coef: "26527", exp: -2, sign: 1}}
]
]
```