diff --git a/docsite/site/content/tutorials/_index.md b/docsite/site/content/tutorials/_index.md index 4c1e556..c1d9605 100644 --- a/docsite/site/content/tutorials/_index.md +++ b/docsite/site/content/tutorials/_index.md @@ -9,5 +9,6 @@ A collection of useful Tutorials Some example use cases for UCG. +* A gentle introduction to ucg while compiling to a json file. * Creating a launch script for a docker container * Recursive Modules \ No newline at end of file diff --git a/docsite/site/content/tutorials/a-json-config.md b/docsite/site/content/tutorials/a-json-config.md new file mode 100644 index 0000000..6cd24c6 --- /dev/null +++ b/docsite/site/content/tutorials/a-json-config.md @@ -0,0 +1,178 @@ ++++ +title = "A simple JSON configuration" +slug = "a-json-config" +weight = 1 +in_search_index = true ++++ + +UCG is a pure functional language, with immutable values, and forward type +inference. It can be used to translate it's various datatypes into a number of +common configuration file formats as well as create some environment variables +and command line flags. + +One of it's intended use cases is to create json configuration files safely. +Lets imagine a webservice that uses json as it's configuration format. It needs +to know a few things to operate properly. + +* The port it should listen on +* The ip address it should listen on +* The database connection information +* The host to check for it's dynamic configuration service (i.e. Consul or Etcd) + +The service expects the config file to look something like this + +```json +{ + "port": 8888, + "addr": "0.0.0.0", + "db": { + "host": "mydbhost", + "port": 3306, + "database": "myservicedb", + }, + "config_svc": "https://consul.internal:8080" +} +``` + +To get started we need to create file config.ucg that to hold our services +configuration. + +```sh +touch config.ucg +``` + +Then we create our configuration starting with a UCG tuple. + +``` +let conf = { + port = 8888, + addr = "0.0.0.0", +}; +``` + +This is our first UCG statement. Statements in UCG start with an optional +keyword, in this case `let` and are terminated by a semicolon (`;`). + +`let` introduces a named binding, in this case, a tuple. Inside the tuple we +have two keys: `port` and `addr`. Quoting a key is optional in UCG if the key +is a single word with all word characters or `_` and no spaces. If you need +dashes or spaces you can quote the key. The first key `port` is a number, +specifically an integer. the second `addr` is a string. + +Now we need to add our database and dynamic configuration service values to our +tuple. + +``` +let db_confs = import "db/mysql/hosts.ucg"; +let consul_hosts = import "services/consul/hosts.ucg".host_pool; + +let conf = { + port = 8888, + addr = "0.0.0.0", + db = { + host = db_confs.host_pool.addr, + port = db_confs.host_pool.port, + database = "myservicedb", + }, + config_svc = consul_hosts.url, +}; +``` + +There are a couple new items here. The first is the UCG import expression. +Import expressions start with the keyword `import` and are followed by a string +specifying the relative or absolute path to a UCG file. Relative import paths +are relative to the file they appear in or if not there then relative the path +specified in the `UCG_IMPORT_PATH` variable. When you import a file all of it's +named bindings are exposed as a UCG tuple. We select specific values from the +`services/consul/hosts.ucg` file as part of the import statement using dot +selectors. + +This file assumes that the `db/mysql/hosts.ucg` import contains a named binding +called `host_pool` that is a tuple with an `addr` field and a `port` field. It +also assumes the `services/consul/hosts.ucg` has a host_pool named binding with +a url field. + +Imports allow us to share our configuration values for shared services among +all of the configurations for their consumers regardless of the format they +expect. + +Let's create those two files now. + +```sh +mkdir -p db/mysql/ +mkdir -p services/consul +touch db/mysql/hosts.ucg +touch db/services/consul/hosts.ucg +``` + +In the `db/mysql/hosts.ucg` file add the following. + +``` +let host_pool = { + addr = "mysql-pool.internal, + port = 3306, +}; +``` + +In the `services/consul/hosts.ucg` file add the following. + +``` +let make_pool = func (addr, port) => { + addr = addr, + port = port, + url = "https://@:@" % (addr, port), +}; + +let host_pool = make_pool("consul.internal", 8080); +``` + +Our `services/consul/hosts.ucg` file is a little more interesting that our +previous files. We introduce UCG functions as well as format strings. A UCG +function starts with the `func` keyword a set of argments the fatcomma (`=>`) +and a valid UCG expression. Functions can close over their environment and they +can reference the variables defined in the argument in the expression. This one +constructs a tuple for us. + +The function also uses a format string to construct our URL for the consul +service. Format strings start with a template string followed by the `%` +character. They are then followed by a parenthesized list with the items to +substitute in the string. + +Format strings also have an alternate form that you can read about in the +reference. + +We have now constructed our config format but we don't have a json file yet. +For that we need an `out` statement. Back in our `config.ucg` file add the +following statement at the bottom: `out json conf;`. + + +``` +let db_confs = import "db/mysql/hosts.ucg"; +let consul_hosts = import "services/consul/hosts.ucg".host_pool; + +let conf = { + port = 8888, + addr = "0.0.0.0", + db = { + host = db_confs.host_pool.addr, + port = db_confs.host_pool.port, + database = "myservicedb", + }, + config_svc = consul_hosts.url, +}; + +out json conf; +``` + +The out statement converts the UCG intermediate representation into the desired +output format. Each output format has a defined translation and in some cases +expected rules that the value should satisfy. Any errors in the translation +will result in a compile error and no output will be generated. In this case we +specify the `json` compile target and the `conf` value we generated. + +Running the following will generate a `config.json` file that looks like our +example above. + +```sh +ucg build config.ucg +``` \ No newline at end of file diff --git a/docsite/site/content/tutorials/recursive-modules.md b/docsite/site/content/tutorials/recursive-modules.md index 6566e2d..4dcdda4 100644 --- a/docsite/site/content/tutorials/recursive-modules.md +++ b/docsite/site/content/tutorials/recursive-modules.md @@ -1,7 +1,7 @@ +++ title = "Recursive Modules and their Uses" slug = "recursive-modules" -weight = 2 +weight = 3 in_search_index = true +++ diff --git a/docsite/site/content/tutorials/script.md b/docsite/site/content/tutorials/script.md index 2a0aecc..7080e40 100644 --- a/docsite/site/content/tutorials/script.md +++ b/docsite/site/content/tutorials/script.md @@ -1,6 +1,6 @@ +++ title = "Creating a launch script for a docker container" -weight = 1 +weight = 2 sort_by = "weight" in_search_index = true +++