---
title: "Inheritance and R expressions"
output:
  rmarkdown::html_vignette:
    css:
    - !expr system.file("rmarkdown/templates/html_vignette/resources/vignette.css", package = "rmarkdown")
    - ../man/fragments/codeblock.css
vignette: >
  %\VignetteIndexEntry{Inheritance and R expressions}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```


```{r child="../man/fragments/knitr_with_config_hooks.Rmd", include = FALSE}
```

## Defaults and inheritance

The `default` configuration provides a set of values to use when no named configuration is active. Other configurations automatically inherit all `default` values so need only define values specialized for that configuration.

For example, in this configuration the `production` configuration doesn't specify a value for `trials` so it will be read from the `default` configuration:


```{yaml output.var="config_yaml"}
default:
  trials: 5
  dataset: "data-sampled.csv"
  
production:
  dataset: "data.csv"
```



```{r, with_config=TRUE, config_yml="config_yaml"}
config::get(config = "production")
```


All configurations automatically inherit from the `default` configuration. Configurations can also inherit from one or more other named configurations. For example, in this file the `production` configuration inherits from the `test` configuration:


```{yaml, output.var = "config_yaml"}
default:
  trials: 5
  dataset: "data-sampled.csv"

test:
  trials: 30
  dataset: "data-test.csv"
  
production:
  inherits: test
  dataset: "data.csv"
```


```{r, with_config=TRUE, config_yml="config_yaml"}
config::get(config = "production")
```

## Using R code inside the yaml file

You can execute R code within configuration files by prefacing values with `!expr`. This could be useful in cases where you want to base configuration values on environment variables, R options, or even other config files. For example:


```{yaml, output.var = "config_yaml"}
default:
  cores: 2
  debug: true
  server: "localhost:5555"
   
production:
  cores: !expr getOption("mc.cores")
  debug: !expr Sys.getenv("ENABLE_DEBUG") == "1"
  server: !expr config::get("server", file = "etc/server-config.yml")
```


The default result:

```{r, with_config=TRUE, config_yml="config_yaml"}
config::get("server")
```


The production result will depend on the environment variables on the server:


```{r, codeblock_label = "server-config.yml"}
cat(readLines("etc/server-config.yml"), sep = "\n")
```


```{r, with_config=TRUE, config_yml="config_yaml"}
config::get("server", config = "production")
```




### Referencing previously assigned parameters

You can use any previously assigned parameter inside of R code so long as it is assigned directly.


```{yaml, output.var = "config_yaml"}
default:
  file: "data.csv"

test:
  data_dir: "test/out"
  dataset: !expr file.path(data_dir, file)
  
production:
  data_dir: "production/out"
  dataset: !expr file.path(data_dir, file)
```



```{r, with_config=TRUE, config_yml="config_yaml"}
config::get("dataset", config = "test")
```

```{r, with_config=TRUE, config_yml="config_yaml"}
config::get(config = "production")
```


## Limitations of using R expressions

The ability to use R expressions gives substantial flexibility to allow configurations to depend on environment variables, files and other information that's available in the target environment.

However, keep in mind these limitations:

- Any R expression can only refer to elements in the configuration file that it inherits from.
- An R expression can not refer to another R expression.

If the config file violates these limitations, `config::get()` will throw an error.

As an example, let's say you want to construct a configuration that computes a location based on a filename and a folder.

### A valid example

If both `filename` and `folder` are constant (i.e. not computed expressions) this works:


```{yaml, output.var = "config_yaml"}
default:
  filename: "trials.csv"
  folder: "some/path"
  location: !expr file.path(folder, filename)
```


```{r, with_config=TRUE, config_yml="config_yaml", error = TRUE}
config::get("location")
```

### An invalid example

However, if `folder` is also a computed value, then `location` can not be computed and this throws an error:

```{yaml, output.var = "config_yaml"}
default:
  filename: "trials.csv"
  folder: !expr c("some/path")
  location: !expr file.path(folder, filename)
```


```{r, with_config=TRUE, config_yml="config_yaml", error = TRUE}
config::get("location")
```

