---
title: "Configuration files for R projects"
output:
    html:
        meta:
            css: ["@default@1.14.24", "@copy-button@1.14.24", "@article@1.14.24", "assets/tweaks.css"]
            js: ["@copy-button@1.14.24", "@toc-highlight@1.14.24", "@sidenotes@1.14.24"]
            lang: en
        options:
            toc: true
            js_highlight:
                package: prism
                version: 1.29.0

vignette: >
  %\VignetteEngine{litedown::vignette}
  %\VignetteIndexEntry{Configuration files for R projects}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
litedown::reactor(error = TRUE, message = TRUE, print = NA, fig.height = 5)
```

ronfig aims to enable intuitive configuration of R projects via a single
function, `load_config()`. It's raison d'être is to handle the situation where
you have multiple, somewhat overlapping, parameter configurations that you
want to quickly switch between whilst avoiding, potentially error-inducing, copy
and paste.

To keep things simple, rather than introducing another language
for our configuration file, we restrict ourselves to a subset of base R.
By default we allow the following set of functions/operators:

- `<-`, `=`, `+`, `-`, `*`, `:`
- `$`, `[`, `[[`
- `$<-`, `[<-`, `[[<-`
- `c()`
- `as.Date()`
- `array()`, `matrix()`
- `list()`, `data.frame()`
- `Sys.Date()`, `Sys.time()`
- `seq()`, `sequence()`, and `seq_len()`
- `file.path()`
- `modifyList()`

We also define a convenience function, `cc()`, which automatically quotes input
to save typing.

This use of a reduced subset of R is primarily to ensure that loading a
configuration file has no side-effects on a users environment and performs
limited computation. Under the hood, `load_config()` is little more than careful
wrapping of `base::sys.source()` and `utils::modifyList()` with care taken as to
where evaluation takes place.

Should you want more flexibility, the argument `strict = FALSE` can be set. This
enables all functions from base R. In particular, you can then use `::` to call
in to other packages.

## Example

### Configuration file

Configuration files look similar to the following example file that we bundle in
the package:

```{r, comment=''}
file <- system.file("config.R", package = "ronfig")
cat(readChar(file, file.info(file)$size))
```

### Loading

We can easily load any of the example configurations in to a list in our local
environment:

```{r}
library(ronfig)
str(load_config(file))
str(load_config(file, "debug"))
str(load_config(file, "forecast"))
```

### Loading things as they are

If we just wish to load the configuration file as is, we can pass the argument
`as_is = TRUE`. This will load configuration file in to a list. If we specify
a specific configuration, that value will be pulled out and returned directly:

```{r}
str(load_config(file, as_is = TRUE))
str(load_config(file, "forecast", as_is = TRUE))
```


### Using other functions

To make other functions available within the configuration file, users can
inject `carrier::crate()` objects in to the evaluation environment. As an
example, assume we wanted to permit the use of `mean()` within our configuration
file. By default this would error:

```{r}
f <- tempfile()
cat("default <- list(a=mean(1:10))", file = f)
with(load_config(f), a)
```
If we now inject `mean()` as a crated function, things work as desired:

```{r}
crate <- carrier::crate(function(x) mean(x))
with(load_config(f, crates = list(mean = crate)), a)
unlink(f)
```

## Related work

- [config](https://cran.r-project.org/package=config) which uses yaml for
  it's configuration file.

