# Quick start guide

`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`:

    library(potions)

    brew(x = 1)

    paste0("The value of x is ", pour("x"))
    #> [1] "The value of x is 1"

    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")`:

    options("digits") # set to 7 by default
    #> $digits
    #> [1] 7
    print(pi)
    #> [1] 3.141593

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:

    library(potions)
    brew(digits = 3)

    print(pi, digits = pour("digits")) # using potions
    #> [1] 3.14
    print(pi) # default is unaffected
    #> [1] 3.141593

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.

    # 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.

    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).
- 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 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:

    .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.

    .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:

    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()`:

    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()`:

    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)
      }
    }