---
title: "Quick start guide"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Quick start guide}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```
`potions` is a package for easily storing and retrieving information via 
`options()`. It therefore provides functionality somewhat similar to 
`{settings}`, but with 
syntax based more closely on 
`{here}`. The intended use 
of `potions` is for adding novel information to `options()` for use within 
single packages or workflows. 
## Basic usage
`potions` has three basic functions:
- `brew()` to store data
- `pour()` to retrieve data
- `drain()` to clear data
The first step is to store data using `brew()`, which accepts data in three 
formats:
- Named arguments, e.g. `brew(x = 1)`
- A `list`, e.g. `brew(list(x = 1))`
- A configuration file, e.g. `brew(file = "my-config-file.yml")`
Information stored using `brew` can be retrieved using `pour`:
```{r}
library(potions)
brew(x = 1)
paste0("The value of x is ", pour("x"))
drain()
```
## Interactions with global options
Because `potions` uses a novel `S3` object for all data
storage, it **never overwrites existing global options**, and is therefore safe
to use without affecting existing workflows. For example, `print.default` takes
it's default `digits` argument from `getOption("digits")`:
```{r}
options("digits") # set to 7 by default
print(pi)
```
If we use `potions` to set `digits`, we do not affect this behaviour. Instead,
the user must specifically retrieve data using `pour` for these settings to be 
applied:
```{r}
library(potions)
brew(digits = 3)
print(pi, digits = pour("digits")) # using potions
print(pi) # default is unaffected
```
This feature - i.e. storing data in a novel `S3` object - means that `potions` 
can distinguish between interactive use in the console versus being called 
within a package. Data can be provided and used independently by multiple 
packages, and in the console, without generating conflicts. 
Options stored using `potions` are not persistent across sessions; you will
need to reload options each time you open a new workspace. It is unlikely, 
therefore, that you will need to 'clear' the data stored by `potions` at any
point. If you do need to remove data, you can do so using `drain()` (without
any further arguments).
## Using `config` files
Often it is necessary to share a script, but without sharing certain sensitive
information necessary to run the code. A common example is API keys or other
sensitive information required to download data from a web service. In such 
cases, the default, interactive method of using `brew()` is insufficient, i.e.
```{r eval = FALSE}
# start of script
brew(list("my-secret-key" = "123456")) # shares secret information
```
To avoid this problem, you can instead supply the path to a file containing that
information, i.e.
```{r eval = FALSE}
brew(file = "config.yml") # hides secret information
```
You can then simply add the corresponding file name to your `gitignore`, and 
your script will still run, without sharing sensitive information.
## Using `potions` in package development
When weighing up architectural decisions about how packages should share 
information between functions, there are a few solutions that developers 
can choose between:
- Where a developer needs to be able to call static information across multiple
functions, an efficient solution is to use `sysdata.rda`, which supports
internal use of named objects while avoiding `options()` completely.
- Where a function relies on information stored in `options()`, and for which 
there is no override, it is possible to temporarily reset `options()` within a 
function. In these cases, CRAN requires that the initial state be restored
after use, for which `on.exit()` is a sensible choice (See [Advanced R section 6.7.4](https://adv-r.hadley.nz/functions.html#on-exit)).
- Finally, where there is a need for dynamic, package-wide options that can be
changed by the developer or the user, packages such as `potions` or 
[settings](https://cran.r-project.org/package=settings) can be valuable.
To use `potions` in a package development situation, 
create a file in the `R` directory called `onLoad.R`, containing the following
code:
```{r, eval=FALSE}
.onLoad <- function(libname, pkgname) {
  if(pkgname == "packagenamehere") {
    potions::brew(.pkg = "packagenamehere")
  }
}
```
This is important because it tells `potions` that you are developing a package,
what that package is called, and where future calls to `brew()` from within that
package should place their data. It is also possible to add defaults here, e.g.
```{r, eval=FALSE}
.onLoad <- function(libname, pkgname) {
  if(pkgname == "packagenamehere") {
    potions::brew(
      n_attempts == 5,
      verbose == TRUE,
      .pkg = "packagenamehere")
  }
}
```
Often when developing a package, you will want users to call your own 
configuration function, rather than call `brew()` directly. This provides 
greater control over the names & types of data stored by `potions`, which in 
turn gives you - the developer - greater certainty when calling those data 
*within* your package via `pour()`. For example, you might want to specify that 
a specific argument is supplied as numeric:
```{r, eval = FALSE}
packagename_config <- function(fontsize = 10){
  if(!is.numeric(fontsize)){
    rlang::abort("Argument `fontsize` must be a number")
  }
  brew(list(fontsize = fontsize))
}
```
An additional benefit of writing a wrapper function is to allow users to provide
their own `config` file. The easiest way to do this is to support a `file`
argument within your own function, then pass this directly to `brew()`:
```{r, eval = FALSE}
packagename_config <- function(file = NULL){
  if(!is.null(file)){
    brew(file = file)
  }
}
```
This approach is risky, however, as it doesn't allow any checks. An alternative
is to intercept the file, run your own checks, then pass the result to `brew()`:
```{r, eval = FALSE}
packagename_config <- function(file = NULL){
  if(!is.null(file)){
    config_data <- potions::read_config(x)
    # add any checks to `data` that are needed here
    if(length(names(data)) != length(data)){
      rlang::abort("Not all entries are named!")
    }
    # pass to `brew`
    brew(config_data)
  }
}
```