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