Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/otann/wrench
🔧 Elegant configuration for a more civilized age
https://github.com/otann/wrench
12-factor 12factor clojure config configuration environment-variables
Last synced: about 2 months ago
JSON representation
🔧 Elegant configuration for a more civilized age
- Host: GitHub
- URL: https://github.com/otann/wrench
- Owner: Otann
- License: epl-1.0
- Created: 2017-12-24T02:20:13.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2023-06-24T18:23:49.000Z (over 1 year ago)
- Last Synced: 2024-10-21T05:39:20.726Z (2 months ago)
- Topics: 12-factor, 12factor, clojure, config, configuration, environment-variables
- Language: Clojure
- Homepage:
- Size: 92.8 KB
- Stars: 46
- Watchers: 6
- Forks: 4
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Wrench
[![Circle CI](https://circleci.com/gh/Otann/wrench.svg?style=shield&no-cache=0)](https://circleci.com/gh/Otann/wrench)
[![Clojars](https://img.shields.io/clojars/v/wrench.svg?no-cache=1)](https://clojars.org/wrench)
[![codecov](https://codecov.io/gh/Otann/wrench/branch/master/graph/badge.svg)](https://codecov.io/gh/Otann/wrench)Wrench is a library to manage your clojure app's configuration.
It is designed with specific goals in mind:- **All values are available during initialization of your code**
- That means you can use it in your `def`s (and def-like macros, like `defroutes`)
- All values come from environment variables, as [12 factors manifesto](https://12factor.net/config) recommends
- Each configuration could be accompanied with a custom spec
- One can ensure that whole config matches provided specs during runtime
- Configuration values are coerced to their spec from string and edn (enables values like `[8080 8888]`)
- Definition and usage of each key are easily traceable, since they are simple vars
- For local development extra values could be provided in REPL and from local EDN file## Installation
Add `[wrench "0.3.3"]` to the dependency section in your project.clj file.
Wrench requires 1.9 version of Clojure.
## Usage
Simplest way to use it is to define a config with a simple `def`.
For instance, if you want to read environment variable `USER` you would do following:```clojure
(require '[wrench.core :as cfg])
(cfg/def user)
```You can also customize name of the variable and provide specification:
```clojure
(cfg/def port {:name "HTTP_PORT", :spec int?})
```In this case loaded value would be coerced to an int.
Following specs are coerced to corresponding values: `string?`, `int?`, `double?`, `boolean?`, `keyword?`.
EVerything else is coerced to edn.There are plenty of other options:
- `:doc` will be symbol's documentation
- `:spec` spec-compatible (including any predicate) to validate the value, defaults to `string?`
- `:name` name of the environment variable, defaults to uppercased name of the var (ignoring namespace) with dashes replaced with underscores
- `:require` fails validation, if value is missing, default is `false`
- `:default` to provide a fallback value if it is missing, default is nil
- `:secret` to hide value from `*out*` during validation, default is `false````clojure
(cfg/def oauth-secret {:doc "OAuth secret to validate token"
:require true
:secret true})(cfg/def host {:doc "Remote host for a dependency service"
:name "SERVICE_NAME_HOST"
:require true})
```Then use those vars as you would use any other constant, i.e.:
```clojure
(cfg/def port {:name "NREPL_PORT"
:spec int?
:default 7888})(mount/defstate nrepl-server
:start (nrepl-server/start-server :port port)
:stop (nrepl-server/stop-server nrepl-server))
```If a value does not pass validation, `::cfg/invalid` will be used.
To ensure you have everything configured properly, validate your config before app starts:
```clojure
(defn -main [& args]
(println "Starting service!")
(if-not (cfg/validate-and-print)
(System/exit 1)))
```If everything is alright, then configuration will be printed to `*out*`,
replacing values marked as `:secret` with ``:
```
Loaded config:
- #'some.service/port 8080
- #'some.service/token
```
If there were errors during validation
or required keys are missing, then aggregated summary will be printed and `false` returned:```
Failed to load config:
- configuration #'some.service/token is required and is missing
- configuration #'some.service/port present, but does not conform spec: something-wrong
```## Testing
Idiomatic `with-redefs` could be used to alter var's value:
```clojure
;; service.clj
(ns some.service
(:require [wrench.core :as cfg]))
(cfg/def user {:default "Rich"});; service-test.clj
(ns some.service-test
(:require [clojure.test :refer :all]
[some.service :as svc]))
(deftest a-test
(testing "Let's talk about javascript"
(with-redefs [svc/user "David"]
(is (= svc/user "David")))))
```You can also permanently alter config, by providing alternative values to vars:
```clojure
(cfg/reset! :var {#'svc/user "David"})
```or environment variables
```clojure
(cfg/reset! :env {"PORTS" "[8080 8081 8082]"})
; or load from file
(cfg/reset! :env (cfg/from-file "dev-config.edn"))
;; or in combination
(cfg/reset! :env (from-file "dev-config.edn")
:var {#'ports [8080 8081]})
```Those changes will be applied to global scope, your experience may vary depending on your test runner.
## REPL and reloaded workflow
If during REPL development you ever need whole configuration map, it is available using:
```clojure
(cfg/config)
```Note, that wrench relies on default reloading mechanics, maning that changes in environment variables
or in external config file would not trigger reloading variables with
`(clojure.tools.namespace.repl/refresh)`.To mitigate this you could either use `refresh-all` or reload config manually before your system starts:
```clojure
(defn reset "reloads modified source files, and restarts the states" []
(stop)
(cfg/reset! :env (cfg/from-file "dev-config.edn"))
(ns-tools/refresh :after 'user/go))
```## License
Copyright © 2018 Anton Chebotaev
Distributed under the Eclipse Public License either version 1.0 or (at
your option) any later version.