---
title: "Custom Checks"
author: "Hannah Frick"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Custom Checks}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

By default, the `goodpractice()` function and its alias `gp()` run all the checks available in the package (use `all_checks()` to show all checks implemented). In addition, users can provide their own custom checks.

## What's happening inside of `gp()`?

The `gp()` function essentially carries out two types of steps:

- First, it runs preparation steps for the checks, such as calculating test coverage or cyclomatic complexity.
- Then it carries out the checks, e.g., if cyclomatic complexity exceeds a threshold.

The results of the preparation steps and the checks are added to the return object, also referred to as the state. The print method accesses the check results and prints the advice for the failed checks - or praise if none fail.


## Writing custom checks

### Checks without corresponding preparation steps

Custom checks can be created with the `make_check()` function. As inputs it takes a short `description` of the check, the `check` itself, and the `gp` advice to be given in case the check fails. To illustrate this, let's use a simplified version of the check on `T` and `F` instead of `TRUE` and `FALSE`. 

The `check` argument is a function which takes the state as the input and carries out the check, returning `TRUE` if the check was successful. The state includes the path to the source code of the package and the `checkTnF()` function of the **tools** package can be used to check if `T` or `F` was used. 

```{r}
library(goodpractice)

# make a simple version of the T/F check
check_simple_tf <- make_check(
  
  description = "TRUE and FALSE is used, not T and F",
  gp = "avoid 'T' and 'F', use 'TRUE' and 'FALSE' instead.",
  check = function(state) {
      length(tools::checkTnF(dir = state$path)) == 0
  }
)
```

Additional checks can be used in `gp()` via the `extra_checks` argument. This should be a named list of `check` objects as returned by the `make_check()` function. All checks to be carried out, regardless of whether they are provided by the **goodpractice** package or custom checks, must be named in the `checks` argument to `gp()`.

The check on `T`/`F` implemented in the package gives more helpful advice than this simplified version and returns which files contain `T` and `F` so let's do a quick comparison and run both versions:

```{r}
# get path to example package
pkg_path <- system.file("bad1", package = "goodpractice")
gp(pkg_path, checks = c("simple_tf", "truefalse_not_tf"),
   extra_checks = list(simple_tf = check_simple_tf))
```

### Including a preparation step

Including a preparation step is optional but can be helpful if several checks require the same preparations upfront. In the following example we check for two different fields to be present in the DESCRIPTION file, the URL field and the BugReports field. Both checks can be carried out easily building on a preparation step with the [**desc**](https://cran.r-project.org/package=desc) package for handling DESCRIPTION files.

The checks are linked to the preparation via the prep name: it appears as the `name` argument to `make_prep()`, as the `preps` argument to `make_check()` and finally as the name in the list for the `extra_preps` argument to `gp()`.

```{r}
# prep: process DESCRIPTION file
desc_fun <- function(path, quiet) {
  desc::description$new(path)
}

prep_desc <- make_prep(name = "desc", func = desc_fun)

# check for an URL field
check_url <- make_check(
  description = "URL field in DESCRIPTION",
  preps = "desc",
  gp = "have a URL field in DESCRIPTION",
  check = function(state) state$desc$has_fields("URL")
)

# check for a BugReport field
check_bugreports <- make_check(
  description = "BugReports in DESCRIPTION",
  preps = "desc",
  gp = "add a BugReports field to DESCRIPTION",
  check = function(state) state$desc$has_fields('BugReports')
)

# run the two checks with their corresponding prep step
gp(pkg_path, checks = c("url", "bugreports"),
   extra_preps = list("desc" = prep_desc),
   extra_checks = list("url" = check_url, "bugreports" = check_bugreports))
```

More examples for using custom checks can be found in the [rOpenSci unconf 2017](https://unconf17.ropensci.org/) project [**checkers**](https://github.com/ropensci-archive/checkers) for automated checking of best practices for research compendia.
