{"id":15062804,"url":"https://github.com/walmartlabs/dyn-edn","last_synced_at":"2025-12-12T01:22:09.383Z","repository":{"id":62432118,"uuid":"121806036","full_name":"walmartlabs/dyn-edn","owner":"walmartlabs","description":"Dynamic properties in EDN content","archived":false,"fork":false,"pushed_at":"2020-05-02T17:03:02.000Z","size":26,"stargazers_count":94,"open_issues_count":0,"forks_count":9,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-04-15T12:55:47.393Z","etag":null,"topics":["clojure","configuration","edn"],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/walmartlabs.png","metadata":{"files":{"readme":"README.asciidoc","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-16T22:08:28.000Z","updated_at":"2024-03-12T04:07:03.000Z","dependencies_parsed_at":"2022-11-01T21:01:00.654Z","dependency_job_id":null,"html_url":"https://github.com/walmartlabs/dyn-edn","commit_stats":null,"previous_names":["hlship/dyn-edn"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/walmartlabs%2Fdyn-edn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/walmartlabs%2Fdyn-edn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/walmartlabs%2Fdyn-edn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/walmartlabs%2Fdyn-edn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/walmartlabs","download_url":"https://codeload.github.com/walmartlabs/dyn-edn/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248198888,"owners_count":21063628,"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":["clojure","configuration","edn"],"created_at":"2024-09-24T23:46:46.417Z","updated_at":"2025-12-12T01:22:09.345Z","avatar_url":"https://github.com/walmartlabs.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"== dyn-edn\n\nimage:https://img.shields.io/clojars/v/com.walmartlabs/dyn-edn.svg[Clojars Project, link=\"https://clojars.org/com.walmartlabs/dyn-edn\"]\n\nOften, especially in production, you don't know all of the configuration until\nyour application is actually started. For example, in a cloud provider,\nimportant IP addresses and port numbers are often assigned dynamically.\nThis information is provided to the processes via environment variables.\n\nThis approach has been codified as part of the link:https://12factor.net/config[12-Factor App] manifesto.\n\nHowever, in most Clojure projects, configuration is done primarily in terms of\nlink:https://github.com/edn-format/edn[EDN format] configuration files.\n\nThis library provides a number of reader macros that allow an EDN configuration file\nto reference data provided in environment variables, JVM system properties, or elsewhere.\n\nlink:http://walmartlabs.github.io/apidocs/dyn-edn/[API Documentation]\n\n===  Overview\n\n`dyn-edn` introduces a little bit of indirection into the otherwise fixed content\nof an EDN file, in the form of reader macros.\n\nThe dynamic bits are __properties__:\n\n  * Shell environment variables\n\n  * JVM System properties\n\n  * Explicitly provided properties\n\nThe following reader macros are available:\n\n`#dyn/prop`::\n    Accesses dynamic properties. The value is either a key, or a vector of a key\n    and a default value.\n\n`#dyn/join`::\n    Joins a number of values together to form a single string; this is used when\n    building a single string from a mix of properties and static text.\n\n`#dyn/long`::\n    Converts a string to a long value.  Typically used with `#dyn/prop`.\n\n`#dyn/boolean`::\n    Converts a string to a boolean value.  Typically used with `#dyn/prop`.\n\n`#dyn/keyword`::\n    Converts a string to a keyword value. Typically used with `#dyn/prop`.\n\nHere's an example showing all the variants:\n\n[source,clojure]\n----\n{:connection-pool\n  {:user-name #dyn/prop [DB_USER \"accountsuser\"]\n   :user-pw #dyn/prop DB_PW\n   :url  #dyn/join [\"jdbc:postgresql://\"\n                       #dyn/prop [DB_HOST \"localhost\"]\n                       \":\"\n                       #dyn/prop [DB_PORT \"5432\"]\n                       \"/accounts\"]}\n :web-server\n  {:port #dyn/long #dyn/prop \"WEB_PORT\"}}\n----\n\nIn this example, the `DB_USER`, `DB_PW`, `DB_HOST`, and `DB_PORT`, and `WEB_PORT` environment variables\nall play a role.\n`DB_USER` and `DB_PORT` are optional, since default values have been provided.\n\nLet's assume that the `DB_HOST` environment variable is `db.example.org`,\n`DB_PW` is `change-me`, and `WEB_PORT` was `8192`,\nand the other referenced environment variables are unset.\n\nAfter parsing and reader macro expansion, the resulting data will be:\n\n[source,clojure]\n----\n{:connection-pool\n  {:user-name \"accountsuser\"\n   :user-pw   \"change-me\"\n   :url        \"jdbc:postgresql://db.example.org:5432/accounts\"]}\n :web-server\n  {:port 8192}}\n----\n\nNotice that combining `#dyn/long` and `#dyn/prop` has ensured that the web server port number is present\nas a number, not as a string.\n\n=== Property Lookup\n\nThe `#dyn/prop` macro's value is either single key, or a vector of a key and a default value.\nThe key may be a symbol, keyword, or string.\n\nAn exception is thrown if the property is not found and there is no default.\n\nFor environment variables or JVM system properties, the dynamic value that replaces\nthe macro will always be a string.\nFor explicit properties, the dynamic value is typically a string, but can be any Clojure data: string, number, keyword, even\na map or vector.\n\nUnderneath the covers, the key is used for a simple lookup in a map.\nThe map is a merge of all environment variables, all JVM system properties, and any application-provided\nproperties.\n\nAs a convenience, each environment variable is added to the map _twice_: one using a string key, and once\nwith the string key converted to a symbol.\n\nJVM system properties are added using string keys and string values.\n\nApplication properties are added exactly as provided; typically this means keyword keys and any kind of data values.\n\n=== Usage\n\nThe `env-readers` function returns a map of readers; it may optionally be passed additional\nproperties beyond those obtained from docker secrets, environment variables and JVM system properties.\n\n[source,clojure]\n----\n(require '[clojure.edn :as edn]\n         '[clojure.java.io :as io]\n         '[com.walmartlabs.dyn-edn :refer [env-readers])\n\n(-\u003e\u003e \"config.edn\"\n     io/resource\n     slurp\n     (edn/read-string {:readers (env-readers)})\n----\n\n=== Usage with Docker secrets\n\nIn a docker swarm secrets can be passed to a container when the container is added as a service to the swarm. In the default case,\nthese secrets are mounted from tmpfs at /run/secrets although this location can be specified when the service is\ncreated.\n\ne.g.\n\n[source,sh]\n----\n$ printf \u003csecret\u003e | docker secret create my_secret -\n\n$ docker service create --replicas 1 --name \u003cservicename\u003e \\\n    --secret src=my-secret,target=\"/mysecrets/mysecret\" \\\n    --publish published=8081,target=8081 \u003cimage:tag\u003e\n----\n\nThe above will create a secret called `my-secret` and make it available as a file at `/mysecrets/mysecret`.\n\nThe following can be used to recover the secret within the Docker conainer:\n\n[source,clojure]\n----\n(require '[clojure.edn :as edn]\n         '[clojure.java.io :as io]\n         '[com.walmartlabs.dyn-edn :refer [env-readers])\n\n(-\u003e\u003e \"config.edn\"\n     io/resource\n     slurp\n     (edn/read-string\n       {:readers (env-readers\n                   {:docker-secrets-dir \"/mysecrets\"})})\n----\n\nIf the `:docker-secrets-dir` is not available as a property, it's assumed that all secrets have been mounted at\n`/run/secrets` (which is Docker's default location).\n\nNOTE: In order for secrets to be available under this library, it is assumed that all secrets are mounted in the same directory.\n\nUsing the example above\n\n[source,sh]\n----\n$ printf 5432 | docker secret create DB_PORT -\n\n$ docker service create --replicas 1 --name \u003cservicename\u003e \\\n    --secret DB_PORT \\\n    --publish published=8081,target=8081 \u003cimage:tag\u003e\n----\n\nand if DB_PORT is **not** in the Environment of the container, the outcome will be the same. The value of the parameter\nwill be read from the file at `/run/secrets/DB_HOST` and made available.\n\nNOTE: the value of an env variable with a particular name will overwrite a secret with\nthat name i.e. an environment variable has precedence.\n\n=== License\n\nCopyright (c) [2018]-present, Wal-Mart Store, Inc.\n\nDistributed under the Apache Software License 2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwalmartlabs%2Fdyn-edn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwalmartlabs%2Fdyn-edn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwalmartlabs%2Fdyn-edn/lists"}