From ccde263bade096039bf48db43d8df50d1df1e7d3 Mon Sep 17 00:00:00 2001
From: jennybc
Date: Thu, 9 Jan 2025 07:18:17 +0000
Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20hadley/r?=
=?UTF-8?q?-pkgs@d6e3d3f0bdf63f98a28a37545baff50628f1f137=20=F0=9F=9A=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
book-asciidoc/data.adoc | 2 +-
.../dependencies-mindset-background.adoc | 2 +-
book-asciidoc/package-within.adoc | 10 +-
book-asciidoc/preface.adoc | 6 +-
book-asciidoc/testing-basics.adoc | 6 +-
book-asciidoc/testing-design.adoc | 10 +-
book-asciidoc/website.adoc | 8 +-
book-asciidoc/whole-game.adoc | 18 +--
data.html | 4 +-
dependencies-mindset-background.html | 2 +-
package-within.html | 10 +-
preface.html | 6 +-
search.json | 30 ++---
sitemap.xml | 108 +++++++++---------
structure.html | 4 +-
testing-basics.html | 6 +-
testing-design.html | 14 +--
website.html | 12 +-
whole-game.html | 22 ++--
workflow101.html | 8 +-
20 files changed, 144 insertions(+), 144 deletions(-)
diff --git a/book-asciidoc/data.adoc b/book-asciidoc/data.adoc
index 026d77246..e310cd23a 100644
--- a/book-asciidoc/data.adoc
+++ b/book-asciidoc/data.adoc
@@ -48,7 +48,7 @@ If the `+DESCRIPTION+` contains `+LazyData: true+`, then datasets will be lazily
[source,r,cell-code]
----
lobstr::mem_used()
-#> 57.94 MB
+#> 57.95 MB
library(nycflights13)
lobstr::mem_used()
#> 59.70 MB
diff --git a/book-asciidoc/dependencies-mindset-background.adoc b/book-asciidoc/dependencies-mindset-background.adoc
index 818dd481f..f5b121a08 100644
--- a/book-asciidoc/dependencies-mindset-background.adoc
+++ b/book-asciidoc/dependencies-mindset-background.adoc
@@ -205,7 +205,7 @@ sd
#> function (x, na.rm = FALSE)
#> sqrt(var(if (is.vector(x) || is.factor(x)) x else as.double(x),
#> na.rm = na.rm))
-#>
+#>
#>
----
diff --git a/book-asciidoc/package-within.adoc b/book-asciidoc/package-within.adoc
index 7f5465de8..768d4d98d 100644
--- a/book-asciidoc/package-within.adoc
+++ b/book-asciidoc/package-within.adoc
@@ -75,7 +75,7 @@ Finally, this cleaned (cleaner?) data is written back out to a CSV file. They li
now <- Sys.time()
timestamp <- format(now, "%Y-%B-%d_%H-%M-%S")
(outfile <- paste0(timestamp, "_", sub("(.*)([.]csv$)", "\\1_clean\\2", infile)))
-#> [1] "2025-January-08_07-15-06_swim_clean.csv"
+#> [1] "2025-January-09_07-15-32_swim_clean.csv"
write.csv(dat, file = outfile, quote = FALSE, row.names = FALSE)
----
@@ -503,7 +503,7 @@ The timestamps now reflect the current time, but the group raises a new concern.
[source,r,cell-code]
----
format(Sys.time(), "%Y-%B-%d_%H-%M-%S")
-#> [1] "2025-January-08_07-15-07"
+#> [1] "2025-January-09_07-15-32"
----
This formats `+Sys.time()+` in such a way that it includes the month _name_ (not number) and the local timefootnote:[It would clearly be better to format according to ISO 8601, which encodes the month by number, but please humor me for the sake of making this example more obvious.].
@@ -549,7 +549,7 @@ format(Sys.time(), "%Y-%B-%d_%H-%M-%S")
----
....
-#> [1] "2025-janeiro-08_04-15-07"
+#> [1] "2025-janeiro-09_04-15-33"
....
After:
@@ -557,10 +557,10 @@ After:
[source,r,cell-code]
----
outfile_path("INFILE.csv")
-#> [1] "2025-January-08_07-15-07_INFILE_clean.csv"
+#> [1] "2025-January-09_07-15-32_INFILE_clean.csv"
format(Sys.time(), "%Y-%B-%d_%H-%M-%S")
-#> [1] "2025-January-08_07-15-07"
+#> [1] "2025-January-09_07-15-33"
----
Notice that her month name switched from Portuguese to English and the time is clearly being reported in a different time zone. The calls to `+Sys.setlocale()+` and `+Sys.setenv()+` inside `+timestamp()+` have made persistent (and very surprising) changes to her R session. This sort of side effect is very undesirable and is extremely difficult to track down and debug, especially in more complicated settings.
diff --git a/book-asciidoc/preface.adoc b/book-asciidoc/preface.adoc
index cda1a1188..162e2645b 100644
--- a/book-asciidoc/preface.adoc
+++ b/book-asciidoc/preface.adoc
@@ -113,7 +113,7 @@ devtools::session_info()
#> collate C.UTF-8
#> ctype C.UTF-8
#> tz UTC
-#> date 2025-01-08
+#> date 2025-01-09
#> pandoc 2.9.2.1 @ /usr/bin/ (via rmarkdown)
#>
#> ─ Packages ───────────────────────────────────────────────────────
@@ -151,7 +151,7 @@ devtools::session_info()
#> mime 0.12 2021-09-28 [1] RSPM
#> miniUI 0.1.1.1 2018-05-18 [1] RSPM
#> munsell 0.5.1 2024-04-01 [1] RSPM
-#> pillar 1.10.0 2024-12-17 [1] RSPM
+#> pillar 1.10.1 2025-01-07 [1] RSPM
#> pkgbuild 1.4.5 2024-10-28 [1] RSPM
#> pkgconfig 2.0.3 2019-09-22 [1] RSPM
#> pkgload 1.4.0 2024-06-28 [1] RSPM
@@ -182,7 +182,7 @@ devtools::session_info()
#> vctrs 0.6.5 2023-12-01 [1] RSPM
#> vroom 1.6.5 2023-12-05 [1] RSPM
#> withr 3.0.2 2024-10-28 [1] RSPM
-#> xfun 0.49 2024-10-31 [1] RSPM
+#> xfun 0.50 2025-01-07 [1] RSPM
#> xml2 1.3.6 2023-12-04 [1] RSPM
#> xtable 1.8-4 2019-04-21 [1] RSPM
#>
diff --git a/book-asciidoc/testing-basics.adoc b/book-asciidoc/testing-basics.adoc
index 0a4491fd5..cfdc4f0a2 100644
--- a/book-asciidoc/testing-basics.adoc
+++ b/book-asciidoc/testing-basics.adoc
@@ -265,18 +265,18 @@ test_that("basic duplication works", {
expect_equal(str_dup(c("a", "b"), 2), c("aa", "bb"))
expect_equal(str_dup(c("a", "b"), c(2, 3)), c("aa", "bbb"))
})
-#> Test passed 😸
+#> Test passed 🎊
test_that("0 duplicates equals empty string", {
expect_equal(str_dup("a", 0), "")
expect_equal(str_dup(c("a", "b"), 0), rep("", 2))
})
-#> Test passed 😸
+#> Test passed 🎉
test_that("uses tidyverse recycling rules", {
expect_error(str_dup(1:2, 1:3), class = "vctrs_error_incompatible_size")
})
-#> Test passed 🥇
+#> Test passed 🎊
----
This file shows a typical mix of tests:
diff --git a/book-asciidoc/testing-design.adoc b/book-asciidoc/testing-design.adoc
index 85b288448..043795d8a 100644
--- a/book-asciidoc/testing-design.adoc
+++ b/book-asciidoc/testing-design.adoc
@@ -176,7 +176,7 @@ test_that("landscape changes leak outside the test", {
expect_equal(getOption("opt_whatever"), "whatever")
expect_equal(Sys.getenv("envvar_whatever"), "whatever")
})
-#> Test passed 🌈
+#> Test passed 🥳
grep("jsonlite", search(), value = TRUE)
#> [1] "package:jsonlite"
@@ -233,7 +233,7 @@ test_that("withr makes landscape changes local to a test", {
expect_equal(getOption("opt_whatever"), "whatever")
expect_equal(Sys.getenv("envvar_whatever"), "whatever")
})
-#> Test passed 😸
+#> Test passed 😀
grep("jsonlite", search(), value = TRUE)
#> character(0)
@@ -330,7 +330,7 @@ test_that("subtraction works", {
useful_thing <- 3
expect_equal(5 - useful_thing, 2)
})
-#> Test passed 🎊
+#> Test passed 🌈
----
In real life, `+useful_thing+` is usually a more complicated object that somehow feels burdensome to instantiate. Notice how `+useful_thing <- 3+` appears in more than one place. Conventional wisdom says we should DRY this code out. It’s tempting to just move `+useful_thing+`’s definition outside of the tests:
@@ -342,12 +342,12 @@ useful_thing <- 3
test_that("multiplication works", {
expect_equal(2 * useful_thing, 6)
})
-#> Test passed 🥳
+#> Test passed 😀
test_that("subtraction works", {
expect_equal(5 - useful_thing, 2)
})
-#> Test passed 🥇
+#> Test passed 😸
----
But we really do think the first form, with the repetition, is often the better choice.
diff --git a/book-asciidoc/website.adoc b/book-asciidoc/website.adoc
index 465a796cf..7d2214b39 100644
--- a/book-asciidoc/website.adoc
+++ b/book-asciidoc/website.adoc
@@ -28,7 +28,7 @@ usethis::use_pkgdown()
----
....
-#> ✔ Setting active project to "/tmp/RtmpQ8Xj2M/mypackage".
+#> ✔ Setting active project to "/tmp/RtmpJEIQm9/mypackage".
#> ✔ Adding "^_pkgdown\\.yml$", "^docs$", and "^pkgdown$" to
#> '.Rbuildignore'.
#> ✔ Adding "docs" to '.gitignore'.
@@ -51,7 +51,7 @@ pkgdown::build_site()
----
....
-#> ✔ Setting active project to "/tmp/RtmpQ8Xj2M/mypackage".
+#> ✔ Setting active project to "/tmp/RtmpJEIQm9/mypackage".
#> ── Installing package mypackage into temporary library ─────────────
#> ── Initialising site ───────────────────────────────────────────────────────────
#> Copying /BS5/assets/katex-auto.js to katex-auto.js
@@ -84,8 +84,8 @@ pkgdown::build_site()
#> Updating deps/search-1.0.0/fuse.min.js
#> Updating deps/search-1.0.0/mark.min.js
#> ── Building pkgdown site for package mypackage ─────────────────────────────────
-#> Reading from: /tmp/RtmpQ8Xj2M/mypackage
-#> Writing to: /tmp/RtmpQ8Xj2M/mypackage/docs
+#> Reading from: /tmp/RtmpJEIQm9/mypackage
+#> Writing to: /tmp/RtmpJEIQm9/mypackage/docs
#> ── Sitrep ──────────────────────────────────────────────────────────────────────
#> ✖ URLs not ok.
#> In _pkgdown.yml, url is missing.
diff --git a/book-asciidoc/whole-game.adoc b/book-asciidoc/whole-game.adoc
index 135aaa68d..7720eb7ed 100644
--- a/book-asciidoc/whole-game.adoc
+++ b/book-asciidoc/whole-game.adoc
@@ -71,8 +71,8 @@ create_package("~/path/to/regexcite")
For the creation of this book we have to work in a temporary directory, because the book is built non-interactively in the cloud. Behind the scenes, we’re executing our own `+create_package()+` command, but don’t be surprised if our output differs a bit from yours.
....
-#> ✔ Creating '/tmp/RtmpvS3Toa/regexcite/'.
-#> ✔ Setting active project to "/tmp/RtmpvS3Toa/regexcite".
+#> ✔ Creating '/tmp/RtmpwIwJgQ/regexcite/'.
+#> ✔ Setting active project to "/tmp/RtmpwIwJgQ/regexcite".
#> ✔ Creating 'R/'.
#> ✔ Writing 'DESCRIPTION'.
#> Package: regexcite
@@ -159,7 +159,7 @@ Click on History (the clock icon in the Git pane) and, if you consented, you wil
[width="100%",cols="<21%,<59%,<20%",options="header",]
|===
|commit |author |message
-|c2ae46871b… |jennybc jennybc@users.noreply.github.com |Initial commit
+|2e021ee080… |jennybc jennybc@users.noreply.github.com |Initial commit
|===
[TIP]
@@ -512,14 +512,14 @@ install()
....
── R CMD build ─────────────────────────────────────────────────────
-* checking for file ‘/tmp/RtmpvS3Toa/regexcite/DESCRIPTION’ ... OK
+* checking for file ‘/tmp/RtmpwIwJgQ/regexcite/DESCRIPTION’ ... OK
* preparing ‘regexcite’:
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files and shell scripts
* checking for empty or unneeded directories
* building ‘regexcite_0.0.0.9000.tar.gz’
Running /opt/R/4.4.2/lib/R/bin/R CMD INSTALL \
- /tmp/RtmpvS3Toa/regexcite_0.0.0.9000.tar.gz --install-tests
+ /tmp/RtmpwIwJgQ/regexcite_0.0.0.9000.tar.gz --install-tests
* installing to library ‘/home/runner/work/_temp/Library’
* installing *source* package ‘regexcite’ ...
** using staged installation
@@ -874,7 +874,7 @@ The very best way to render `+README.Rmd+` is with `+build_readme()+`, because i
----
build_readme()
#> ℹ Installing regexcite in temporary library
-#> ℹ Building '/tmp/RtmpvS3Toa/regexcite/README.Rmd'
+#> ℹ Building '/tmp/RtmpwIwJgQ/regexcite/README.Rmd'
----
You can see the rendered `+README.md+` simply by https://github.com/jennybc/regexcite#readme[visiting regexcite on GitHub].
@@ -892,7 +892,7 @@ check()
....
── R CMD check results ─────────────────── regexcite 0.0.0.9000 ────
-Duration: 10s
+Duration: 9.9s
0 errors ✔ | 0 warnings ✔ | 0 notes ✔
....
@@ -906,7 +906,7 @@ install()
....
── R CMD build ─────────────────────────────────────────────────────
-* checking for file ‘/tmp/RtmpvS3Toa/regexcite/DESCRIPTION’ ... OK
+* checking for file ‘/tmp/RtmpwIwJgQ/regexcite/DESCRIPTION’ ... OK
* preparing ‘regexcite’:
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files and shell scripts
@@ -914,7 +914,7 @@ install()
Removed empty directory ‘regexcite/tests/testthat/_snaps’
* building ‘regexcite_0.0.0.9000.tar.gz’
Running /opt/R/4.4.2/lib/R/bin/R CMD INSTALL \
- /tmp/RtmpvS3Toa/regexcite_0.0.0.9000.tar.gz --install-tests
+ /tmp/RtmpwIwJgQ/regexcite_0.0.0.9000.tar.gz --install-tests
* installing to library ‘/home/runner/work/_temp/Library’
* installing *source* package ‘regexcite’ ...
** using staged installation
diff --git a/data.html b/data.html
index 48b47e469..f2ef6e89c 100644
--- a/data.html
+++ b/data.html
@@ -460,14 +460,14 @@
If the DESCRIPTION contains LazyData: true, then datasets will be lazily loaded. This means that they won’t occupy any memory until you use them. The following example shows memory usage before and after loading the nycflights13 package. You can see that memory usage doesn’t change significantly until you inspect the flights dataset stored inside the package.
We recommend that you include LazyData: true in your DESCRIPTION if you are shipping .rda files below data/. If you use use_data() to create such datasets, it will automatically make this modification to DESCRIPTION for you.
It’s defined in terms of another function, var(), also from the stats package. So what happens if we override var() with our own definition? Does it break sd()?
This formats Sys.time() in such a way that it includes the month name (not number) and the local time6.
Table 5.1 shows what happens when such a timestamp is produced by several hypothetical colleagues cleaning some data at exactly the same instant in time.
Notice that her month name switched from Portuguese to English and the time is clearly being reported in a different time zone. The calls to Sys.setlocale() and Sys.setenv() inside timestamp() have made persistent (and very surprising) changes to her R session. This sort of side effect is very undesirable and is extremely difficult to track down and debug, especially in more complicated settings.
Prefa
#> vctrs 0.6.5 2023-12-01 [1] RSPM#> vroom 1.6.5 2023-12-05 [1] RSPM#> withr 3.0.2 2024-10-28 [1] RSPM
-#> xfun 0.49 2024-10-31 [1] RSPM
+#> xfun 0.50 2025-01-07 [1] RSPM#> xml2 1.3.6 2023-12-04 [1] RSPM#> xtable 1.8-4 2019-04-21 [1] RSPM#>
diff --git a/search.json b/search.json
index 7f2b86f68..8659d7f6f 100644
--- a/search.json
+++ b/search.json
@@ -44,7 +44,7 @@
"href": "preface.html#colophon",
"title": "Preface",
"section": "Colophon",
- "text": "Colophon\nThis book was authored using Quarto inside RStudio. The website is hosted with Netlify, and automatically updated after every commit by GitHub actions. The complete source is available from GitHub.\nThis version of the book was built with:\n\nlibrary(devtools)\n#> Loading required package: usethis\nlibrary(roxygen2)\nlibrary(testthat)\n#> \n#> Attaching package: 'testthat'\n#> The following object is masked from 'package:devtools':\n#> \n#> test_file\n#> The following object is masked from 'package:dplyr':\n#> \n#> matches\n#> The following object is masked from 'package:purrr':\n#> \n#> is_null\n#> The following objects are masked from 'package:readr':\n#> \n#> edition_get, local_edition\n#> The following object is masked from 'package:tidyr':\n#> \n#> matches\ndevtools::session_info()\n#> ─ Session info ───────────────────────────────────────────────────\n#> setting value\n#> version R version 4.4.2 (2024-10-31)\n#> os Ubuntu 22.04.5 LTS\n#> system x86_64, linux-gnu\n#> ui X11\n#> language (EN)\n#> collate C.UTF-8\n#> ctype C.UTF-8\n#> tz UTC\n#> date 2025-01-08\n#> pandoc 2.9.2.1 @ /usr/bin/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────────────\n#> package * version date (UTC) lib source\n#> bit 4.5.0.1 2024-12-03 [1] RSPM\n#> bit64 4.5.2 2024-09-22 [1] RSPM\n#> brio 1.1.5 2024-04-24 [1] RSPM\n#> cachem 1.1.0 2024-05-16 [1] RSPM\n#> cli 3.6.3 2024-06-21 [1] RSPM\n#> colorspace 2.1-1 2024-07-26 [1] RSPM\n#> crayon 1.5.3 2024-06-20 [1] RSPM\n#> devtools * 2.4.5 2022-10-11 [1] RSPM\n#> digest 0.6.37 2024-08-19 [1] RSPM\n#> dplyr * 1.1.4 2023-11-17 [1] RSPM\n#> ellipsis 0.3.2 2021-04-29 [1] RSPM\n#> evaluate 1.0.1 2024-10-10 [1] RSPM\n#> fastmap 1.2.0 2024-05-15 [1] RSPM\n#> forcats * 1.0.0 2023-01-29 [1] RSPM\n#> fs 1.6.5 2024-10-30 [1] RSPM\n#> generics 0.1.3 2022-07-05 [1] RSPM\n#> ggplot2 * 3.5.1 2024-04-23 [1] RSPM\n#> glue 1.8.0 2024-09-30 [1] RSPM\n#> gtable 0.3.6 2024-10-25 [1] RSPM\n#> hms 1.1.3 2023-03-21 [1] RSPM\n#> htmltools 0.5.8.1 2024-04-04 [1] RSPM\n#> htmlwidgets 1.6.4 2023-12-06 [1] RSPM\n#> httpuv 1.6.15 2024-03-26 [1] RSPM\n#> jsonlite 1.8.9 2024-09-20 [1] RSPM\n#> knitr 1.49 2024-11-08 [1] RSPM\n#> later 1.4.1 2024-11-27 [1] RSPM\n#> lifecycle 1.0.4 2023-11-07 [1] RSPM\n#> lubridate * 1.9.4 2024-12-08 [1] RSPM\n#> magrittr 2.0.3 2022-03-30 [1] RSPM\n#> memoise 2.0.1 2021-11-26 [1] RSPM\n#> mime 0.12 2021-09-28 [1] RSPM\n#> miniUI 0.1.1.1 2018-05-18 [1] RSPM\n#> munsell 0.5.1 2024-04-01 [1] RSPM\n#> pillar 1.10.0 2024-12-17 [1] RSPM\n#> pkgbuild 1.4.5 2024-10-28 [1] RSPM\n#> pkgconfig 2.0.3 2019-09-22 [1] RSPM\n#> pkgload 1.4.0 2024-06-28 [1] RSPM\n#> profvis 0.4.0 2024-09-20 [1] RSPM\n#> promises 1.3.2 2024-11-28 [1] RSPM\n#> purrr * 1.0.2 2023-08-10 [1] RSPM\n#> R6 2.5.1 2021-08-19 [1] RSPM\n#> Rcpp 1.0.13-1 2024-11-02 [1] RSPM\n#> readr * 2.1.5 2024-01-10 [1] RSPM\n#> remotes 2.5.0 2024-03-17 [1] RSPM\n#> rlang 1.1.4 2024-06-04 [1] RSPM\n#> rmarkdown 2.29 2024-11-04 [1] RSPM\n#> roxygen2 * 7.3.2 2024-06-28 [1] RSPM\n#> scales 1.3.0 2023-11-28 [1] RSPM\n#> sessioninfo 1.2.2 2021-12-06 [1] RSPM\n#> shiny 1.10.0 2024-12-14 [1] RSPM\n#> stringi 1.8.4 2024-05-06 [1] RSPM\n#> stringr * 1.5.1 2023-11-14 [1] RSPM\n#> testthat * 3.2.2 2024-12-10 [1] RSPM\n#> tibble * 3.2.1 2023-03-20 [1] RSPM\n#> tidyr * 1.3.1 2024-01-24 [1] RSPM\n#> tidyselect 1.2.1 2024-03-11 [1] RSPM\n#> tidyverse * 2.0.0 2023-02-22 [1] RSPM\n#> timechange 0.3.0 2024-01-18 [1] RSPM\n#> tzdb 0.4.0 2023-05-12 [1] RSPM\n#> urlchecker 1.0.1 2021-11-30 [1] RSPM\n#> usethis * 3.1.0 2024-11-26 [1] RSPM\n#> vctrs 0.6.5 2023-12-01 [1] RSPM\n#> vroom 1.6.5 2023-12-05 [1] RSPM\n#> withr 3.0.2 2024-10-28 [1] RSPM\n#> xfun 0.49 2024-10-31 [1] RSPM\n#> xml2 1.3.6 2023-12-04 [1] RSPM\n#> xtable 1.8-4 2019-04-21 [1] RSPM\n#> \n#> [1] /home/runner/work/_temp/Library\n#> [2] /opt/R/4.4.2/lib/R/site-library\n#> [3] /opt/R/4.4.2/lib/R/library\n#> \n#> ──────────────────────────────────────────────────────────────────\n\n\n\n\n\nMüller, Kirill, and Lorenz Walthert. 2018. Styler: Non-Invasive Pretty Printing of R Code. http://styler.r-lib.org.",
+ "text": "Colophon\nThis book was authored using Quarto inside RStudio. The website is hosted with Netlify, and automatically updated after every commit by GitHub actions. The complete source is available from GitHub.\nThis version of the book was built with:\n\nlibrary(devtools)\n#> Loading required package: usethis\nlibrary(roxygen2)\nlibrary(testthat)\n#> \n#> Attaching package: 'testthat'\n#> The following object is masked from 'package:devtools':\n#> \n#> test_file\n#> The following object is masked from 'package:dplyr':\n#> \n#> matches\n#> The following object is masked from 'package:purrr':\n#> \n#> is_null\n#> The following objects are masked from 'package:readr':\n#> \n#> edition_get, local_edition\n#> The following object is masked from 'package:tidyr':\n#> \n#> matches\ndevtools::session_info()\n#> ─ Session info ───────────────────────────────────────────────────\n#> setting value\n#> version R version 4.4.2 (2024-10-31)\n#> os Ubuntu 22.04.5 LTS\n#> system x86_64, linux-gnu\n#> ui X11\n#> language (EN)\n#> collate C.UTF-8\n#> ctype C.UTF-8\n#> tz UTC\n#> date 2025-01-09\n#> pandoc 2.9.2.1 @ /usr/bin/ (via rmarkdown)\n#> \n#> ─ Packages ───────────────────────────────────────────────────────\n#> package * version date (UTC) lib source\n#> bit 4.5.0.1 2024-12-03 [1] RSPM\n#> bit64 4.5.2 2024-09-22 [1] RSPM\n#> brio 1.1.5 2024-04-24 [1] RSPM\n#> cachem 1.1.0 2024-05-16 [1] RSPM\n#> cli 3.6.3 2024-06-21 [1] RSPM\n#> colorspace 2.1-1 2024-07-26 [1] RSPM\n#> crayon 1.5.3 2024-06-20 [1] RSPM\n#> devtools * 2.4.5 2022-10-11 [1] RSPM\n#> digest 0.6.37 2024-08-19 [1] RSPM\n#> dplyr * 1.1.4 2023-11-17 [1] RSPM\n#> ellipsis 0.3.2 2021-04-29 [1] RSPM\n#> evaluate 1.0.1 2024-10-10 [1] RSPM\n#> fastmap 1.2.0 2024-05-15 [1] RSPM\n#> forcats * 1.0.0 2023-01-29 [1] RSPM\n#> fs 1.6.5 2024-10-30 [1] RSPM\n#> generics 0.1.3 2022-07-05 [1] RSPM\n#> ggplot2 * 3.5.1 2024-04-23 [1] RSPM\n#> glue 1.8.0 2024-09-30 [1] RSPM\n#> gtable 0.3.6 2024-10-25 [1] RSPM\n#> hms 1.1.3 2023-03-21 [1] RSPM\n#> htmltools 0.5.8.1 2024-04-04 [1] RSPM\n#> htmlwidgets 1.6.4 2023-12-06 [1] RSPM\n#> httpuv 1.6.15 2024-03-26 [1] RSPM\n#> jsonlite 1.8.9 2024-09-20 [1] RSPM\n#> knitr 1.49 2024-11-08 [1] RSPM\n#> later 1.4.1 2024-11-27 [1] RSPM\n#> lifecycle 1.0.4 2023-11-07 [1] RSPM\n#> lubridate * 1.9.4 2024-12-08 [1] RSPM\n#> magrittr 2.0.3 2022-03-30 [1] RSPM\n#> memoise 2.0.1 2021-11-26 [1] RSPM\n#> mime 0.12 2021-09-28 [1] RSPM\n#> miniUI 0.1.1.1 2018-05-18 [1] RSPM\n#> munsell 0.5.1 2024-04-01 [1] RSPM\n#> pillar 1.10.1 2025-01-07 [1] RSPM\n#> pkgbuild 1.4.5 2024-10-28 [1] RSPM\n#> pkgconfig 2.0.3 2019-09-22 [1] RSPM\n#> pkgload 1.4.0 2024-06-28 [1] RSPM\n#> profvis 0.4.0 2024-09-20 [1] RSPM\n#> promises 1.3.2 2024-11-28 [1] RSPM\n#> purrr * 1.0.2 2023-08-10 [1] RSPM\n#> R6 2.5.1 2021-08-19 [1] RSPM\n#> Rcpp 1.0.13-1 2024-11-02 [1] RSPM\n#> readr * 2.1.5 2024-01-10 [1] RSPM\n#> remotes 2.5.0 2024-03-17 [1] RSPM\n#> rlang 1.1.4 2024-06-04 [1] RSPM\n#> rmarkdown 2.29 2024-11-04 [1] RSPM\n#> roxygen2 * 7.3.2 2024-06-28 [1] RSPM\n#> scales 1.3.0 2023-11-28 [1] RSPM\n#> sessioninfo 1.2.2 2021-12-06 [1] RSPM\n#> shiny 1.10.0 2024-12-14 [1] RSPM\n#> stringi 1.8.4 2024-05-06 [1] RSPM\n#> stringr * 1.5.1 2023-11-14 [1] RSPM\n#> testthat * 3.2.2 2024-12-10 [1] RSPM\n#> tibble * 3.2.1 2023-03-20 [1] RSPM\n#> tidyr * 1.3.1 2024-01-24 [1] RSPM\n#> tidyselect 1.2.1 2024-03-11 [1] RSPM\n#> tidyverse * 2.0.0 2023-02-22 [1] RSPM\n#> timechange 0.3.0 2024-01-18 [1] RSPM\n#> tzdb 0.4.0 2023-05-12 [1] RSPM\n#> urlchecker 1.0.1 2021-11-30 [1] RSPM\n#> usethis * 3.1.0 2024-11-26 [1] RSPM\n#> vctrs 0.6.5 2023-12-01 [1] RSPM\n#> vroom 1.6.5 2023-12-05 [1] RSPM\n#> withr 3.0.2 2024-10-28 [1] RSPM\n#> xfun 0.50 2025-01-07 [1] RSPM\n#> xml2 1.3.6 2023-12-04 [1] RSPM\n#> xtable 1.8-4 2019-04-21 [1] RSPM\n#> \n#> [1] /home/runner/work/_temp/Library\n#> [2] /opt/R/4.4.2/lib/R/site-library\n#> [3] /opt/R/4.4.2/lib/R/library\n#> \n#> ──────────────────────────────────────────────────────────────────\n\n\n\n\n\nMüller, Kirill, and Lorenz Walthert. 2018. Styler: Non-Invasive Pretty Printing of R Code. http://styler.r-lib.org.",
"crumbs": [
"Preface"
]
@@ -137,7 +137,7 @@
"href": "whole-game.html#create_package",
"title": "1 The Whole Game",
"section": "\n1.4 create_package()\n",
- "text": "1.4 create_package()\n\nCall create_package() to initialize a new package in a directory on your computer. create_package() will automatically create that directory if it doesn’t exist yet (and that is usually the case). See Section 4.1 for more on creating packages.\nMake a deliberate choice about where to create this package on your computer. It should probably be somewhere within your home directory, alongside your other R projects. It should not be nested inside another RStudio Project, R package, or Git repo. Nor should it be in an R package library, which holds packages that have already been built and installed. The conversion of the source package we create here into an installed package is part of what devtools facilitates. Don’t try to do devtools’ job for it!\nOnce you’ve selected where to create this package, substitute your chosen path into a create_package() call like this:\n\ncreate_package(\"~/path/to/regexcite\")\n\nFor the creation of this book we have to work in a temporary directory, because the book is built non-interactively in the cloud. Behind the scenes, we’re executing our own create_package() command, but don’t be surprised if our output differs a bit from yours.\n\n#> ✔ Creating '/tmp/Rtmp9zEBZC/regexcite/'.\n#> ✔ Setting active project to \"/tmp/Rtmp9zEBZC/regexcite\".\n#> ✔ Creating 'R/'.\n#> ✔ Writing 'DESCRIPTION'.\n#> Package: regexcite\n#> Title: What the Package Does (One Line, Title Case)\n#> Version: 0.0.0.9000\n#> Authors@R (parsed):\n#> * First Last <first.last@example.com> [aut, cre]\n#> Description: What the package does (one paragraph).\n#> License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a\n#> license\n#> Encoding: UTF-8\n#> Roxygen: list(markdown = TRUE)\n#> RoxygenNote: 7.3.2\n#> ✔ Writing 'NAMESPACE'.\n#> ✔ Writing 'regexcite.Rproj'.\n#> ✔ Adding \"^regexcite\\\\.Rproj$\" to '.Rbuildignore'.\n#> ✔ Adding \".Rproj.user\" to '.gitignore'.\n#> ✔ Adding \"^\\\\.Rproj\\\\.user$\" to '.Rbuildignore'.\n#> ✔ Setting active project to \"<no active project>\".\n\nIf you’re working in RStudio, you should find yourself in a new instance of RStudio, opened into your new regexcite package (and Project). If you somehow need to do this manually, navigate to the directory and double click on regexcite.Rproj. RStudio has special handling for packages and you should now see a Build tab in the same pane as Environment and History.\nYou probably need to call library(devtools) again, because create_package() has probably dropped you into a fresh R session, in your new package.\n\nlibrary(devtools)\n\nWhat’s in this new directory that is also an R package and, probably, an RStudio Project? Here’s a listing (locally, you can consult your Files pane):\n\n\n\n\npath\ntype\n\n\n\n.Rbuildignore\nfile\n\n\n.gitignore\nfile\n\n\nDESCRIPTION\nfile\n\n\nNAMESPACE\nfile\n\n\nR\ndirectory\n\n\nregexcite.Rproj\nfile\n\n\n\n\n\n\n\n\n\n\n\nRStudio\n\n\n\nIn the Files pane, go to More (gear symbol) > Show Hidden Files to toggle the visibility of hidden files (a.k.a. “dotfiles”). A select few are visible all the time, but sometimes you want to see them all.\n\n\n\n\n.Rbuildignore lists files that we need to have around but that should not be included when building the R package from source. If you aren’t using RStudio, create_package() may not create this file (nor .gitignore) at first, since there’s no RStudio-related machinery that needs to be ignored. However, you will likely develop the need for .Rbuildignore at some point, regardless of what editor you are using. It is discussed in more detail in Section 3.3.1.\n\n.Rproj.user, if you have it, is a directory used internally by RStudio.\n\n.gitignore anticipates Git usage and tells Git to ignore some standard, behind-the-scenes files created by R and RStudio. Even if you do not plan to use Git, this is harmless.\n\nDESCRIPTION provides metadata about your package. We edit this shortly and Chapter 9 covers the general topic of the DESCRIPTION file.\n\nNAMESPACE declares the functions your package exports for external use and the external functions your package imports from other packages. At this point, it is empty, except for a comment declaring that this is a file you should not edit by hand.\nThe R/ directory is the “business end” of your package. It will soon contain .R files with function definitions.\n\nregexcite.Rproj is the file that makes this directory an RStudio Project. Even if you don’t use RStudio, this file is harmless. Or you can suppress its creation with create_package(..., rstudio = FALSE). More in Section 4.2.",
+ "text": "1.4 create_package()\n\nCall create_package() to initialize a new package in a directory on your computer. create_package() will automatically create that directory if it doesn’t exist yet (and that is usually the case). See Section 4.1 for more on creating packages.\nMake a deliberate choice about where to create this package on your computer. It should probably be somewhere within your home directory, alongside your other R projects. It should not be nested inside another RStudio Project, R package, or Git repo. Nor should it be in an R package library, which holds packages that have already been built and installed. The conversion of the source package we create here into an installed package is part of what devtools facilitates. Don’t try to do devtools’ job for it!\nOnce you’ve selected where to create this package, substitute your chosen path into a create_package() call like this:\n\ncreate_package(\"~/path/to/regexcite\")\n\nFor the creation of this book we have to work in a temporary directory, because the book is built non-interactively in the cloud. Behind the scenes, we’re executing our own create_package() command, but don’t be surprised if our output differs a bit from yours.\n\n#> ✔ Creating '/tmp/RtmpTXUOM4/regexcite/'.\n#> ✔ Setting active project to \"/tmp/RtmpTXUOM4/regexcite\".\n#> ✔ Creating 'R/'.\n#> ✔ Writing 'DESCRIPTION'.\n#> Package: regexcite\n#> Title: What the Package Does (One Line, Title Case)\n#> Version: 0.0.0.9000\n#> Authors@R (parsed):\n#> * First Last <first.last@example.com> [aut, cre]\n#> Description: What the package does (one paragraph).\n#> License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a\n#> license\n#> Encoding: UTF-8\n#> Roxygen: list(markdown = TRUE)\n#> RoxygenNote: 7.3.2\n#> ✔ Writing 'NAMESPACE'.\n#> ✔ Writing 'regexcite.Rproj'.\n#> ✔ Adding \"^regexcite\\\\.Rproj$\" to '.Rbuildignore'.\n#> ✔ Adding \".Rproj.user\" to '.gitignore'.\n#> ✔ Adding \"^\\\\.Rproj\\\\.user$\" to '.Rbuildignore'.\n#> ✔ Setting active project to \"<no active project>\".\n\nIf you’re working in RStudio, you should find yourself in a new instance of RStudio, opened into your new regexcite package (and Project). If you somehow need to do this manually, navigate to the directory and double click on regexcite.Rproj. RStudio has special handling for packages and you should now see a Build tab in the same pane as Environment and History.\nYou probably need to call library(devtools) again, because create_package() has probably dropped you into a fresh R session, in your new package.\n\nlibrary(devtools)\n\nWhat’s in this new directory that is also an R package and, probably, an RStudio Project? Here’s a listing (locally, you can consult your Files pane):\n\n\n\n\npath\ntype\n\n\n\n.Rbuildignore\nfile\n\n\n.gitignore\nfile\n\n\nDESCRIPTION\nfile\n\n\nNAMESPACE\nfile\n\n\nR\ndirectory\n\n\nregexcite.Rproj\nfile\n\n\n\n\n\n\n\n\n\n\n\nRStudio\n\n\n\nIn the Files pane, go to More (gear symbol) > Show Hidden Files to toggle the visibility of hidden files (a.k.a. “dotfiles”). A select few are visible all the time, but sometimes you want to see them all.\n\n\n\n\n.Rbuildignore lists files that we need to have around but that should not be included when building the R package from source. If you aren’t using RStudio, create_package() may not create this file (nor .gitignore) at first, since there’s no RStudio-related machinery that needs to be ignored. However, you will likely develop the need for .Rbuildignore at some point, regardless of what editor you are using. It is discussed in more detail in Section 3.3.1.\n\n.Rproj.user, if you have it, is a directory used internally by RStudio.\n\n.gitignore anticipates Git usage and tells Git to ignore some standard, behind-the-scenes files created by R and RStudio. Even if you do not plan to use Git, this is harmless.\n\nDESCRIPTION provides metadata about your package. We edit this shortly and Chapter 9 covers the general topic of the DESCRIPTION file.\n\nNAMESPACE declares the functions your package exports for external use and the external functions your package imports from other packages. At this point, it is empty, except for a comment declaring that this is a file you should not edit by hand.\nThe R/ directory is the “business end” of your package. It will soon contain .R files with function definitions.\n\nregexcite.Rproj is the file that makes this directory an RStudio Project. Even if you don’t use RStudio, this file is harmless. Or you can suppress its creation with create_package(..., rstudio = FALSE). More in Section 4.2.",
"crumbs": [
"Getting started",
"1The Whole Game"
@@ -148,7 +148,7 @@
"href": "whole-game.html#use_git",
"title": "1 The Whole Game",
"section": "\n1.5 use_git()\n",
- "text": "1.5 use_git()\n\nThe regexcite directory is an R source package and an RStudio Project. Now we make it also a Git repository, with use_git(). (By the way, use_git() works in any project, regardless of whether it’s an R package.)\n\nuse_git()\n#> ✔ Initialising Git repo.\n#> ✔ Adding \".Rhistory\", \".Rdata\", \".httr-oauth\", \".DS_Store\", and\n#> \".quarto\" to '.gitignore'.\n\nIn an interactive session, you will be asked if you want to commit some files here and you should accept the offer. Behind the scenes, we’ll also commit those same files.\nSo what has changed in the package? Only the creation of a .git directory, which is hidden in most contexts, including the RStudio file browser. Its existence is evidence that we have indeed initialized a Git repo here.\n\n\n\n\npath\ntype\n\n\n.git\ndirectory\n\n\n\n\nIf you’re using RStudio, it probably requested permission to relaunch itself in this Project, which you should do. You can do so manually by quitting, then relaunching RStudio by double clicking on regexcite.Rproj. Now, in addition to package development support, you have access to a basic Git client in the Git tab of the Environment/History/Build pane.\n\nClick on History (the clock icon in the Git pane) and, if you consented, you will see an initial commit made via use_git():\n\n\n\n\ncommit\nauthor\nmessage\n\n\nb1e27e4c10…\njennybc jennybc@users.noreply.github.com\n\nInitial commit\n\n\n\n\n\n\n\n\n\n\nRStudio\n\n\n\nRStudio can initialize a Git repository, in any Project, even if it’s not an R package, as long you’ve set up RStudio + Git integration. Do Tools > Version Control > Project Setup. Then choose Version control system: Git and initialize a new git repository for this project.",
+ "text": "1.5 use_git()\n\nThe regexcite directory is an R source package and an RStudio Project. Now we make it also a Git repository, with use_git(). (By the way, use_git() works in any project, regardless of whether it’s an R package.)\n\nuse_git()\n#> ✔ Initialising Git repo.\n#> ✔ Adding \".Rhistory\", \".Rdata\", \".httr-oauth\", \".DS_Store\", and\n#> \".quarto\" to '.gitignore'.\n\nIn an interactive session, you will be asked if you want to commit some files here and you should accept the offer. Behind the scenes, we’ll also commit those same files.\nSo what has changed in the package? Only the creation of a .git directory, which is hidden in most contexts, including the RStudio file browser. Its existence is evidence that we have indeed initialized a Git repo here.\n\n\n\n\npath\ntype\n\n\n.git\ndirectory\n\n\n\n\nIf you’re using RStudio, it probably requested permission to relaunch itself in this Project, which you should do. You can do so manually by quitting, then relaunching RStudio by double clicking on regexcite.Rproj. Now, in addition to package development support, you have access to a basic Git client in the Git tab of the Environment/History/Build pane.\n\nClick on History (the clock icon in the Git pane) and, if you consented, you will see an initial commit made via use_git():\n\n\n\n\ncommit\nauthor\nmessage\n\n\nd1b52dcb8f…\njennybc jennybc@users.noreply.github.com\n\nInitial commit\n\n\n\n\n\n\n\n\n\n\nRStudio\n\n\n\nRStudio can initialize a Git repository, in any Project, even if it’s not an R package, as long you’ve set up RStudio + Git integration. Do Tools > Version Control > Project Setup. Then choose Version control system: Git and initialize a new git repository for this project.",
"crumbs": [
"Getting started",
"1The Whole Game"
@@ -192,7 +192,7 @@
"href": "whole-game.html#check",
"title": "1 The Whole Game",
"section": "\n1.9 check()\n",
- "text": "1.9 check()\n\nWe have informal, empirical evidence that strsplit1() works. But how can we be sure that all the moving parts of the regexcite package still work? This may seem silly to check, after such a small addition, but it’s good to establish the habit of checking this often.\nR CMD check, executed in the shell, is the gold standard for checking that an R package is in full working order. check() is a convenient way to run this without leaving your R session.\nNote that check() produces rather voluminous output, optimized for interactive consumption. We intercept that here and just reveal a summary. Your local check() output will be different.\n\ncheck()\n\n\n── R CMD check results ─────────────────── regexcite 0.0.0.9000 ────\nDuration: 7.2s\n\n❯ checking DESCRIPTION meta-information ... WARNING\n Non-standard license specification:\n `use_mit_license()`, `use_gpl3_license()` or friends to pick a\n license\n Standardizable: FALSE\n\n0 errors ✔ | 1 warning ✖ | 0 notes ✔\n\nIt is essential to actually read the output of the check! Deal with problems early and often. It’s just like incremental development of .R and .Rmd files. The longer you go between full checks that everything works, the harder it becomes to pinpoint and solve your problems.\nAt this point, we expect 1 warning (and 0 errors, 0 notes):\nNon-standard license specification:\n `use_mit_license()`, `use_gpl3_license()` or friends to pick a\n license\nWe’ll address that soon, by doing exactly what it says. You can learn more about check() in Section 4.5.\n\n\n\n\n\n\nRStudio\n\n\n\nRStudio exposes check() in the Build menu, in the Build pane via Check, and in keyboard shortcuts Ctrl + Shift + E (Windows & Linux) or Cmd + Shift + E (macOS).",
+ "text": "1.9 check()\n\nWe have informal, empirical evidence that strsplit1() works. But how can we be sure that all the moving parts of the regexcite package still work? This may seem silly to check, after such a small addition, but it’s good to establish the habit of checking this often.\nR CMD check, executed in the shell, is the gold standard for checking that an R package is in full working order. check() is a convenient way to run this without leaving your R session.\nNote that check() produces rather voluminous output, optimized for interactive consumption. We intercept that here and just reveal a summary. Your local check() output will be different.\n\ncheck()\n\n\n── R CMD check results ─────────────────── regexcite 0.0.0.9000 ────\nDuration: 7s\n\n❯ checking DESCRIPTION meta-information ... WARNING\n Non-standard license specification:\n `use_mit_license()`, `use_gpl3_license()` or friends to pick a\n license\n Standardizable: FALSE\n\n0 errors ✔ | 1 warning ✖ | 0 notes ✔\n\nIt is essential to actually read the output of the check! Deal with problems early and often. It’s just like incremental development of .R and .Rmd files. The longer you go between full checks that everything works, the harder it becomes to pinpoint and solve your problems.\nAt this point, we expect 1 warning (and 0 errors, 0 notes):\nNon-standard license specification:\n `use_mit_license()`, `use_gpl3_license()` or friends to pick a\n license\nWe’ll address that soon, by doing exactly what it says. You can learn more about check() in Section 4.5.\n\n\n\n\n\n\nRStudio\n\n\n\nRStudio exposes check() in the Build menu, in the Build pane via Check, and in keyboard shortcuts Ctrl + Shift + E (Windows & Linux) or Cmd + Shift + E (macOS).",
"crumbs": [
"Getting started",
"1The Whole Game"
@@ -236,7 +236,7 @@
"href": "whole-game.html#check-again",
"title": "1 The Whole Game",
"section": "\n1.13 check() again",
- "text": "1.13 check() again\nregexcite should pass R CMD check cleanly now and forever more: 0 errors, 0 warnings, 0 notes.\n\ncheck()\n\n\n── R CMD check results ─────────────────── regexcite 0.0.0.9000 ────\nDuration: 9s\n\n0 errors ✔ | 0 warnings ✔ | 0 notes ✔",
+ "text": "1.13 check() again\nregexcite should pass R CMD check cleanly now and forever more: 0 errors, 0 warnings, 0 notes.\n\ncheck()\n\n\n── R CMD check results ─────────────────── regexcite 0.0.0.9000 ────\nDuration: 8.7s\n\n0 errors ✔ | 0 warnings ✔ | 0 notes ✔",
"crumbs": [
"Getting started",
"1The Whole Game"
@@ -247,7 +247,7 @@
"href": "whole-game.html#install",
"title": "1 The Whole Game",
"section": "\n1.14 install()\n",
- "text": "1.14 install()\n\nNow that we know we have a minimum viable product, let’s install the regexcite package into your library via install():\n\ninstall()\n\n\n── R CMD build ─────────────────────────────────────────────────────\n* checking for file ‘/tmp/Rtmp9zEBZC/regexcite/DESCRIPTION’ ... OK\n* preparing ‘regexcite’:\n* checking DESCRIPTION meta-information ... OK\n* checking for LF line-endings in source and make files and shell scripts\n* checking for empty or unneeded directories\n* building ‘regexcite_0.0.0.9000.tar.gz’\nRunning /opt/R/4.4.2/lib/R/bin/R CMD INSTALL \\\n /tmp/Rtmp9zEBZC/regexcite_0.0.0.9000.tar.gz --install-tests \n* installing to library ‘/home/runner/work/_temp/Library’\n* installing *source* package ‘regexcite’ ...\n** using staged installation\n** R\n** byte-compile and prepare package for lazy loading\n** help\n*** installing help indices\n** building package indices\n** testing if installed package can be loaded from temporary location\n** testing if installed package can be loaded from final location\n** testing if installed package keeps a record of temporary installation path\n* DONE (regexcite)\n\n\n\n\n\n\n\nRStudio\n\n\n\nRStudio exposes similar functionality in the Build menu and in the Build pane via Install and Restart, and in keyboard shortcuts Ctrl + Shift + B (Windows & Linux) or Cmd + Shift + B (macOS).\n\n\nAfter installation is complete, we can attach and use regexcite like any other package. Let’s revisit our small example from the top. This is also a good time to restart your R session and ensure you have a clean workspace.\n\nlibrary(regexcite)\n\nx <- \"alfa,bravo,charlie,delta\"\nstrsplit1(x, split = \",\")\n#> [1] \"alfa\" \"bravo\" \"charlie\" \"delta\"\n\nSuccess!",
+ "text": "1.14 install()\n\nNow that we know we have a minimum viable product, let’s install the regexcite package into your library via install():\n\ninstall()\n\n\n── R CMD build ─────────────────────────────────────────────────────\n* checking for file ‘/tmp/RtmpTXUOM4/regexcite/DESCRIPTION’ ... OK\n* preparing ‘regexcite’:\n* checking DESCRIPTION meta-information ... OK\n* checking for LF line-endings in source and make files and shell scripts\n* checking for empty or unneeded directories\n* building ‘regexcite_0.0.0.9000.tar.gz’\nRunning /opt/R/4.4.2/lib/R/bin/R CMD INSTALL \\\n /tmp/RtmpTXUOM4/regexcite_0.0.0.9000.tar.gz --install-tests \n* installing to library ‘/home/runner/work/_temp/Library’\n* installing *source* package ‘regexcite’ ...\n** using staged installation\n** R\n** byte-compile and prepare package for lazy loading\n** help\n*** installing help indices\n** building package indices\n** testing if installed package can be loaded from temporary location\n** testing if installed package can be loaded from final location\n** testing if installed package keeps a record of temporary installation path\n* DONE (regexcite)\n\n\n\n\n\n\n\nRStudio\n\n\n\nRStudio exposes similar functionality in the Build menu and in the Build pane via Install and Restart, and in keyboard shortcuts Ctrl + Shift + B (Windows & Linux) or Cmd + Shift + B (macOS).\n\n\nAfter installation is complete, we can attach and use regexcite like any other package. Let’s revisit our small example from the top. This is also a good time to restart your R session and ensure you have a clean workspace.\n\nlibrary(regexcite)\n\nx <- \"alfa,bravo,charlie,delta\"\nstrsplit1(x, split = \",\")\n#> [1] \"alfa\" \"bravo\" \"charlie\" \"delta\"\n\nSuccess!",
"crumbs": [
"Getting started",
"1The Whole Game"
@@ -291,7 +291,7 @@
"href": "whole-game.html#use_readme_rmd",
"title": "1 The Whole Game",
"section": "\n1.18 use_readme_rmd()\n",
- "text": "1.18 use_readme_rmd()\n\nNow that your package is on GitHub, the README.md file matters. It is the package’s home page and welcome mat, at least until you decide to give it a website (see Chapter 19), add a vignette (see Chapter 17), or submit it to CRAN (see Chapter 22).\nThe use_readme_rmd() function initializes a basic, executable README.Rmd ready for you to edit:\n\nuse_readme_rmd()\n#> ✔ Writing 'README.Rmd'.\n#> ✔ Adding \"^README\\\\.Rmd$\" to '.Rbuildignore'.\n#> ☐ Update 'README.Rmd' to include installation instructions.\n#> ✔ Writing '.git/hooks/pre-commit'.\n\nIn addition to creating README.Rmd, this adds some lines to .Rbuildignore, and creates a Git pre-commit hook to help you keep README.Rmd and README.md in sync.\nREADME.Rmd already has sections that prompt you to:\n\nDescribe the purpose of the package.\nProvide installation instructions. If a GitHub remote is detected when use_readme_rmd() is called, this section is pre-filled with instructions on how to install from GitHub.\nShow a bit of usage.\n\nHow to populate this skeleton? Copy stuff liberally from DESCRIPTION and any formal and informal tests or examples you have. Anything is better than nothing. This is helpful because people probably won’t install your package and comb through individual help files to figure out how to use it.\nWe like to write the README in R Markdown, so it can feature actual usage. The inclusion of live code also makes it less likely that your README grows stale and out-of-sync with your actual package.\nTo make your own edits, if RStudio has not already done so, open README.Rmd for editing. Make sure it shows some usage of str_split_one().\nThe README.Rmd we use is here: README.Rmd and here’s what it contains:\n\n---\noutput: github_document\n---\n\n<!-- README.md is generated from README.Rmd. Please edit that file -->\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n collapse = TRUE,\n comment = \"#>\",\n fig.path = \"man/figures/README-\",\n out.width = \"100%\"\n)\n```\n\n**NOTE: This is a toy package created for expository purposes, for the second edition of [R Packages](https://r-pkgs.org). It is not meant to actually be useful. If you want a package for factor handling, please see [stringr](https://stringr.tidyverse.org), [stringi](https://stringi.gagolewski.com/),\n[rex](https://cran.r-project.org/package=rex), and\n[rematch2](https://cran.r-project.org/package=rematch2).**\n\n# regexcite\n\n<!-- badges: start -->\n<!-- badges: end -->\n\nThe goal of regexcite is to make regular expressions more exciting!\nIt provides convenience functions to make some common tasks with string manipulation and regular expressions a bit easier.\n\n## Installation\n\nYou can install the development version of regexcite from [GitHub](https://github.com/) with:\n \n``` r\n# install.packages(\"devtools\")\ndevtools::install_github(\"jennybc/regexcite\")\n```\n\n## Usage\n\nA fairly common task when dealing with strings is the need to split a single string into many parts.\nThis is what `base::strplit()` and `stringr::str_split()` do.\n\n```{r}\n(x <- \"alfa,bravo,charlie,delta\")\nstrsplit(x, split = \",\")\nstringr::str_split(x, pattern = \",\")\n```\n\nNotice how the return value is a **list** of length one, where the first element holds the character vector of parts.\nOften the shape of this output is inconvenient, i.e. we want the un-listed version.\n\nThat's exactly what `regexcite::str_split_one()` does.\n\n```{r}\nlibrary(regexcite)\n\nstr_split_one(x, pattern = \",\")\n```\n\nUse `str_split_one()` when the input is known to be a single string.\nFor safety, it will error if its input has length greater than one.\n\n`str_split_one()` is built on `stringr::str_split()`, so you can use its `n` argument and stringr's general interface for describing the `pattern` to be matched.\n\n```{r}\nstr_split_one(x, pattern = \",\", n = 2)\n\ny <- \"192.168.0.1\"\nstr_split_one(y, pattern = stringr::fixed(\".\"))\n```\n\nDon’t forget to render it to make README.md! The pre-commit hook should remind you if you try to commit README.Rmd, but not README.md, and also when README.md appears to be out-of-date.\nThe very best way to render README.Rmd is with build_readme(), because it takes care to render with the most current version of your package, i.e. it installs a temporary copy from the current source.\n\nbuild_readme()\n#> ℹ Installing regexcite in temporary library\n#> ℹ Building '/tmp/Rtmp9zEBZC/regexcite/README.Rmd'\n\nYou can see the rendered README.md simply by visiting regexcite on GitHub.\nFinally, don’t forget to do one last commit. And push, if you’re using GitHub.",
+ "text": "1.18 use_readme_rmd()\n\nNow that your package is on GitHub, the README.md file matters. It is the package’s home page and welcome mat, at least until you decide to give it a website (see Chapter 19), add a vignette (see Chapter 17), or submit it to CRAN (see Chapter 22).\nThe use_readme_rmd() function initializes a basic, executable README.Rmd ready for you to edit:\n\nuse_readme_rmd()\n#> ✔ Writing 'README.Rmd'.\n#> ✔ Adding \"^README\\\\.Rmd$\" to '.Rbuildignore'.\n#> ☐ Update 'README.Rmd' to include installation instructions.\n#> ✔ Writing '.git/hooks/pre-commit'.\n\nIn addition to creating README.Rmd, this adds some lines to .Rbuildignore, and creates a Git pre-commit hook to help you keep README.Rmd and README.md in sync.\nREADME.Rmd already has sections that prompt you to:\n\nDescribe the purpose of the package.\nProvide installation instructions. If a GitHub remote is detected when use_readme_rmd() is called, this section is pre-filled with instructions on how to install from GitHub.\nShow a bit of usage.\n\nHow to populate this skeleton? Copy stuff liberally from DESCRIPTION and any formal and informal tests or examples you have. Anything is better than nothing. This is helpful because people probably won’t install your package and comb through individual help files to figure out how to use it.\nWe like to write the README in R Markdown, so it can feature actual usage. The inclusion of live code also makes it less likely that your README grows stale and out-of-sync with your actual package.\nTo make your own edits, if RStudio has not already done so, open README.Rmd for editing. Make sure it shows some usage of str_split_one().\nThe README.Rmd we use is here: README.Rmd and here’s what it contains:\n\n---\noutput: github_document\n---\n\n<!-- README.md is generated from README.Rmd. Please edit that file -->\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n collapse = TRUE,\n comment = \"#>\",\n fig.path = \"man/figures/README-\",\n out.width = \"100%\"\n)\n```\n\n**NOTE: This is a toy package created for expository purposes, for the second edition of [R Packages](https://r-pkgs.org). It is not meant to actually be useful. If you want a package for factor handling, please see [stringr](https://stringr.tidyverse.org), [stringi](https://stringi.gagolewski.com/),\n[rex](https://cran.r-project.org/package=rex), and\n[rematch2](https://cran.r-project.org/package=rematch2).**\n\n# regexcite\n\n<!-- badges: start -->\n<!-- badges: end -->\n\nThe goal of regexcite is to make regular expressions more exciting!\nIt provides convenience functions to make some common tasks with string manipulation and regular expressions a bit easier.\n\n## Installation\n\nYou can install the development version of regexcite from [GitHub](https://github.com/) with:\n \n``` r\n# install.packages(\"devtools\")\ndevtools::install_github(\"jennybc/regexcite\")\n```\n\n## Usage\n\nA fairly common task when dealing with strings is the need to split a single string into many parts.\nThis is what `base::strplit()` and `stringr::str_split()` do.\n\n```{r}\n(x <- \"alfa,bravo,charlie,delta\")\nstrsplit(x, split = \",\")\nstringr::str_split(x, pattern = \",\")\n```\n\nNotice how the return value is a **list** of length one, where the first element holds the character vector of parts.\nOften the shape of this output is inconvenient, i.e. we want the un-listed version.\n\nThat's exactly what `regexcite::str_split_one()` does.\n\n```{r}\nlibrary(regexcite)\n\nstr_split_one(x, pattern = \",\")\n```\n\nUse `str_split_one()` when the input is known to be a single string.\nFor safety, it will error if its input has length greater than one.\n\n`str_split_one()` is built on `stringr::str_split()`, so you can use its `n` argument and stringr's general interface for describing the `pattern` to be matched.\n\n```{r}\nstr_split_one(x, pattern = \",\", n = 2)\n\ny <- \"192.168.0.1\"\nstr_split_one(y, pattern = stringr::fixed(\".\"))\n```\n\nDon’t forget to render it to make README.md! The pre-commit hook should remind you if you try to commit README.Rmd, but not README.md, and also when README.md appears to be out-of-date.\nThe very best way to render README.Rmd is with build_readme(), because it takes care to render with the most current version of your package, i.e. it installs a temporary copy from the current source.\n\nbuild_readme()\n#> ℹ Installing regexcite in temporary library\n#> ℹ Building '/tmp/RtmpTXUOM4/regexcite/README.Rmd'\n\nYou can see the rendered README.md simply by visiting regexcite on GitHub.\nFinally, don’t forget to do one last commit. And push, if you’re using GitHub.",
"crumbs": [
"Getting started",
"1The Whole Game"
@@ -302,7 +302,7 @@
"href": "whole-game.html#the-end-check-and-install",
"title": "1 The Whole Game",
"section": "\n1.19 The end: check() and install()\n",
- "text": "1.19 The end: check() and install()\n\nLet’s run check() again to make sure all is still well.\n\ncheck()\n\n\n── R CMD check results ─────────────────── regexcite 0.0.0.9000 ────\nDuration: 9.8s\n\n0 errors ✔ | 0 warnings ✔ | 0 notes ✔\n\nregexcite should have no errors, warnings or notes. This would be a good time to re-build and install it properly. And celebrate!\n\ninstall()\n\n\n── R CMD build ─────────────────────────────────────────────────────\n* checking for file ‘/tmp/Rtmp9zEBZC/regexcite/DESCRIPTION’ ... OK\n* preparing ‘regexcite’:\n* checking DESCRIPTION meta-information ... OK\n* checking for LF line-endings in source and make files and shell scripts\n* checking for empty or unneeded directories\nRemoved empty directory ‘regexcite/tests/testthat/_snaps’\n* building ‘regexcite_0.0.0.9000.tar.gz’\nRunning /opt/R/4.4.2/lib/R/bin/R CMD INSTALL \\\n /tmp/Rtmp9zEBZC/regexcite_0.0.0.9000.tar.gz --install-tests \n* installing to library ‘/home/runner/work/_temp/Library’\n* installing *source* package ‘regexcite’ ...\n** using staged installation\n** R\n** tests\n** byte-compile and prepare package for lazy loading\n** help\n*** installing help indices\n** building package indices\n** testing if installed package can be loaded from temporary location\n** testing if installed package can be loaded from final location\n** testing if installed package keeps a record of temporary installation path\n* DONE (regexcite)\n\nFeel free to visit the regexcite package on GitHub, which appears exactly as developed here. The commit history reflects each individual step, so use the diffs to see the addition and modification of files, as the package evolved. The rest of this book goes in greater detail for each step you’ve seen here and much more.",
+ "text": "1.19 The end: check() and install()\n\nLet’s run check() again to make sure all is still well.\n\ncheck()\n\n\n── R CMD check results ─────────────────── regexcite 0.0.0.9000 ────\nDuration: 9.9s\n\n0 errors ✔ | 0 warnings ✔ | 0 notes ✔\n\nregexcite should have no errors, warnings or notes. This would be a good time to re-build and install it properly. And celebrate!\n\ninstall()\n\n\n── R CMD build ─────────────────────────────────────────────────────\n* checking for file ‘/tmp/RtmpTXUOM4/regexcite/DESCRIPTION’ ... OK\n* preparing ‘regexcite’:\n* checking DESCRIPTION meta-information ... OK\n* checking for LF line-endings in source and make files and shell scripts\n* checking for empty or unneeded directories\nRemoved empty directory ‘regexcite/tests/testthat/_snaps’\n* building ‘regexcite_0.0.0.9000.tar.gz’\nRunning /opt/R/4.4.2/lib/R/bin/R CMD INSTALL \\\n /tmp/RtmpTXUOM4/regexcite_0.0.0.9000.tar.gz --install-tests \n* installing to library ‘/home/runner/work/_temp/Library’\n* installing *source* package ‘regexcite’ ...\n** using staged installation\n** R\n** tests\n** byte-compile and prepare package for lazy loading\n** help\n*** installing help indices\n** building package indices\n** testing if installed package can be loaded from temporary location\n** testing if installed package can be loaded from final location\n** testing if installed package keeps a record of temporary installation path\n* DONE (regexcite)\n\nFeel free to visit the regexcite package on GitHub, which appears exactly as developed here. The commit history reflects each individual step, so use the diffs to see the addition and modification of files, as the package evolved. The rest of this book goes in greater detail for each step you’ve seen here and much more.",
"crumbs": [
"Getting started",
"1The Whole Game"
@@ -566,7 +566,7 @@
"href": "package-within.html",
"title": "5 The package within",
"section": "",
- "text": "5.1 Alfa: a script that works\nThis part of the book ends the same way it started, with the development of a small toy package. Chapter 1 established the basic mechanics, workflow, and tooling of package development, but said practically nothing about the R code inside the package. This chapter focuses primarily on the package’s R code and how it differs from R code in a script.\nStarting with a data analysis script, you learn how to find the package that lurks within. You’ll isolate and then extract reusable data and logic from the script, put this into an R package, and then use that package in a much simplified script. We’ve included a few rookie mistakes along the way, in order to highlight special considerations for the R code inside a package.\nNote that the section headers incorporate the NATO phonetic alphabet (alfa, bravo, etc.) and have no specific meaning. They are just a convenient way to mark our progress towards a working package. It is fine to follow along just by reading and this chapter is completely self-contained, i.e. it’s not a prerequisite for material later in the book. But if you wish to see the state of specific files along the way, they can be found in the source files for the book.\nLet’s consider data-cleaning.R, a fictional data analysis script for a group that collects reports from people who went for a swim:\nTheir data usually comes as a CSV file, such as swim.csv:\nname,where,temp\nAdam,beach,95\nBess,coast,91\nCora,seashore,28\nDale,beach,85\nEvan,seaside,31\ndata-cleaning.R begins by reading swim.csv into a data frame:\ninfile <- \"swim.csv\"\n(dat <- read.csv(infile))\n#> name where temp\n#> 1 Adam beach 95\n#> 2 Bess coast 91\n#> 3 Cora seashore 28\n#> 4 Dale beach 85\n#> 5 Evan seaside 31\nThey then classify each observation as using American (“US”) or British (“UK”) English, based on the word chosen to describe the sandy place where the ocean and land meet. The where column is used to build the new english column.\ndat$english[dat$where == \"beach\"] <- \"US\"\ndat$english[dat$where == \"coast\"] <- \"US\"\ndat$english[dat$where == \"seashore\"] <- \"UK\"\ndat$english[dat$where == \"seaside\"] <- \"UK\"\nSadly, the temperatures are often reported in a mix of Fahrenheit and Celsius. In the absence of better information, they guess that Americans report temperatures in Fahrenheit and therefore those observations are converted to Celsius.\ndat$temp[dat$english == \"US\"] <- (dat$temp[dat$english == \"US\"] - 32) * 5/9\ndat\n#> name where temp english\n#> 1 Adam beach 35.0 US\n#> 2 Bess coast 32.8 US\n#> 3 Cora seashore 28.0 UK\n#> 4 Dale beach 29.4 US\n#> 5 Evan seaside 31.0 UK\nFinally, this cleaned (cleaner?) data is written back out to a CSV file. They like to capture a timestamp in the filename when they do this1.\nnow <- Sys.time()\ntimestamp <- format(now, \"%Y-%B-%d_%H-%M-%S\")\n(outfile <- paste0(timestamp, \"_\", sub(\"(.*)([.]csv$)\", \"\\\\1_clean\\\\2\", infile)))\n#> [1] \"2025-January-08_07-15-03_swim_clean.csv\"\nwrite.csv(dat, file = outfile, quote = FALSE, row.names = FALSE)\nHere is data-cleaning.R in its entirety:\ninfile <- \"swim.csv\"\n(dat <- read.csv(infile))\n\ndat$english[dat$where == \"beach\"] <- \"US\"\ndat$english[dat$where == \"coast\"] <- \"US\"\ndat$english[dat$where == \"seashore\"] <- \"UK\"\ndat$english[dat$where == \"seaside\"] <- \"UK\"\n\ndat$temp[dat$english == \"US\"] <- (dat$temp[dat$english == \"US\"] - 32) * 5/9\ndat\n\nnow <- Sys.time()\ntimestamp <- format(now, \"%Y-%B-%d_%H-%M-%S\")\n(outfile <- paste0(timestamp, \"_\", sub(\"(.*)([.]csv$)\", \"\\\\1_clean\\\\2\", infile)))\nwrite.csv(dat, file = outfile, quote = FALSE, row.names = FALSE)\nEven if your typical analytical tasks are quite different, hopefully you see a few familiar patterns here. It’s easy to imagine that this group does very similar pre-processing of many similar data files over time. Their analyses can be more efficient and consistent if they make these standard data maneuvers available to themselves as functions in a package, instead of inlining the same data and logic into dozens or hundreds of data ingest scripts.",
+ "text": "5.1 Alfa: a script that works\nThis part of the book ends the same way it started, with the development of a small toy package. Chapter 1 established the basic mechanics, workflow, and tooling of package development, but said practically nothing about the R code inside the package. This chapter focuses primarily on the package’s R code and how it differs from R code in a script.\nStarting with a data analysis script, you learn how to find the package that lurks within. You’ll isolate and then extract reusable data and logic from the script, put this into an R package, and then use that package in a much simplified script. We’ve included a few rookie mistakes along the way, in order to highlight special considerations for the R code inside a package.\nNote that the section headers incorporate the NATO phonetic alphabet (alfa, bravo, etc.) and have no specific meaning. They are just a convenient way to mark our progress towards a working package. It is fine to follow along just by reading and this chapter is completely self-contained, i.e. it’s not a prerequisite for material later in the book. But if you wish to see the state of specific files along the way, they can be found in the source files for the book.\nLet’s consider data-cleaning.R, a fictional data analysis script for a group that collects reports from people who went for a swim:\nTheir data usually comes as a CSV file, such as swim.csv:\nname,where,temp\nAdam,beach,95\nBess,coast,91\nCora,seashore,28\nDale,beach,85\nEvan,seaside,31\ndata-cleaning.R begins by reading swim.csv into a data frame:\ninfile <- \"swim.csv\"\n(dat <- read.csv(infile))\n#> name where temp\n#> 1 Adam beach 95\n#> 2 Bess coast 91\n#> 3 Cora seashore 28\n#> 4 Dale beach 85\n#> 5 Evan seaside 31\nThey then classify each observation as using American (“US”) or British (“UK”) English, based on the word chosen to describe the sandy place where the ocean and land meet. The where column is used to build the new english column.\ndat$english[dat$where == \"beach\"] <- \"US\"\ndat$english[dat$where == \"coast\"] <- \"US\"\ndat$english[dat$where == \"seashore\"] <- \"UK\"\ndat$english[dat$where == \"seaside\"] <- \"UK\"\nSadly, the temperatures are often reported in a mix of Fahrenheit and Celsius. In the absence of better information, they guess that Americans report temperatures in Fahrenheit and therefore those observations are converted to Celsius.\ndat$temp[dat$english == \"US\"] <- (dat$temp[dat$english == \"US\"] - 32) * 5/9\ndat\n#> name where temp english\n#> 1 Adam beach 35.0 US\n#> 2 Bess coast 32.8 US\n#> 3 Cora seashore 28.0 UK\n#> 4 Dale beach 29.4 US\n#> 5 Evan seaside 31.0 UK\nFinally, this cleaned (cleaner?) data is written back out to a CSV file. They like to capture a timestamp in the filename when they do this1.\nnow <- Sys.time()\ntimestamp <- format(now, \"%Y-%B-%d_%H-%M-%S\")\n(outfile <- paste0(timestamp, \"_\", sub(\"(.*)([.]csv$)\", \"\\\\1_clean\\\\2\", infile)))\n#> [1] \"2025-January-09_07-15-28_swim_clean.csv\"\nwrite.csv(dat, file = outfile, quote = FALSE, row.names = FALSE)\nHere is data-cleaning.R in its entirety:\ninfile <- \"swim.csv\"\n(dat <- read.csv(infile))\n\ndat$english[dat$where == \"beach\"] <- \"US\"\ndat$english[dat$where == \"coast\"] <- \"US\"\ndat$english[dat$where == \"seashore\"] <- \"UK\"\ndat$english[dat$where == \"seaside\"] <- \"UK\"\n\ndat$temp[dat$english == \"US\"] <- (dat$temp[dat$english == \"US\"] - 32) * 5/9\ndat\n\nnow <- Sys.time()\ntimestamp <- format(now, \"%Y-%B-%d_%H-%M-%S\")\n(outfile <- paste0(timestamp, \"_\", sub(\"(.*)([.]csv$)\", \"\\\\1_clean\\\\2\", infile)))\nwrite.csv(dat, file = outfile, quote = FALSE, row.names = FALSE)\nEven if your typical analytical tasks are quite different, hopefully you see a few familiar patterns here. It’s easy to imagine that this group does very similar pre-processing of many similar data files over time. Their analyses can be more efficient and consistent if they make these standard data maneuvers available to themselves as functions in a package, instead of inlining the same data and logic into dozens or hundreds of data ingest scripts.",
"crumbs": [
"Getting started",
"5The package within"
@@ -643,7 +643,7 @@
"href": "package-within.html#sec-package-within-side-effects",
"title": "5 The package within",
"section": "\n5.7 Golf: side effects",
- "text": "5.7 Golf: side effects\nThe timestamps now reflect the current time, but the group raises a new concern. As it stands, the timestamps reflect who has done the data cleaning and which part of the world they’re in. The heart of the timestamp strategy is this format string5:\n\nformat(Sys.time(), \"%Y-%B-%d_%H-%M-%S\")\n#> [1] \"2025-January-08_07-15-03\"\n\nThis formats Sys.time() in such a way that it includes the month name (not number) and the local time6.\nTable 5.1 shows what happens when such a timestamp is produced by several hypothetical colleagues cleaning some data at exactly the same instant in time.\n\n\n\nTable 5.1: Timestamp varies by locale and timezone.\n\n\n\n\n\n\n\n\n\n\nlocation\ntimestamp\nLC_TIME\ntz\n\n\n\nRome, Italy\n2020-settembre-05_00-30-00\nit_IT.UTF-8\nEurope/Rome\n\n\nWarsaw, Poland\n2020-września-05_00-30-00\npl_PL.UTF-8\nEurope/Warsaw\n\n\nSao Paulo, Brazil\n2020-setembro-04_19-30-00\npt_BR.UTF-8\nAmerica/Sao_Paulo\n\n\nGreenwich, England\n2020-September-04_23-30-00\nen_GB.UTF-8\nEurope/London\n\n\n“Computer World!”\n2020-September-04_22-30-00\nC\nUTC\n\n\n\n\n\n\n\n\nNote that the month names vary, as does the time, and even the date! The safest choice is to form timestamps with respect to a fixed locale and time zone (presumably the non-geographic choices represented by “Computer World!” above).\nYou do some research and learn that you can force a certain locale via Sys.setlocale() and force a certain time zone by setting the TZ environment variable. Specifically, we set the LC_TIME component of the locale to “C” and the time zone to “UTC” (Coordinated Universal Time). Here’s your first attempt to improve timestamp():\n\ntimestamp <- function(time = Sys.time()) {\n Sys.setlocale(\"LC_TIME\", \"C\")\n Sys.setenv(TZ = \"UTC\")\n format(time, \"%Y-%B-%d_%H-%M-%S\")\n}\n\nBut your Brazilian colleague notices that datetimes print differently, before and after she uses outfile_path() from your package:\nBefore:\n\nformat(Sys.time(), \"%Y-%B-%d_%H-%M-%S\")\n\n\n#> [1] \"2025-janeiro-08_04-15-04\"\n\nAfter:\n\noutfile_path(\"INFILE.csv\")\n#> [1] \"2025-January-08_07-15-03_INFILE_clean.csv\"\n\nformat(Sys.time(), \"%Y-%B-%d_%H-%M-%S\")\n#> [1] \"2025-January-08_07-15-04\"\n\nNotice that her month name switched from Portuguese to English and the time is clearly being reported in a different time zone. The calls to Sys.setlocale() and Sys.setenv() inside timestamp() have made persistent (and very surprising) changes to her R session. This sort of side effect is very undesirable and is extremely difficult to track down and debug, especially in more complicated settings.\nHere are better versions of timestamp():\n\n# use withr::local_*() functions to keep the changes local to timestamp()\ntimestamp <- function(time = Sys.time()) {\n withr::local_locale(c(\"LC_TIME\" = \"C\"))\n withr::local_timezone(\"UTC\")\n format(time, \"%Y-%B-%d_%H-%M-%S\")\n}\n\n# use the tz argument to format.POSIXct()\ntimestamp <- function(time = Sys.time()) {\n withr::local_locale(c(\"LC_TIME\" = \"C\"))\n format(time, \"%Y-%B-%d_%H-%M-%S\", tz = \"UTC\")\n}\n\n# put the format() call inside withr::with_*()\ntimestamp <- function(time = Sys.time()) {\n withr::with_locale(\n c(\"LC_TIME\" = \"C\"),\n format(time, \"%Y-%B-%d_%H-%M-%S\", tz = \"UTC\")\n )\n}\n\nThese show various methods to limit the scope of our changes to LC_TIME and the timezone. A good rule of thumb is to make the scope of such changes as narrow as possible and practical. The tz argument of format() is the most surgical way to deal with the timezone, but nothing similar exists for LC_TIME. We make the temporary locale modification using the withr package, which provides a very flexible toolkit for temporary state changes. This (and base::on.exit()) are discussed further in Section 6.5. Note that if you use withr as we do above, you would need to list it in DESCRIPTION in Imports (Chapter 11, Section 10.1.3).\nThis underscores a point from the previous section: you need to adopt a different mindset when defining functions inside a package. Try to avoid making any changes to the user’s overall state. If such changes are unavoidable, make sure to reverse them (if possible) or to document them explicitly (if related to the function’s primary purpose).",
+ "text": "5.7 Golf: side effects\nThe timestamps now reflect the current time, but the group raises a new concern. As it stands, the timestamps reflect who has done the data cleaning and which part of the world they’re in. The heart of the timestamp strategy is this format string5:\n\nformat(Sys.time(), \"%Y-%B-%d_%H-%M-%S\")\n#> [1] \"2025-January-09_07-15-29\"\n\nThis formats Sys.time() in such a way that it includes the month name (not number) and the local time6.\nTable 5.1 shows what happens when such a timestamp is produced by several hypothetical colleagues cleaning some data at exactly the same instant in time.\n\n\n\nTable 5.1: Timestamp varies by locale and timezone.\n\n\n\n\n\n\n\n\n\n\nlocation\ntimestamp\nLC_TIME\ntz\n\n\n\nRome, Italy\n2020-settembre-05_00-30-00\nit_IT.UTF-8\nEurope/Rome\n\n\nWarsaw, Poland\n2020-września-05_00-30-00\npl_PL.UTF-8\nEurope/Warsaw\n\n\nSao Paulo, Brazil\n2020-setembro-04_19-30-00\npt_BR.UTF-8\nAmerica/Sao_Paulo\n\n\nGreenwich, England\n2020-September-04_23-30-00\nen_GB.UTF-8\nEurope/London\n\n\n“Computer World!”\n2020-September-04_22-30-00\nC\nUTC\n\n\n\n\n\n\n\n\nNote that the month names vary, as does the time, and even the date! The safest choice is to form timestamps with respect to a fixed locale and time zone (presumably the non-geographic choices represented by “Computer World!” above).\nYou do some research and learn that you can force a certain locale via Sys.setlocale() and force a certain time zone by setting the TZ environment variable. Specifically, we set the LC_TIME component of the locale to “C” and the time zone to “UTC” (Coordinated Universal Time). Here’s your first attempt to improve timestamp():\n\ntimestamp <- function(time = Sys.time()) {\n Sys.setlocale(\"LC_TIME\", \"C\")\n Sys.setenv(TZ = \"UTC\")\n format(time, \"%Y-%B-%d_%H-%M-%S\")\n}\n\nBut your Brazilian colleague notices that datetimes print differently, before and after she uses outfile_path() from your package:\nBefore:\n\nformat(Sys.time(), \"%Y-%B-%d_%H-%M-%S\")\n\n\n#> [1] \"2025-janeiro-09_04-15-29\"\n\nAfter:\n\noutfile_path(\"INFILE.csv\")\n#> [1] \"2025-January-09_07-15-29_INFILE_clean.csv\"\n\nformat(Sys.time(), \"%Y-%B-%d_%H-%M-%S\")\n#> [1] \"2025-January-09_07-15-29\"\n\nNotice that her month name switched from Portuguese to English and the time is clearly being reported in a different time zone. The calls to Sys.setlocale() and Sys.setenv() inside timestamp() have made persistent (and very surprising) changes to her R session. This sort of side effect is very undesirable and is extremely difficult to track down and debug, especially in more complicated settings.\nHere are better versions of timestamp():\n\n# use withr::local_*() functions to keep the changes local to timestamp()\ntimestamp <- function(time = Sys.time()) {\n withr::local_locale(c(\"LC_TIME\" = \"C\"))\n withr::local_timezone(\"UTC\")\n format(time, \"%Y-%B-%d_%H-%M-%S\")\n}\n\n# use the tz argument to format.POSIXct()\ntimestamp <- function(time = Sys.time()) {\n withr::local_locale(c(\"LC_TIME\" = \"C\"))\n format(time, \"%Y-%B-%d_%H-%M-%S\", tz = \"UTC\")\n}\n\n# put the format() call inside withr::with_*()\ntimestamp <- function(time = Sys.time()) {\n withr::with_locale(\n c(\"LC_TIME\" = \"C\"),\n format(time, \"%Y-%B-%d_%H-%M-%S\", tz = \"UTC\")\n )\n}\n\nThese show various methods to limit the scope of our changes to LC_TIME and the timezone. A good rule of thumb is to make the scope of such changes as narrow as possible and practical. The tz argument of format() is the most surgical way to deal with the timezone, but nothing similar exists for LC_TIME. We make the temporary locale modification using the withr package, which provides a very flexible toolkit for temporary state changes. This (and base::on.exit()) are discussed further in Section 6.5. Note that if you use withr as we do above, you would need to list it in DESCRIPTION in Imports (Chapter 11, Section 10.1.3).\nThis underscores a point from the previous section: you need to adopt a different mindset when defining functions inside a package. Try to avoid making any changes to the user’s overall state. If such changes are unavoidable, make sure to reverse them (if possible) or to document them explicitly (if related to the function’s primary purpose).",
"crumbs": [
"Getting started",
"5The package within"
@@ -764,7 +764,7 @@
"href": "data.html",
"title": "7 Data",
"section": "",
- "text": "7.1 Exported data\nIt’s often useful to include data in a package. If the primary purpose of a package is to distribute useful functions, example datasets make it easier to write excellent documentation. These datasets can be hand-crafted to provide compelling use cases for the functions in the package. Here are some examples of this type of package data:\nAt the other extreme, some packages exist solely for the purpose of distributing data, along with its documentation. These are sometimes called “data packages”. A data package can be a nice way to share example data across multiple packages. It is also a useful technique for getting relatively large, static files out of a more function-oriented package, which might require more frequent updates. Here are some examples of data packages:\nFinally, many packages benefit from having internal data that is used for internal purposes, but that is not directly exposed to the users of the package.\nIn this chapter we describe useful mechanisms for including data in your package. The practical details differ depending on who needs access to the data, how often it changes, and what they will do with it:\nThe most common location for package data is (surprise!) data/. We recommend that each file in this directory be an .rda file created by save() containing a single R object, with the same name as the file. The easiest way to achieve this is to use usethis::use_data().\nmy_pkg_data <- sample(1000)\nusethis::use_data(my_pkg_data)\nLet’s imagine we are working on a package named “pkg”. The snippet above creates data/my_pkg_data.rda inside the source of the pkg package and adds LazyData: true in your DESCRIPTION. This makes the my_pkg_data R object available to users of pkg via pkg::my_pkg_data or, after attaching pkg with library(pkg), as my_pkg_data.\nThe snippet above is something the maintainer executes once (or every time they need to update my_pkg_data). This is workflow code and should not appear in the R/ directory of the source package. (We’ll talk about a suitable place to keep this code below.) For larger datasets, you may want to experiment with the compression setting, which is under the control of the compress argument. The default is “bzip2”, but sometimes “gzip” or “xz” can create smaller files.\nIt’s possible to use other types of files below data/, but we don’t recommend it because .rda files are already fast, small, and explicit. The other possibilities are described in the documentation for utils::data() and in the Data in packages section of Writing R Extensions. In terms of advice to package authors, the help topic for data() seems to implicitly make the same recommendations as we do above:\nIf the DESCRIPTION contains LazyData: true, then datasets will be lazily loaded. This means that they won’t occupy any memory until you use them. The following example shows memory usage before and after loading the nycflights13 package. You can see that memory usage doesn’t change significantly until you inspect the flights dataset stored inside the package.\nlobstr::mem_used()\n#> 58.00 MB\nlibrary(nycflights13)\nlobstr::mem_used()\n#> 59.76 MB\n\ninvisible(flights)\nlobstr::mem_used()\n#> 100.46 MB\nWe recommend that you include LazyData: true in your DESCRIPTION if you are shipping .rda files below data/. If you use use_data() to create such datasets, it will automatically make this modification to DESCRIPTION for you.",
+ "text": "7.1 Exported data\nIt’s often useful to include data in a package. If the primary purpose of a package is to distribute useful functions, example datasets make it easier to write excellent documentation. These datasets can be hand-crafted to provide compelling use cases for the functions in the package. Here are some examples of this type of package data:\nAt the other extreme, some packages exist solely for the purpose of distributing data, along with its documentation. These are sometimes called “data packages”. A data package can be a nice way to share example data across multiple packages. It is also a useful technique for getting relatively large, static files out of a more function-oriented package, which might require more frequent updates. Here are some examples of data packages:\nFinally, many packages benefit from having internal data that is used for internal purposes, but that is not directly exposed to the users of the package.\nIn this chapter we describe useful mechanisms for including data in your package. The practical details differ depending on who needs access to the data, how often it changes, and what they will do with it:\nThe most common location for package data is (surprise!) data/. We recommend that each file in this directory be an .rda file created by save() containing a single R object, with the same name as the file. The easiest way to achieve this is to use usethis::use_data().\nmy_pkg_data <- sample(1000)\nusethis::use_data(my_pkg_data)\nLet’s imagine we are working on a package named “pkg”. The snippet above creates data/my_pkg_data.rda inside the source of the pkg package and adds LazyData: true in your DESCRIPTION. This makes the my_pkg_data R object available to users of pkg via pkg::my_pkg_data or, after attaching pkg with library(pkg), as my_pkg_data.\nThe snippet above is something the maintainer executes once (or every time they need to update my_pkg_data). This is workflow code and should not appear in the R/ directory of the source package. (We’ll talk about a suitable place to keep this code below.) For larger datasets, you may want to experiment with the compression setting, which is under the control of the compress argument. The default is “bzip2”, but sometimes “gzip” or “xz” can create smaller files.\nIt’s possible to use other types of files below data/, but we don’t recommend it because .rda files are already fast, small, and explicit. The other possibilities are described in the documentation for utils::data() and in the Data in packages section of Writing R Extensions. In terms of advice to package authors, the help topic for data() seems to implicitly make the same recommendations as we do above:\nIf the DESCRIPTION contains LazyData: true, then datasets will be lazily loaded. This means that they won’t occupy any memory until you use them. The following example shows memory usage before and after loading the nycflights13 package. You can see that memory usage doesn’t change significantly until you inspect the flights dataset stored inside the package.\nlobstr::mem_used()\n#> 58.01 MB\nlibrary(nycflights13)\nlobstr::mem_used()\n#> 59.76 MB\n\ninvisible(flights)\nlobstr::mem_used()\n#> 100.47 MB\nWe recommend that you include LazyData: true in your DESCRIPTION if you are shipping .rda files below data/. If you use use_data() to create such datasets, it will automatically make this modification to DESCRIPTION for you.",
"crumbs": [
"Package components",
"7Data"
@@ -1017,7 +1017,7 @@
"href": "dependencies-mindset-background.html#sec-dependencies-namespace",
"title": "10 Dependencies: Mindset and Background",
"section": "\n10.2 Namespace",
- "text": "10.2 Namespace\nSo far, we’ve explained the mechanics of declaring a dependency in DESCRIPTION (Section 9.6) and how to analyze the costs and benefits of dependencies (Section 10.1). Before we explain how to use your dependencies in various parts of your package in Chapter 11, we need to establish the concepts of a package namespace and the search path.\n\n10.2.1 Motivation\nAs the name suggests, namespaces provide “spaces” for “names”. They provide a context for looking up the value of an object associated with a name.\nWithout knowing it, you’ve probably already used namespaces. Have you ever used the :: operator? It disambiguates functions with the same name. For example, both the lubridate and here packages provide a here() function. If you attach lubridate, then here, here() will refer to the here version, because the last package attached wins. But if you attach the packages in the opposite order, here() will refer to the lubridate version.\n\nlibrary(lubridate) | library(here)\nlibrary(here) | library(lubridate)\n\nhere() # here::here() | here() # lubridate::here()\n\nThis can be confusing. Instead, you can qualify the function call with a specific namespace: lubridate::here() and here::here(). Then the order in which the packages are attached won’t matter4.\n\nlubridate::here() # always gets lubridate::here()\nhere::here() # always gets here::here()\n\nAs you will see in Section 11.4, the package::function() calling style is also our default recommendation for how to use your dependencies in the code below R/, because it eliminates all ambiguity.\nBut, in the context of package code, the use of :: is not really our main line of defense against the confusion seen in the example above. In packages, we rely on namespaces to ensure that every package works the same way regardless of what packages are attached by the user.\nConsider the sd() function from the stats package that is part of base R:\n\nsd\n#> function (x, na.rm = FALSE) \n#> sqrt(var(if (is.vector(x) || is.factor(x)) x else as.double(x), \n#> na.rm = na.rm))\n#> <bytecode: 0x558948ac2008>\n#> <environment: namespace:stats>\n\nIt’s defined in terms of another function, var(), also from the stats package. So what happens if we override var() with our own definition? Does it break sd()?\n\nvar <- function(x) -5\nvar(1:5)\n#> [1] -5\n\nsd(1:5)\n#> [1] 1.58\n\nSurprisingly, it does not! That’s because when sd() looks for an object called var(), it looks first in the stats package namespace, so it finds stats::var(), not the var() we created in the global environment. It would be chaos if functions like sd() could be broken by a user redefining var() or by attaching a package that overrides var(). The package namespace system is what saves us from this fate.\n\n10.2.2 The NAMESPACE file\nThe NAMESPACE file plays a key role in defining your package’s namespace. Here are selected lines from the NAMESPACE file in the testthat package:\n# Generated by roxygen2: do not edit by hand\n\nS3method(compare,character)\nS3method(print,testthat_results)\nexport(compare)\nexport(expect_equal)\nimport(rlang)\nimportFrom(brio,readLines)\nuseDynLib(testthat, .registration = TRUE)\nThe first line announces that this file is not written by hand, but rather is generated by the roxygen2 package. We’ll return to this topic soon, after we discuss the remaining lines.\nYou can see that the NAMESPACE file looks a bit like R code (but it is not). Each line contains a directive: S3method(), export(), importFrom(), and so on. Each directive describes an R object, and says whether it’s exported from this package to be used by others, or it’s imported from another package to be used internally.\nThese directives are the most important in our development approach, in order of frequency:\n\n\nexport(): export a function (including S3 and S4 generics).\n\nS3method(): export an S3 method.\n\nimportFrom(): import selected object from another namespace (including S4 generics).\n\nimport(): import all objects from another package’s namespace.\n\nuseDynLib(): registers routines from a DLL (this is specific to packages with compiled code).\n\nThere are other directives that we won’t cover here, because they are explicitly discouraged or they just rarely come up in our development work.\n\n\nexportPattern(): exports all functions that match a pattern. We feel it’s safer to always use explicit exports and we avoid the use of this directive.\n\nexportClasses(), exportMethods(), importClassesFrom(), importMethodsFrom(): export and import S4 classes and methods. We only work in the S4 system when necessary for compatibility with another package, i.e. we generally don’t implement methods or classes that we own with S4. Therefore the S4 coverage in this book is very minimal.\n\nIn the devtools workflow, the NAMESPACE file is not written by hand! Instead, we prefer to generate NAMESPACE with the roxygen2 package, using specific tags located in a roxygen comment above each function’s definition in the R/*.R files (Section 11.3). We will have much more to say about roxygen comments and the roxygen2 package when we discuss package documentation in Chapter 16. For now, we just lay out the reasons we prefer this method of generating the NAMESPACE file:\n\nNamespace tags are integrated into the source code, so when you read the code it’s easier to see what’s being exported and imported and why.\nRoxygen2 abstracts away some of the details of NAMESPACE. You only need to learn one tag, @export, and roxygen2 will figure out which specific directive to use, based on whether the associated object is a regular function, S3 method, S4 method, or S4 class.\nRoxygen2 keeps NAMESPACE tidy. No matter how many times @importFrom foo bar appears in your roxygen comments, you’ll only get one importFrom(foo, bar) in your NAMESPACE. Roxygen2 also keeps NAMESPACE organised in a principled order, sorting first by the directive type and then alphabetically. Roxygen2 takes away the burden of writing NAMESPACE, while also trying to keep the file as readable as possible. This organization also makes Git diffs much more informative.\n\nNote that you can choose to use roxygen2 to generate just NAMESPACE, just man/*.Rd (Chapter 16), or both (as is our practice). If you don’t use any namespace-related tags, roxygen2 won’t touch NAMESPACE. If you don’t use any documentation-related tags, roxygen2 won’t touch man/.",
+ "text": "10.2 Namespace\nSo far, we’ve explained the mechanics of declaring a dependency in DESCRIPTION (Section 9.6) and how to analyze the costs and benefits of dependencies (Section 10.1). Before we explain how to use your dependencies in various parts of your package in Chapter 11, we need to establish the concepts of a package namespace and the search path.\n\n10.2.1 Motivation\nAs the name suggests, namespaces provide “spaces” for “names”. They provide a context for looking up the value of an object associated with a name.\nWithout knowing it, you’ve probably already used namespaces. Have you ever used the :: operator? It disambiguates functions with the same name. For example, both the lubridate and here packages provide a here() function. If you attach lubridate, then here, here() will refer to the here version, because the last package attached wins. But if you attach the packages in the opposite order, here() will refer to the lubridate version.\n\nlibrary(lubridate) | library(here)\nlibrary(here) | library(lubridate)\n\nhere() # here::here() | here() # lubridate::here()\n\nThis can be confusing. Instead, you can qualify the function call with a specific namespace: lubridate::here() and here::here(). Then the order in which the packages are attached won’t matter4.\n\nlubridate::here() # always gets lubridate::here()\nhere::here() # always gets here::here()\n\nAs you will see in Section 11.4, the package::function() calling style is also our default recommendation for how to use your dependencies in the code below R/, because it eliminates all ambiguity.\nBut, in the context of package code, the use of :: is not really our main line of defense against the confusion seen in the example above. In packages, we rely on namespaces to ensure that every package works the same way regardless of what packages are attached by the user.\nConsider the sd() function from the stats package that is part of base R:\n\nsd\n#> function (x, na.rm = FALSE) \n#> sqrt(var(if (is.vector(x) || is.factor(x)) x else as.double(x), \n#> na.rm = na.rm))\n#> <bytecode: 0x55dbe8609da0>\n#> <environment: namespace:stats>\n\nIt’s defined in terms of another function, var(), also from the stats package. So what happens if we override var() with our own definition? Does it break sd()?\n\nvar <- function(x) -5\nvar(1:5)\n#> [1] -5\n\nsd(1:5)\n#> [1] 1.58\n\nSurprisingly, it does not! That’s because when sd() looks for an object called var(), it looks first in the stats package namespace, so it finds stats::var(), not the var() we created in the global environment. It would be chaos if functions like sd() could be broken by a user redefining var() or by attaching a package that overrides var(). The package namespace system is what saves us from this fate.\n\n10.2.2 The NAMESPACE file\nThe NAMESPACE file plays a key role in defining your package’s namespace. Here are selected lines from the NAMESPACE file in the testthat package:\n# Generated by roxygen2: do not edit by hand\n\nS3method(compare,character)\nS3method(print,testthat_results)\nexport(compare)\nexport(expect_equal)\nimport(rlang)\nimportFrom(brio,readLines)\nuseDynLib(testthat, .registration = TRUE)\nThe first line announces that this file is not written by hand, but rather is generated by the roxygen2 package. We’ll return to this topic soon, after we discuss the remaining lines.\nYou can see that the NAMESPACE file looks a bit like R code (but it is not). Each line contains a directive: S3method(), export(), importFrom(), and so on. Each directive describes an R object, and says whether it’s exported from this package to be used by others, or it’s imported from another package to be used internally.\nThese directives are the most important in our development approach, in order of frequency:\n\n\nexport(): export a function (including S3 and S4 generics).\n\nS3method(): export an S3 method.\n\nimportFrom(): import selected object from another namespace (including S4 generics).\n\nimport(): import all objects from another package’s namespace.\n\nuseDynLib(): registers routines from a DLL (this is specific to packages with compiled code).\n\nThere are other directives that we won’t cover here, because they are explicitly discouraged or they just rarely come up in our development work.\n\n\nexportPattern(): exports all functions that match a pattern. We feel it’s safer to always use explicit exports and we avoid the use of this directive.\n\nexportClasses(), exportMethods(), importClassesFrom(), importMethodsFrom(): export and import S4 classes and methods. We only work in the S4 system when necessary for compatibility with another package, i.e. we generally don’t implement methods or classes that we own with S4. Therefore the S4 coverage in this book is very minimal.\n\nIn the devtools workflow, the NAMESPACE file is not written by hand! Instead, we prefer to generate NAMESPACE with the roxygen2 package, using specific tags located in a roxygen comment above each function’s definition in the R/*.R files (Section 11.3). We will have much more to say about roxygen comments and the roxygen2 package when we discuss package documentation in Chapter 16. For now, we just lay out the reasons we prefer this method of generating the NAMESPACE file:\n\nNamespace tags are integrated into the source code, so when you read the code it’s easier to see what’s being exported and imported and why.\nRoxygen2 abstracts away some of the details of NAMESPACE. You only need to learn one tag, @export, and roxygen2 will figure out which specific directive to use, based on whether the associated object is a regular function, S3 method, S4 method, or S4 class.\nRoxygen2 keeps NAMESPACE tidy. No matter how many times @importFrom foo bar appears in your roxygen comments, you’ll only get one importFrom(foo, bar) in your NAMESPACE. Roxygen2 also keeps NAMESPACE organised in a principled order, sorting first by the directive type and then alphabetically. Roxygen2 takes away the burden of writing NAMESPACE, while also trying to keep the file as readable as possible. This organization also makes Git diffs much more informative.\n\nNote that you can choose to use roxygen2 to generate just NAMESPACE, just man/*.Rd (Chapter 16), or both (as is our practice). If you don’t use any namespace-related tags, roxygen2 won’t touch NAMESPACE. If you don’t use any documentation-related tags, roxygen2 won’t touch man/.",
"crumbs": [
"Package metadata",
"10Dependencies: Mindset and Background"
@@ -1303,7 +1303,7 @@
"href": "testing-basics.html#test-organisation",
"title": "13 Testing basics",
"section": "\n13.4 Test organisation",
- "text": "13.4 Test organisation\nA test file lives in tests/testthat/. Its name must start with test. We will inspect and execute a test file from the stringr package.\n\nBut first, for the purposes of rendering this book, we must attach stringr and testthat. Note that in real-life test-running situations, this is taken care of by your package development tooling:\n\nDuring interactive development, devtools::load_all() makes testthat and the package-under-development available (both its exported and unexported functions).\nDuring arms-length test execution, this is taken care of by devtools::test_active_file(), devtools::test(), and tests/testthat.R.\n\n\n\n\n\n\n\nImportant\n\n\n\nYour test files should not include these library() calls. We also explicitly request testthat edition 3, but in a real package this will be declared in DESCRIPTION.\n\nlibrary(testthat)\nlibrary(stringr)\nlocal_edition(3)\n\n\n\nHere are the contents of tests/testthat/test-dup.r from stringr:\n\ntest_that(\"basic duplication works\", {\n expect_equal(str_dup(\"a\", 3), \"aaa\")\n expect_equal(str_dup(\"abc\", 2), \"abcabc\")\n expect_equal(str_dup(c(\"a\", \"b\"), 2), c(\"aa\", \"bb\"))\n expect_equal(str_dup(c(\"a\", \"b\"), c(2, 3)), c(\"aa\", \"bbb\"))\n})\n#> Test passed 😀\n\ntest_that(\"0 duplicates equals empty string\", {\n expect_equal(str_dup(\"a\", 0), \"\")\n expect_equal(str_dup(c(\"a\", \"b\"), 0), rep(\"\", 2))\n})\n#> Test passed 😸\n\ntest_that(\"uses tidyverse recycling rules\", {\n expect_error(str_dup(1:2, 1:3), class = \"vctrs_error_incompatible_size\")\n})\n#> Test passed 🥇\n\nThis file shows a typical mix of tests:\n\n“basic duplication works” tests typical usage of str_dup().\n“0 duplicates equals empty string” probes a specific edge case.\n“uses tidyverse recycling rules” checks that malformed input results in a specific kind of error.\n\nTests are organised hierarchically: expectations are grouped into tests which are organised in files:\n\nA file holds multiple related tests. In this example, the file tests/testthat/test-dup.r has all of the tests for the code in R/dup.r.\n\nA test groups together multiple expectations to test the output from a simple function, a range of possibilities for a single parameter from a more complicated function, or tightly related functionality from across multiple functions. This is why they are sometimes called unit tests. Each test should cover a single unit of functionality. A test is created with test_that(desc, code).\nIt’s common to write the description (desc) to create something that reads naturally, e.g. test_that(\"basic duplication works\", { ... }). A test failure report includes this description, which is why you want a concise statement of the test’s purpose, e.g. a specific behaviour.\n\nAn expectation is the atom of testing. It describes the expected result of a computation: Does it have the right value and right class? Does it produce an error when it should? An expectation automates visual checking of results in the console. Expectations are functions that start with expect_.\n\nYou want to arrange things such that, when a test fails, you’ll know what’s wrong and where in your code to look for the problem. This motivates all our recommendations regarding file organisation, file naming, and the test description. Finally, try to avoid putting too many expectations in one test - it’s better to have more smaller tests than fewer larger tests.",
+ "text": "13.4 Test organisation\nA test file lives in tests/testthat/. Its name must start with test. We will inspect and execute a test file from the stringr package.\n\nBut first, for the purposes of rendering this book, we must attach stringr and testthat. Note that in real-life test-running situations, this is taken care of by your package development tooling:\n\nDuring interactive development, devtools::load_all() makes testthat and the package-under-development available (both its exported and unexported functions).\nDuring arms-length test execution, this is taken care of by devtools::test_active_file(), devtools::test(), and tests/testthat.R.\n\n\n\n\n\n\n\nImportant\n\n\n\nYour test files should not include these library() calls. We also explicitly request testthat edition 3, but in a real package this will be declared in DESCRIPTION.\n\nlibrary(testthat)\nlibrary(stringr)\nlocal_edition(3)\n\n\n\nHere are the contents of tests/testthat/test-dup.r from stringr:\n\ntest_that(\"basic duplication works\", {\n expect_equal(str_dup(\"a\", 3), \"aaa\")\n expect_equal(str_dup(\"abc\", 2), \"abcabc\")\n expect_equal(str_dup(c(\"a\", \"b\"), 2), c(\"aa\", \"bb\"))\n expect_equal(str_dup(c(\"a\", \"b\"), c(2, 3)), c(\"aa\", \"bbb\"))\n})\n#> Test passed 🌈\n\ntest_that(\"0 duplicates equals empty string\", {\n expect_equal(str_dup(\"a\", 0), \"\")\n expect_equal(str_dup(c(\"a\", \"b\"), 0), rep(\"\", 2))\n})\n#> Test passed 🌈\n\ntest_that(\"uses tidyverse recycling rules\", {\n expect_error(str_dup(1:2, 1:3), class = \"vctrs_error_incompatible_size\")\n})\n#> Test passed 🎉\n\nThis file shows a typical mix of tests:\n\n“basic duplication works” tests typical usage of str_dup().\n“0 duplicates equals empty string” probes a specific edge case.\n“uses tidyverse recycling rules” checks that malformed input results in a specific kind of error.\n\nTests are organised hierarchically: expectations are grouped into tests which are organised in files:\n\nA file holds multiple related tests. In this example, the file tests/testthat/test-dup.r has all of the tests for the code in R/dup.r.\n\nA test groups together multiple expectations to test the output from a simple function, a range of possibilities for a single parameter from a more complicated function, or tightly related functionality from across multiple functions. This is why they are sometimes called unit tests. Each test should cover a single unit of functionality. A test is created with test_that(desc, code).\nIt’s common to write the description (desc) to create something that reads naturally, e.g. test_that(\"basic duplication works\", { ... }). A test failure report includes this description, which is why you want a concise statement of the test’s purpose, e.g. a specific behaviour.\n\nAn expectation is the atom of testing. It describes the expected result of a computation: Does it have the right value and right class? Does it produce an error when it should? An expectation automates visual checking of results in the console. Expectations are functions that start with expect_.\n\nYou want to arrange things such that, when a test fails, you’ll know what’s wrong and where in your code to look for the problem. This motivates all our recommendations regarding file organisation, file naming, and the test description. Finally, try to avoid putting too many expectations in one test - it’s better to have more smaller tests than fewer larger tests.",
"crumbs": [
"Testing",
"13Testing basics"
@@ -1358,7 +1358,7 @@
"href": "testing-design.html#sec-testing-design-principles",
"title": "14 Designing your test suite",
"section": "\n14.2 High-level principles for testing",
- "text": "14.2 High-level principles for testing\nIn later sections, we offer concrete strategies for how to handle common testing dilemmas in R. Here we lay out the high-level principles that underpin these recommendations:\n\nA test should ideally be self-sufficient and self-contained.\nThe interactive workflow is important, because you will mostly interact with your tests when they are failing.\nIt’s more important that test code be obvious than, e.g., as DRY as possible.\nHowever, the interactive workflow shouldn’t “leak” into and undermine the test suite.\n\nWriting good tests for a code base often feels more challenging than writing the code in the first place. This can come as a bit of a shock when you’re new to package development and you might be concerned that you’re doing it wrong. Don’t worry, you’re not! Testing presents many unique challenges and maneuvers, which tend to get much less air time in programming communities than strategies for writing the “main code”, i.e. the stuff below R/. As a result, it requires more deliberate effort to develop your skills and taste around testing.\nMany of the packages maintained by our team violate some of the advice you’ll find here. There are (at least) two reasons for that:\n\ntestthat has been evolving for more than twelve years and this chapter reflects the cumulative lessons learned from that experience. The tests in many packages have been in place for a long time and reflect typical practices from different eras and different maintainers.\nThese aren’t hard and fast rules, but are, rather, guidelines. There will always be specific situations where it makes sense to bend the rule.\n\nThis chapter can’t address all possible testing situations, but hopefully these guidelines will help your future decision-making.\n\n14.2.1 Self-sufficient tests\n\nAll tests should strive to be hermetic: a test should contain all of the information necessary to set up, execute, and tear down its environment. Tests should assume as little as possible about the outside environment ….\nFrom the book Software Engineering at Google, Chapter 11\n\nRecall this advice found in Section 6.5, which covers your package’s “main code”, i.e. everything below R/:\n\nThe .R files below R/ should consist almost entirely of function definitions. Any other top-level code is suspicious and should be carefully reviewed for possible conversion into a function.\n\nWe have analogous advice for your test files:\n\nThe test-*.R files below tests/testthat/ should consist almost entirely of calls to test_that(). Any other top-level code is suspicious and should be carefully considered for relocation into calls to test_that() or to other files that get special treatment inside an R package or from testthat.\n\nEliminating (or at least minimizing) top-level code outside of test_that() will have the beneficial effect of making your tests more hermetic. This is basically the testing analogue of the general programming advice that it’s wise to avoid unstructured sharing of state.\nLogic at the top-level of a test file has an awkward scope: Objects or functions defined here have what you might call “test file scope”, if the definitions appear before the first call to test_that(). If top-level code is interleaved between test_that() calls, you can even create “partial test file scope”.\nWhile writing tests, it can feel convenient to rely on these file-scoped objects, especially early in the life of a test suite, e.g. when each test file fits on one screen. But we find that implicitly relying on objects in a test’s parent environment tends to make a test suite harder to understand and maintain over time.\nConsider a test file with top-level code sprinkled around it, outside of test_that():\n\ndat <- data.frame(x = c(\"a\", \"b\", \"c\"), y = c(1, 2, 3))\n\nskip_if(today_is_a_monday())\n\ntest_that(\"foofy() does this\", {\n expect_equal(foofy(dat), ...)\n})\n\ndat2 <- data.frame(x = c(\"x\", \"y\", \"z\"), y = c(4, 5, 6))\n\nskip_on_os(\"windows\")\n\ntest_that(\"foofy2() does that\", {\n expect_snapshot(foofy2(dat, dat2))\n})\n\nWe recommend relocating file-scoped logic to either a narrower scope or to a broader scope. Here’s what it would look like to use a narrow scope, i.e. to inline everything inside test_that() calls:\n\ntest_that(\"foofy() does this\", {\n skip_if(today_is_a_monday())\n \n dat <- data.frame(x = c(\"a\", \"b\", \"c\"), y = c(1, 2, 3))\n \n expect_equal(foofy(dat), ...)\n})\n\ntest_that(\"foofy() does that\", {\n skip_if(today_is_a_monday())\n skip_on_os(\"windows\")\n \n dat <- data.frame(x = c(\"a\", \"b\", \"c\"), y = c(1, 2, 3))\n dat2 <- data.frame(x = c(\"x\", \"y\", \"z\"), y = c(4, 5, 6))\n \n expect_snapshot(foofy(dat, dat2))\n})\n\nBelow we will discuss techniques for moving file-scoped logic to a broader scope.\n\n14.2.2 Self-contained tests\nEach test_that() test has its own execution environment, which makes it somewhat self-contained. For example, an R object you create inside a test does not exist after the test exits:\n\nexists(\"thingy\")\n#> [1] FALSE\n\ntest_that(\"thingy exists\", {\n thingy <- \"thingy\"\n expect_true(exists(thingy))\n})\n#> Test passed 🥳\n\nexists(\"thingy\")\n#> [1] FALSE\n\nThe thingy object lives and dies entirely within the confines of test_that(). However, testthat doesn’t know how to cleanup after actions that affect other aspects of the R landscape:\n\nThe filesystem: creating and deleting files, changing the working directory, etc.\nThe search path: library(), attach().\nGlobal options, like options() and par(), and environment variables.\n\nWatch how calls like library(), options(), and Sys.setenv() have a persistent effect after a test, even when they are executed inside test_that():\n\ngrep(\"jsonlite\", search(), value = TRUE)\n#> character(0)\ngetOption(\"opt_whatever\")\n#> NULL\nSys.getenv(\"envvar_whatever\")\n#> [1] \"\"\n\ntest_that(\"landscape changes leak outside the test\", {\n library(jsonlite)\n options(opt_whatever = \"whatever\")\n Sys.setenv(envvar_whatever = \"whatever\")\n \n expect_match(search(), \"jsonlite\", all = FALSE)\n expect_equal(getOption(\"opt_whatever\"), \"whatever\")\n expect_equal(Sys.getenv(\"envvar_whatever\"), \"whatever\")\n})\n#> Test passed 😀\n\ngrep(\"jsonlite\", search(), value = TRUE)\n#> [1] \"package:jsonlite\"\ngetOption(\"opt_whatever\")\n#> [1] \"whatever\"\nSys.getenv(\"envvar_whatever\")\n#> [1] \"whatever\"\n\nThese changes to the landscape even persist beyond the current test file, i.e. they carry over into all subsequent test files.\nIf it’s easy to avoid making such changes in your test code, that is the best strategy! But if it’s unavoidable, then you have to make sure that you clean up after yourself. This mindset is very similar to one we advocated for in Section 6.5, when discussing how to design well-mannered functions.\nWe like to use the withr package (https://withr.r-lib.org) to make temporary changes in global state, because it automatically captures the initial state and arranges the eventual restoration. You’ve already seen an example of its usage, when we explored snapshot tests:\n\ntest_that(\"side-by-side diffs work\", {\n withr::local_options(width = 20) # <-- (°_°) look here!\n expect_snapshot(\n waldo::compare(c(\"X\", letters), c(letters, \"X\"))\n )\n})\n\nThis test requires the display width to be set at 20 columns, which is considerably less than the default width. withr::local_options(width = 20) sets the width option to 20 and, at the end of the test, restores the option to its original value. withr is also pleasant to use during interactive development: deferred actions are still captured on the global environment and can be executed explicitly via withr::deferred_run() or implicitly by restarting R.\nWe recommend including withr in Suggests, if you’re only going to use it in your tests, or in Imports, if you also use it below R/. Call withr functions as we do above, e.g. like withr::local_whatever(), in either case. See Section 10.4.1 and Section 11.5.2 for more.\n\n\n\n\n\n\nTip\n\n\n\nThe easiest way to add a package to DESCRIPTION is with, e.g., usethis::use_package(\"withr\", type = \"Suggests\"). For tidyverse packages, withr is considered a “free dependency”, i.e. the tidyverse uses withr so extensively that we don’t hesitate to use it whenever it would be useful.\n\n\nwithr has a large set of pre-implemented local_*() / with_*() functions that should handle most of your testing needs, so check there before you write your own. If nothing exists that meets your need, withr::defer() is the general way to schedule some action at the end of a test.1\nHere’s how we would fix the problems in the previous example using withr: Behind the scenes, we reversed the landscape changes, so we can try this again.\n\ngrep(\"jsonlite\", search(), value = TRUE)\n#> character(0)\ngetOption(\"opt_whatever\")\n#> NULL\nSys.getenv(\"envvar_whatever\")\n#> [1] \"\"\n\ntest_that(\"withr makes landscape changes local to a test\", {\n withr::local_package(\"jsonlite\")\n withr::local_options(opt_whatever = \"whatever\")\n withr::local_envvar(envvar_whatever = \"whatever\")\n \n expect_match(search(), \"jsonlite\", all = FALSE)\n expect_equal(getOption(\"opt_whatever\"), \"whatever\")\n expect_equal(Sys.getenv(\"envvar_whatever\"), \"whatever\")\n})\n#> Test passed 🎊\n\ngrep(\"jsonlite\", search(), value = TRUE)\n#> character(0)\ngetOption(\"opt_whatever\")\n#> NULL\nSys.getenv(\"envvar_whatever\")\n#> [1] \"\"\n\ntestthat leans heavily on withr to make test execution environments as reproducible and self-contained as possible. In testthat 3e, testthat::local_reproducible_output() is implicitly part of each test_that() test.\n\ntest_that(\"something specific happens\", {\n local_reproducible_output() # <-- this happens implicitly\n \n # your test code, which might be sensitive to ambient conditions, such as\n # display width or the number of supported colors\n})\n\nlocal_reproducible_output() temporarily sets various options and environment variables to values favorable for testing, e.g. it suppresses colored output, turns off fancy quotes, sets the console width, and sets LC_COLLATE = \"C\". Usually, you can just passively enjoy the benefits of local_reproducible_output(). But you may want to call it explicitly when replicating test results interactively or if you want to override the default settings in a specific test.\n\n14.2.3 Plan for test failure\nWe regret to inform you that most of the quality time you spend with your tests will be when they are inexplicably failing.\n\nIn its purest form, automating testing consists of three activities: writing tests, running tests, and reacting to test failures….\nRemember that tests are often revisited only when something breaks. When you are called to fix a broken test that you have never seen before, you will be thankful someone took the time to make it easy to understand. Code is read far more than it is written, so make sure you write the test you’d like to read!\nFrom the book Software Engineering at Google, Chapter 11\n\nMost of us don’t work on a code base the size of Google. But even in a team of one, tests that you wrote six months ago might as well have been written by someone else. Especially when they are failing.\nWhen we do reverse dependency checks, often involving hundreds or thousands of CRAN packages, we have to inspect test failures to determine if changes in our packages are to blame. As a result, we regularly engage with failing tests in other people’s packages, which leaves us with lots of opinions about practices that create unnecessary testing pain.\nTest troubleshooting nirvana looks like this: In a fresh R session, you can do devtools::load_all() and immediately run an individual test or walk through it line-by-line. There is no need to hunt around for setup code that has to be run manually first, that is found elsewhere in the test file or perhaps in a different file altogether. Test-related code that lives in an unconventional location causes extra self-inflicted pain when you least need it.\nConsider this extreme and abstract example of a test that is difficult to troubleshoot due to implicit dependencies on free-range code:\n\n# dozens or hundreds of lines of top-level code, interspersed with other tests,\n# which you must read and selectively execute\n\ntest_that(\"f() works\", {\n x <- function_from_some_dependency(object_with_unknown_origin)\n expect_equal(f(x), 2.5)\n})\n\nThis test is much easier to drop in on if dependencies are invoked in the normal way, i.e. via ::, and test objects are created inline:\n\n# dozens or hundreds of lines of self-sufficient, self-contained tests,\n# all of which you can safely ignore!\n\ntest_that(\"f() works\", {\n useful_thing <- ...\n x <- somePkg::someFunction(useful_thing)\n expect_equal(f(x), 2.5)\n})\n\nThis test is self-sufficient. The code inside { ... } explicitly creates any necessary objects or conditions and makes explicit calls to any helper functions. This test doesn’t rely on objects or dependencies that happen to be ambiently available.\nSelf-sufficient, self-contained tests are a win-win: It is literally safer to design tests this way and it also makes tests much easier for humans to troubleshoot later.\n\n14.2.4 Repetition is OK\nOne obvious consequence of our suggestion to minimize code with “file scope” is that your tests will probably have some repetition. And that’s OK! We’re going to make the controversial recommendation that you tolerate a fair amount of duplication in test code, i.e. you can relax some of your DRY (“don’t repeat yourself”) tendencies.\n\nKeep the reader in your test function. Good production code is well-factored; good test code is obvious. … think about what will make the problem obvious when a test fails.\nFrom the blog post Why Good Developers Write Bad Unit Tests\n\nHere’s a toy example to make things concrete.\n\ntest_that(\"multiplication works\", {\n useful_thing <- 3\n expect_equal(2 * useful_thing, 6)\n})\n#> Test passed 🌈\n\ntest_that(\"subtraction works\", {\n useful_thing <- 3\n expect_equal(5 - useful_thing, 2)\n})\n#> Test passed 🥇\n\nIn real life, useful_thing is usually a more complicated object that somehow feels burdensome to instantiate. Notice how useful_thing <- 3 appears in more than one place. Conventional wisdom says we should DRY this code out. It’s tempting to just move useful_thing’s definition outside of the tests:\n\nuseful_thing <- 3\n\ntest_that(\"multiplication works\", {\n expect_equal(2 * useful_thing, 6)\n})\n#> Test passed 🥳\n\ntest_that(\"subtraction works\", {\n expect_equal(5 - useful_thing, 2)\n})\n#> Test passed 🎉\n\nBut we really do think the first form, with the repetition, is often the better choice.\nAt this point, many readers might be thinking “but the code I might have to repeat is much longer than 1 line!”. Below we describe the use of test fixtures. This can often reduce complicated situations back to something that resembles this simple example.\n\n14.2.5 Remove tension between interactive and automated testing\nYour test code will be executed in two different settings:\n\nInteractive test development and maintenance, which includes tasks like:\n\nInitial test creation\nModifying tests to adapt to change\nDebugging test failure\n\n\nAutomated test runs, which is accomplished with functions such as:\n\nSingle file: devtools::test_active_file(), testthat::test_file()\n\nWhole package: devtools::test(), devtools::check()\n\n\n\n\nAutomated testing of your whole package is what takes priority. This is ultimately the whole point of your tests. However, the interactive experience is clearly important for the humans doing this work. Therefore it’s important to find a pleasant workflow, but also to ensure that you don’t rig anything for interactive convenience that actually compromises the health of the test suite.\nThese two modes of test-running should not be in conflict with each other. If you perceive tension between these two modes, this can indicate that you’re not taking full advantage of some of testthat’s features and the way it’s designed to work with devtools::load_all().\nWhen working on your tests, use load_all(), just like you do when working below R/. By default, load_all() does all of these things:\n\nSimulates re-building, re-installing, and re-loading your package.\nMakes everything in your package’s namespace available, including unexported functions and objects and anything you’ve imported from another package.\nAttaches testthat, i.e. does library(testthat).\nRuns test helper files, i.e. executes test/testthat/helper.R (more on that below).\n\nThis eliminates the need for any library() calls below tests/testthat/, for the vast majority of R packages. Any instance of library(testthat) is clearly no longer necessary. Likewise, any instance of attaching one of your dependencies via library(somePkg) is unnecessary. In your tests, if you need to call functions from somePkg, do it just as you do below R/. If you have imported the function into your namespace, use fun(). If you have not, use somePkg::fun(). It’s fair to say that library(somePkg) in the tests should be about as rare as taking a dependency via Depends, i.e. there is almost always a better alternative.\nUnnecessary calls to library(somePkg) in test files have a real downside, because they actually change the R landscape. library() alters the search path. This means the circumstances under which you are testing may not necessarily reflect the circumstances under which your package will be used. This makes it easier to create subtle test bugs, which you will have to unravel in the future.\nOne other function that should almost never appear below tests/testhat/ is source(). There are several special files with an official role in testthat workflows (see below), not to mention the entire R package machinery, that provide better ways to make functions, objects, and other logic available in your tests.",
+ "text": "14.2 High-level principles for testing\nIn later sections, we offer concrete strategies for how to handle common testing dilemmas in R. Here we lay out the high-level principles that underpin these recommendations:\n\nA test should ideally be self-sufficient and self-contained.\nThe interactive workflow is important, because you will mostly interact with your tests when they are failing.\nIt’s more important that test code be obvious than, e.g., as DRY as possible.\nHowever, the interactive workflow shouldn’t “leak” into and undermine the test suite.\n\nWriting good tests for a code base often feels more challenging than writing the code in the first place. This can come as a bit of a shock when you’re new to package development and you might be concerned that you’re doing it wrong. Don’t worry, you’re not! Testing presents many unique challenges and maneuvers, which tend to get much less air time in programming communities than strategies for writing the “main code”, i.e. the stuff below R/. As a result, it requires more deliberate effort to develop your skills and taste around testing.\nMany of the packages maintained by our team violate some of the advice you’ll find here. There are (at least) two reasons for that:\n\ntestthat has been evolving for more than twelve years and this chapter reflects the cumulative lessons learned from that experience. The tests in many packages have been in place for a long time and reflect typical practices from different eras and different maintainers.\nThese aren’t hard and fast rules, but are, rather, guidelines. There will always be specific situations where it makes sense to bend the rule.\n\nThis chapter can’t address all possible testing situations, but hopefully these guidelines will help your future decision-making.\n\n14.2.1 Self-sufficient tests\n\nAll tests should strive to be hermetic: a test should contain all of the information necessary to set up, execute, and tear down its environment. Tests should assume as little as possible about the outside environment ….\nFrom the book Software Engineering at Google, Chapter 11\n\nRecall this advice found in Section 6.5, which covers your package’s “main code”, i.e. everything below R/:\n\nThe .R files below R/ should consist almost entirely of function definitions. Any other top-level code is suspicious and should be carefully reviewed for possible conversion into a function.\n\nWe have analogous advice for your test files:\n\nThe test-*.R files below tests/testthat/ should consist almost entirely of calls to test_that(). Any other top-level code is suspicious and should be carefully considered for relocation into calls to test_that() or to other files that get special treatment inside an R package or from testthat.\n\nEliminating (or at least minimizing) top-level code outside of test_that() will have the beneficial effect of making your tests more hermetic. This is basically the testing analogue of the general programming advice that it’s wise to avoid unstructured sharing of state.\nLogic at the top-level of a test file has an awkward scope: Objects or functions defined here have what you might call “test file scope”, if the definitions appear before the first call to test_that(). If top-level code is interleaved between test_that() calls, you can even create “partial test file scope”.\nWhile writing tests, it can feel convenient to rely on these file-scoped objects, especially early in the life of a test suite, e.g. when each test file fits on one screen. But we find that implicitly relying on objects in a test’s parent environment tends to make a test suite harder to understand and maintain over time.\nConsider a test file with top-level code sprinkled around it, outside of test_that():\n\ndat <- data.frame(x = c(\"a\", \"b\", \"c\"), y = c(1, 2, 3))\n\nskip_if(today_is_a_monday())\n\ntest_that(\"foofy() does this\", {\n expect_equal(foofy(dat), ...)\n})\n\ndat2 <- data.frame(x = c(\"x\", \"y\", \"z\"), y = c(4, 5, 6))\n\nskip_on_os(\"windows\")\n\ntest_that(\"foofy2() does that\", {\n expect_snapshot(foofy2(dat, dat2))\n})\n\nWe recommend relocating file-scoped logic to either a narrower scope or to a broader scope. Here’s what it would look like to use a narrow scope, i.e. to inline everything inside test_that() calls:\n\ntest_that(\"foofy() does this\", {\n skip_if(today_is_a_monday())\n \n dat <- data.frame(x = c(\"a\", \"b\", \"c\"), y = c(1, 2, 3))\n \n expect_equal(foofy(dat), ...)\n})\n\ntest_that(\"foofy() does that\", {\n skip_if(today_is_a_monday())\n skip_on_os(\"windows\")\n \n dat <- data.frame(x = c(\"a\", \"b\", \"c\"), y = c(1, 2, 3))\n dat2 <- data.frame(x = c(\"x\", \"y\", \"z\"), y = c(4, 5, 6))\n \n expect_snapshot(foofy(dat, dat2))\n})\n\nBelow we will discuss techniques for moving file-scoped logic to a broader scope.\n\n14.2.2 Self-contained tests\nEach test_that() test has its own execution environment, which makes it somewhat self-contained. For example, an R object you create inside a test does not exist after the test exits:\n\nexists(\"thingy\")\n#> [1] FALSE\n\ntest_that(\"thingy exists\", {\n thingy <- \"thingy\"\n expect_true(exists(thingy))\n})\n#> Test passed 🎉\n\nexists(\"thingy\")\n#> [1] FALSE\n\nThe thingy object lives and dies entirely within the confines of test_that(). However, testthat doesn’t know how to cleanup after actions that affect other aspects of the R landscape:\n\nThe filesystem: creating and deleting files, changing the working directory, etc.\nThe search path: library(), attach().\nGlobal options, like options() and par(), and environment variables.\n\nWatch how calls like library(), options(), and Sys.setenv() have a persistent effect after a test, even when they are executed inside test_that():\n\ngrep(\"jsonlite\", search(), value = TRUE)\n#> character(0)\ngetOption(\"opt_whatever\")\n#> NULL\nSys.getenv(\"envvar_whatever\")\n#> [1] \"\"\n\ntest_that(\"landscape changes leak outside the test\", {\n library(jsonlite)\n options(opt_whatever = \"whatever\")\n Sys.setenv(envvar_whatever = \"whatever\")\n \n expect_match(search(), \"jsonlite\", all = FALSE)\n expect_equal(getOption(\"opt_whatever\"), \"whatever\")\n expect_equal(Sys.getenv(\"envvar_whatever\"), \"whatever\")\n})\n#> Test passed 🎉\n\ngrep(\"jsonlite\", search(), value = TRUE)\n#> [1] \"package:jsonlite\"\ngetOption(\"opt_whatever\")\n#> [1] \"whatever\"\nSys.getenv(\"envvar_whatever\")\n#> [1] \"whatever\"\n\nThese changes to the landscape even persist beyond the current test file, i.e. they carry over into all subsequent test files.\nIf it’s easy to avoid making such changes in your test code, that is the best strategy! But if it’s unavoidable, then you have to make sure that you clean up after yourself. This mindset is very similar to one we advocated for in Section 6.5, when discussing how to design well-mannered functions.\nWe like to use the withr package (https://withr.r-lib.org) to make temporary changes in global state, because it automatically captures the initial state and arranges the eventual restoration. You’ve already seen an example of its usage, when we explored snapshot tests:\n\ntest_that(\"side-by-side diffs work\", {\n withr::local_options(width = 20) # <-- (°_°) look here!\n expect_snapshot(\n waldo::compare(c(\"X\", letters), c(letters, \"X\"))\n )\n})\n\nThis test requires the display width to be set at 20 columns, which is considerably less than the default width. withr::local_options(width = 20) sets the width option to 20 and, at the end of the test, restores the option to its original value. withr is also pleasant to use during interactive development: deferred actions are still captured on the global environment and can be executed explicitly via withr::deferred_run() or implicitly by restarting R.\nWe recommend including withr in Suggests, if you’re only going to use it in your tests, or in Imports, if you also use it below R/. Call withr functions as we do above, e.g. like withr::local_whatever(), in either case. See Section 10.4.1 and Section 11.5.2 for more.\n\n\n\n\n\n\nTip\n\n\n\nThe easiest way to add a package to DESCRIPTION is with, e.g., usethis::use_package(\"withr\", type = \"Suggests\"). For tidyverse packages, withr is considered a “free dependency”, i.e. the tidyverse uses withr so extensively that we don’t hesitate to use it whenever it would be useful.\n\n\nwithr has a large set of pre-implemented local_*() / with_*() functions that should handle most of your testing needs, so check there before you write your own. If nothing exists that meets your need, withr::defer() is the general way to schedule some action at the end of a test.1\nHere’s how we would fix the problems in the previous example using withr: Behind the scenes, we reversed the landscape changes, so we can try this again.\n\ngrep(\"jsonlite\", search(), value = TRUE)\n#> character(0)\ngetOption(\"opt_whatever\")\n#> NULL\nSys.getenv(\"envvar_whatever\")\n#> [1] \"\"\n\ntest_that(\"withr makes landscape changes local to a test\", {\n withr::local_package(\"jsonlite\")\n withr::local_options(opt_whatever = \"whatever\")\n withr::local_envvar(envvar_whatever = \"whatever\")\n \n expect_match(search(), \"jsonlite\", all = FALSE)\n expect_equal(getOption(\"opt_whatever\"), \"whatever\")\n expect_equal(Sys.getenv(\"envvar_whatever\"), \"whatever\")\n})\n#> Test passed 🎉\n\ngrep(\"jsonlite\", search(), value = TRUE)\n#> character(0)\ngetOption(\"opt_whatever\")\n#> NULL\nSys.getenv(\"envvar_whatever\")\n#> [1] \"\"\n\ntestthat leans heavily on withr to make test execution environments as reproducible and self-contained as possible. In testthat 3e, testthat::local_reproducible_output() is implicitly part of each test_that() test.\n\ntest_that(\"something specific happens\", {\n local_reproducible_output() # <-- this happens implicitly\n \n # your test code, which might be sensitive to ambient conditions, such as\n # display width or the number of supported colors\n})\n\nlocal_reproducible_output() temporarily sets various options and environment variables to values favorable for testing, e.g. it suppresses colored output, turns off fancy quotes, sets the console width, and sets LC_COLLATE = \"C\". Usually, you can just passively enjoy the benefits of local_reproducible_output(). But you may want to call it explicitly when replicating test results interactively or if you want to override the default settings in a specific test.\n\n14.2.3 Plan for test failure\nWe regret to inform you that most of the quality time you spend with your tests will be when they are inexplicably failing.\n\nIn its purest form, automating testing consists of three activities: writing tests, running tests, and reacting to test failures….\nRemember that tests are often revisited only when something breaks. When you are called to fix a broken test that you have never seen before, you will be thankful someone took the time to make it easy to understand. Code is read far more than it is written, so make sure you write the test you’d like to read!\nFrom the book Software Engineering at Google, Chapter 11\n\nMost of us don’t work on a code base the size of Google. But even in a team of one, tests that you wrote six months ago might as well have been written by someone else. Especially when they are failing.\nWhen we do reverse dependency checks, often involving hundreds or thousands of CRAN packages, we have to inspect test failures to determine if changes in our packages are to blame. As a result, we regularly engage with failing tests in other people’s packages, which leaves us with lots of opinions about practices that create unnecessary testing pain.\nTest troubleshooting nirvana looks like this: In a fresh R session, you can do devtools::load_all() and immediately run an individual test or walk through it line-by-line. There is no need to hunt around for setup code that has to be run manually first, that is found elsewhere in the test file or perhaps in a different file altogether. Test-related code that lives in an unconventional location causes extra self-inflicted pain when you least need it.\nConsider this extreme and abstract example of a test that is difficult to troubleshoot due to implicit dependencies on free-range code:\n\n# dozens or hundreds of lines of top-level code, interspersed with other tests,\n# which you must read and selectively execute\n\ntest_that(\"f() works\", {\n x <- function_from_some_dependency(object_with_unknown_origin)\n expect_equal(f(x), 2.5)\n})\n\nThis test is much easier to drop in on if dependencies are invoked in the normal way, i.e. via ::, and test objects are created inline:\n\n# dozens or hundreds of lines of self-sufficient, self-contained tests,\n# all of which you can safely ignore!\n\ntest_that(\"f() works\", {\n useful_thing <- ...\n x <- somePkg::someFunction(useful_thing)\n expect_equal(f(x), 2.5)\n})\n\nThis test is self-sufficient. The code inside { ... } explicitly creates any necessary objects or conditions and makes explicit calls to any helper functions. This test doesn’t rely on objects or dependencies that happen to be ambiently available.\nSelf-sufficient, self-contained tests are a win-win: It is literally safer to design tests this way and it also makes tests much easier for humans to troubleshoot later.\n\n14.2.4 Repetition is OK\nOne obvious consequence of our suggestion to minimize code with “file scope” is that your tests will probably have some repetition. And that’s OK! We’re going to make the controversial recommendation that you tolerate a fair amount of duplication in test code, i.e. you can relax some of your DRY (“don’t repeat yourself”) tendencies.\n\nKeep the reader in your test function. Good production code is well-factored; good test code is obvious. … think about what will make the problem obvious when a test fails.\nFrom the blog post Why Good Developers Write Bad Unit Tests\n\nHere’s a toy example to make things concrete.\n\ntest_that(\"multiplication works\", {\n useful_thing <- 3\n expect_equal(2 * useful_thing, 6)\n})\n#> Test passed 😀\n\ntest_that(\"subtraction works\", {\n useful_thing <- 3\n expect_equal(5 - useful_thing, 2)\n})\n#> Test passed 🥳\n\nIn real life, useful_thing is usually a more complicated object that somehow feels burdensome to instantiate. Notice how useful_thing <- 3 appears in more than one place. Conventional wisdom says we should DRY this code out. It’s tempting to just move useful_thing’s definition outside of the tests:\n\nuseful_thing <- 3\n\ntest_that(\"multiplication works\", {\n expect_equal(2 * useful_thing, 6)\n})\n#> Test passed 🌈\n\ntest_that(\"subtraction works\", {\n expect_equal(5 - useful_thing, 2)\n})\n#> Test passed 😀\n\nBut we really do think the first form, with the repetition, is often the better choice.\nAt this point, many readers might be thinking “but the code I might have to repeat is much longer than 1 line!”. Below we describe the use of test fixtures. This can often reduce complicated situations back to something that resembles this simple example.\n\n14.2.5 Remove tension between interactive and automated testing\nYour test code will be executed in two different settings:\n\nInteractive test development and maintenance, which includes tasks like:\n\nInitial test creation\nModifying tests to adapt to change\nDebugging test failure\n\n\nAutomated test runs, which is accomplished with functions such as:\n\nSingle file: devtools::test_active_file(), testthat::test_file()\n\nWhole package: devtools::test(), devtools::check()\n\n\n\n\nAutomated testing of your whole package is what takes priority. This is ultimately the whole point of your tests. However, the interactive experience is clearly important for the humans doing this work. Therefore it’s important to find a pleasant workflow, but also to ensure that you don’t rig anything for interactive convenience that actually compromises the health of the test suite.\nThese two modes of test-running should not be in conflict with each other. If you perceive tension between these two modes, this can indicate that you’re not taking full advantage of some of testthat’s features and the way it’s designed to work with devtools::load_all().\nWhen working on your tests, use load_all(), just like you do when working below R/. By default, load_all() does all of these things:\n\nSimulates re-building, re-installing, and re-loading your package.\nMakes everything in your package’s namespace available, including unexported functions and objects and anything you’ve imported from another package.\nAttaches testthat, i.e. does library(testthat).\nRuns test helper files, i.e. executes test/testthat/helper.R (more on that below).\n\nThis eliminates the need for any library() calls below tests/testthat/, for the vast majority of R packages. Any instance of library(testthat) is clearly no longer necessary. Likewise, any instance of attaching one of your dependencies via library(somePkg) is unnecessary. In your tests, if you need to call functions from somePkg, do it just as you do below R/. If you have imported the function into your namespace, use fun(). If you have not, use somePkg::fun(). It’s fair to say that library(somePkg) in the tests should be about as rare as taking a dependency via Depends, i.e. there is almost always a better alternative.\nUnnecessary calls to library(somePkg) in test files have a real downside, because they actually change the R landscape. library() alters the search path. This means the circumstances under which you are testing may not necessarily reflect the circumstances under which your package will be used. This makes it easier to create subtle test bugs, which you will have to unravel in the future.\nOne other function that should almost never appear below tests/testhat/ is source(). There are several special files with an official role in testthat workflows (see below), not to mention the entire R package machinery, that provide better ways to make functions, objects, and other logic available in your tests.",
"crumbs": [
"Testing",
"14Designing your test suite"
@@ -1666,7 +1666,7 @@
"href": "website.html",
"title": "19 Website",
"section": "",
- "text": "19.1 Initiate a site\nAt this point, we’ve discussed many ways to document your package:\nWouldn’t it be divine if all of that somehow got bundled up together into a beautiful website for your package? The pkgdown package is meant to provide exactly this magic and that is the topic of this chapter.\nAssuming your package has a valid structure, pkgdown should be able to make a website for it. Obviously that website will be more substantial if your package has more of the documentation elements listed above. But something reasonable should happen for any valid R package.\nusethis::use_pkgdown() is a function you run once and it does the initial, minimal setup necessary to start using pkgdown:\nusethis::use_pkgdown()\n#> ✔ Setting active project to \"/tmp/RtmpvHcZ5w/mypackage\".\n#> ✔ Adding \"^_pkgdown\\\\.yml$\", \"^docs$\", and \"^pkgdown$\" to\n#> '.Rbuildignore'.\n#> ✔ Adding \"docs\" to '.gitignore'.\n#> ✔ Writing '_pkgdown.yml'.\n#> ☐ Edit '_pkgdown.yml'.\n#> ✔ Setting active project to \"<no active project>\".\nHere’s what use_pkgdown() does:\npkgdown::build_site() is a function you’ll call repeatedly, to re-render your site locally. In an extremely barebones package, you’ll see something like this:\npkgdown::build_site()\n#> ✔ Setting active project to \"/tmp/RtmpvHcZ5w/mypackage\".\n#> ── Installing package mypackage into temporary library ─────────────\n#> ── Initialising site ───────────────────────────────────────────────────────────\n#> Copying <pkgdown>/BS5/assets/katex-auto.js to katex-auto.js\n#> Copying <pkgdown>/BS5/assets/lightswitch.js to lightswitch.js\n#> Copying <pkgdown>/BS5/assets/link.svg to link.svg\n#> Copying <pkgdown>/BS5/assets/pkgdown.js to pkgdown.js\n#> Updating deps/bootstrap-5.3.1/bootstrap.bundle.min.js\n#> Updating deps/bootstrap-5.3.1/bootstrap.bundle.min.js.map\n#> Updating deps/bootstrap-5.3.1/bootstrap.min.css\n#> Updating deps/bootstrap-toc-1.0.1/bootstrap-toc.min.js\n#> Updating deps/clipboard.js-2.0.11/clipboard.min.js\n#> Updating deps/font-awesome-6.5.2/css/all.css\n#> Updating deps/font-awesome-6.5.2/css/all.min.css\n#> Updating deps/font-awesome-6.5.2/css/v4-shims.css\n#> Updating deps/font-awesome-6.5.2/css/v4-shims.min.css\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-brands-400.ttf\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-brands-400.woff2\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-regular-400.ttf\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-regular-400.woff2\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-solid-900.ttf\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-solid-900.woff2\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-v4compatibility.ttf\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-v4compatibility.woff2\n#> Updating deps/headroom-0.11.0/headroom.min.js\n#> Updating deps/headroom-0.11.0/jQuery.headroom.min.js\n#> Updating deps/jquery-3.6.0/jquery-3.6.0.js\n#> Updating deps/jquery-3.6.0/jquery-3.6.0.min.js\n#> Updating deps/jquery-3.6.0/jquery-3.6.0.min.map\n#> Updating deps/search-1.0.0/autocomplete.jquery.min.js\n#> Updating deps/search-1.0.0/fuse.min.js\n#> Updating deps/search-1.0.0/mark.min.js\n#> ── Building pkgdown site for package mypackage ─────────────────────────────────\n#> Reading from: /tmp/RtmpvHcZ5w/mypackage\n#> Writing to: /tmp/RtmpvHcZ5w/mypackage/docs\n#> ── Sitrep ──────────────────────────────────────────────────────────────────────\n#> ✖ URLs not ok.\n#> In _pkgdown.yml, url is missing.\n#> See details in `vignette(pkgdown::metadata)`.\n#> ✔ Favicons ok.\n#> ✔ Open graph metadata ok.\n#> ✔ Articles metadata ok.\n#> ✔ Reference metadata ok.\n#> ── Initialising site ───────────────────────────────────────────────────────────\n#> ── Building home ───────────────────────────────────────────────────────────────\n#> Writing `authors.html`\n#> Reading 'DESCRIPTION'\n#> Writing `index.html`\n#> Writing `404.html`\n#> ── Building function reference ─────────────────────────────────────────────────\n#> Writing `reference/index.html`\n#> ── Building sitemap ────────────────────────────────────────────────────────────\n#> Writing `sitemap.xml`\n#> ── Building search index ───────────────────────────────────────────────────────\n#> ── Checking for problems ───────────────────────────────────────────────────────\n#> ── Finished building pkgdown site for package mypackage ────────────────────────\n#> ── Finished building pkgdown site for package mypackage ────────────\n#> ✔ Setting active project to \"<no active project>\".\nIn an interactive session your newly rendered site should appear in your default web browser.\nYou can look in the local docs/ directory to see the files that constitute your package’s website. To manually browse the site, open docs/index.html in your preferred browser.\nThis is almost all you truly need to know about pkgdown. It’s certainly a great start and, as your package and ambitions grow, the best place to learn more is the pkgdown-made website for the pkgdown package itself: https://pkgdown.r-lib.org.",
+ "text": "19.1 Initiate a site\nAt this point, we’ve discussed many ways to document your package:\nWouldn’t it be divine if all of that somehow got bundled up together into a beautiful website for your package? The pkgdown package is meant to provide exactly this magic and that is the topic of this chapter.\nAssuming your package has a valid structure, pkgdown should be able to make a website for it. Obviously that website will be more substantial if your package has more of the documentation elements listed above. But something reasonable should happen for any valid R package.\nusethis::use_pkgdown() is a function you run once and it does the initial, minimal setup necessary to start using pkgdown:\nusethis::use_pkgdown()\n#> ✔ Setting active project to \"/tmp/RtmpUqIKmX/mypackage\".\n#> ✔ Adding \"^_pkgdown\\\\.yml$\", \"^docs$\", and \"^pkgdown$\" to\n#> '.Rbuildignore'.\n#> ✔ Adding \"docs\" to '.gitignore'.\n#> ✔ Writing '_pkgdown.yml'.\n#> ☐ Edit '_pkgdown.yml'.\n#> ✔ Setting active project to \"<no active project>\".\nHere’s what use_pkgdown() does:\npkgdown::build_site() is a function you’ll call repeatedly, to re-render your site locally. In an extremely barebones package, you’ll see something like this:\npkgdown::build_site()\n#> ✔ Setting active project to \"/tmp/RtmpUqIKmX/mypackage\".\n#> ── Installing package mypackage into temporary library ─────────────\n#> ── Initialising site ───────────────────────────────────────────────────────────\n#> Copying <pkgdown>/BS5/assets/katex-auto.js to katex-auto.js\n#> Copying <pkgdown>/BS5/assets/lightswitch.js to lightswitch.js\n#> Copying <pkgdown>/BS5/assets/link.svg to link.svg\n#> Copying <pkgdown>/BS5/assets/pkgdown.js to pkgdown.js\n#> Updating deps/bootstrap-5.3.1/bootstrap.bundle.min.js\n#> Updating deps/bootstrap-5.3.1/bootstrap.bundle.min.js.map\n#> Updating deps/bootstrap-5.3.1/bootstrap.min.css\n#> Updating deps/bootstrap-toc-1.0.1/bootstrap-toc.min.js\n#> Updating deps/clipboard.js-2.0.11/clipboard.min.js\n#> Updating deps/font-awesome-6.5.2/css/all.css\n#> Updating deps/font-awesome-6.5.2/css/all.min.css\n#> Updating deps/font-awesome-6.5.2/css/v4-shims.css\n#> Updating deps/font-awesome-6.5.2/css/v4-shims.min.css\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-brands-400.ttf\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-brands-400.woff2\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-regular-400.ttf\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-regular-400.woff2\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-solid-900.ttf\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-solid-900.woff2\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-v4compatibility.ttf\n#> Updating deps/font-awesome-6.5.2/webfonts/fa-v4compatibility.woff2\n#> Updating deps/headroom-0.11.0/headroom.min.js\n#> Updating deps/headroom-0.11.0/jQuery.headroom.min.js\n#> Updating deps/jquery-3.6.0/jquery-3.6.0.js\n#> Updating deps/jquery-3.6.0/jquery-3.6.0.min.js\n#> Updating deps/jquery-3.6.0/jquery-3.6.0.min.map\n#> Updating deps/search-1.0.0/autocomplete.jquery.min.js\n#> Updating deps/search-1.0.0/fuse.min.js\n#> Updating deps/search-1.0.0/mark.min.js\n#> ── Building pkgdown site for package mypackage ─────────────────────────────────\n#> Reading from: /tmp/RtmpUqIKmX/mypackage\n#> Writing to: /tmp/RtmpUqIKmX/mypackage/docs\n#> ── Sitrep ──────────────────────────────────────────────────────────────────────\n#> ✖ URLs not ok.\n#> In _pkgdown.yml, url is missing.\n#> See details in `vignette(pkgdown::metadata)`.\n#> ✔ Favicons ok.\n#> ✔ Open graph metadata ok.\n#> ✔ Articles metadata ok.\n#> ✔ Reference metadata ok.\n#> ── Initialising site ───────────────────────────────────────────────────────────\n#> ── Building home ───────────────────────────────────────────────────────────────\n#> Writing `authors.html`\n#> Reading 'DESCRIPTION'\n#> Writing `index.html`\n#> Writing `404.html`\n#> ── Building function reference ─────────────────────────────────────────────────\n#> Writing `reference/index.html`\n#> ── Building sitemap ────────────────────────────────────────────────────────────\n#> Writing `sitemap.xml`\n#> ── Building search index ───────────────────────────────────────────────────────\n#> ── Checking for problems ───────────────────────────────────────────────────────\n#> ── Finished building pkgdown site for package mypackage ────────────────────────\n#> ── Finished building pkgdown site for package mypackage ────────────\n#> ✔ Setting active project to \"<no active project>\".\nIn an interactive session your newly rendered site should appear in your default web browser.\nYou can look in the local docs/ directory to see the files that constitute your package’s website. To manually browse the site, open docs/index.html in your preferred browser.\nThis is almost all you truly need to know about pkgdown. It’s certainly a great start and, as your package and ambitions grow, the best place to learn more is the pkgdown-made website for the pkgdown package itself: https://pkgdown.r-lib.org.",
"crumbs": [
"Documentation",
"19Website"
diff --git a/sitemap.xml b/sitemap.xml
index a99bdd82a..8bf643dda 100644
--- a/sitemap.xml
+++ b/sitemap.xml
@@ -2,218 +2,218 @@
https://r-pkgs.org/book-asciidoc/R-Packages--2e-.adoc
- 2025-01-08T07:12:52.799Z
+ 2025-01-09T07:13:15.959Zhttps://r-pkgs.org/index.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.978Zhttps://r-pkgs.org/book-asciidoc/preface.adoc
- 2025-01-08T07:12:59.351Z
+ 2025-01-09T07:13:22.803Zhttps://r-pkgs.org/preface.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/introduction.adoc
- 2025-01-08T07:13:05.159Z
+ 2025-01-09T07:13:28.659Zhttps://r-pkgs.org/introduction.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/whole-game.adoc
- 2025-01-08T07:14:31.044Z
+ 2025-01-09T07:14:53.784Zhttps://r-pkgs.org/whole-game.html
- 2025-01-08T07:11:23.827Z
+ 2025-01-09T07:11:57.986Zhttps://r-pkgs.org/book-asciidoc/setup.adoc
- 2025-01-08T07:14:39.500Z
+ 2025-01-09T07:15:06.300Zhttps://r-pkgs.org/setup.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/structure.adoc
- 2025-01-08T07:14:48.564Z
+ 2025-01-09T07:15:14.968Zhttps://r-pkgs.org/structure.html
- 2025-01-08T07:11:23.827Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/workflow101.adoc
- 2025-01-08T07:14:57.620Z
+ 2025-01-09T07:15:23.436Zhttps://r-pkgs.org/workflow101.html
- 2025-01-08T07:11:23.827Z
+ 2025-01-09T07:11:57.986Zhttps://r-pkgs.org/book-asciidoc/package-within.adoc
- 2025-01-08T07:15:08.400Z
+ 2025-01-09T07:15:34.152Zhttps://r-pkgs.org/package-within.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/code.adoc
- 2025-01-08T07:15:20.836Z
+ 2025-01-09T07:15:46.084Zhttps://r-pkgs.org/code.html
- 2025-01-08T07:11:23.747Z
+ 2025-01-09T07:11:57.902Zhttps://r-pkgs.org/book-asciidoc/data.adoc
- 2025-01-08T07:15:32.008Z
+ 2025-01-09T07:15:56.852Zhttps://r-pkgs.org/data.html
- 2025-01-08T07:11:23.747Z
+ 2025-01-09T07:11:57.902Zhttps://r-pkgs.org/book-asciidoc/misc.adoc
- 2025-01-08T07:15:40.344Z
+ 2025-01-09T07:16:04.396Zhttps://r-pkgs.org/misc.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/description.adoc
- 2025-01-08T07:15:47.536Z
+ 2025-01-09T07:16:11.528Zhttps://r-pkgs.org/description.html
- 2025-01-08T07:11:23.747Z
+ 2025-01-09T07:11:57.902Zhttps://r-pkgs.org/book-asciidoc/dependencies-mindset-background.adoc
- 2025-01-08T07:15:59.780Z
+ 2025-01-09T07:16:23.149Zhttps://r-pkgs.org/dependencies-mindset-background.html
- 2025-01-08T07:11:23.747Z
+ 2025-01-09T07:11:57.902Zhttps://r-pkgs.org/book-asciidoc/dependencies-in-practice.adoc
- 2025-01-08T07:16:08.068Z
+ 2025-01-09T07:16:31.273Zhttps://r-pkgs.org/dependencies-in-practice.html
- 2025-01-08T07:11:23.747Z
+ 2025-01-09T07:11:57.902Zhttps://r-pkgs.org/book-asciidoc/license.adoc
- 2025-01-08T07:16:17.508Z
+ 2025-01-09T07:16:39.677Zhttps://r-pkgs.org/license.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/testing-basics.adoc
- 2025-01-08T07:16:23.708Z
+ 2025-01-09T07:16:46.177Zhttps://r-pkgs.org/testing-basics.html
- 2025-01-08T07:11:23.827Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/testing-design.adoc
- 2025-01-08T07:16:32.752Z
+ 2025-01-09T07:16:54.933Zhttps://r-pkgs.org/testing-design.html
- 2025-01-08T07:11:23.827Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/testing-advanced.adoc
- 2025-01-08T07:16:41.264Z
+ 2025-01-09T07:17:02.961Zhttps://r-pkgs.org/testing-advanced.html
- 2025-01-08T07:11:23.827Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/man.adoc
- 2025-01-08T07:16:48.732Z
+ 2025-01-09T07:17:10.593Zhttps://r-pkgs.org/man.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/vignettes.adoc
- 2025-01-08T07:16:58.125Z
+ 2025-01-09T07:17:19.237Zhttps://r-pkgs.org/vignettes.html
- 2025-01-08T07:11:23.827Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/other-markdown.adoc
- 2025-01-08T07:17:06.221Z
+ 2025-01-09T07:17:26.885Zhttps://r-pkgs.org/other-markdown.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/website.adoc
- 2025-01-08T07:17:20.397Z
+ 2025-01-09T07:17:40.929Zhttps://r-pkgs.org/website.html
- 2025-01-08T07:11:23.827Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/software-development-practices.adoc
- 2025-01-08T07:17:26.877Z
+ 2025-01-09T07:17:47.321Zhttps://r-pkgs.org/software-development-practices.html
- 2025-01-08T07:11:23.827Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/lifecycle.adoc
- 2025-01-08T07:17:35.913Z
+ 2025-01-09T07:17:56.661Zhttps://r-pkgs.org/lifecycle.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/release.adoc
- 2025-01-08T07:17:43.077Z
+ 2025-01-09T07:18:04.065Zhttps://r-pkgs.org/release.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/references.adoc
- 2025-01-08T07:17:45.257Z
+ 2025-01-09T07:18:06.277Zhttps://r-pkgs.org/references.html
- 2025-01-08T07:11:23.823Z
+ 2025-01-09T07:11:57.982Zhttps://r-pkgs.org/book-asciidoc/R-CMD-check.adoc
- 2025-01-08T07:17:49.549Z
+ 2025-01-09T07:18:10.674Zhttps://r-pkgs.org/R-CMD-check.html
- 2025-01-08T07:11:23.743Z
+ 2025-01-09T07:11:57.902Z
diff --git a/structure.html b/structure.html
index c3f00803f..40d1d029f 100644
--- a/structure.html
+++ b/structure.html
@@ -462,9 +462,9 @@
Figure 3.1 shows the files present in the source, bundled, and binary forms of a fictional package named zzzpackage. We’ve deliberately crafted this example to include most of the package parts covered in this book. Not every package will include every file seen here, nor does this diagram include every possible file that might appear in a package.
expect_equal(Sys.getenv("envvar_whatever"), "whatever")})
-#> Test passed 🎊
+#> Test passed 🎉grep("jsonlite", search(), value =TRUE)#> character(0)
@@ -698,13 +698,13 @@
useful_thing<-3
expect_equal(2*useful_thing, 6)})
-#> Test passed 🌈
+#> Test passed 😀test_that("subtraction works", {useful_thing<-3expect_equal(5-useful_thing, 2)})
-#> Test passed 🥇
+#> Test passed 🥳
In real life, useful_thing is usually a more complicated object that somehow feels burdensome to instantiate. Notice how useful_thing <- 3 appears in more than one place. Conventional wisdom says we should DRY this code out. It’s tempting to just move useful_thing’s definition outside of the tests:
@@ -713,12 +713,12 @@
test_that("multiplication works", {expect_equal(2*useful_thing, 6)})
-#> Test passed 🥳
+#> Test passed 🌈test_that("subtraction works", {expect_equal(5-useful_thing, 2)})
-#> Test passed 🎉
+#> Test passed 😀
But we really do think the first form, with the repetition, is often the better choice.
At this point, many readers might be thinking “but the code I might have to repeat is much longer than 1 line!”. Below we describe the use of test fixtures. This can often reduce complicated situations back to something that resembles this simple example.
#> ✔ Setting active project to "/tmp/RtmpvHcZ5w/mypackage".
+
#> ✔ Setting active project to "/tmp/RtmpUqIKmX/mypackage".
#> ── Installing package mypackage into temporary library ─────────────
#> ── Initialising site ───────────────────────────────────────────────────────────
#> Copying <pkgdown>/BS5/assets/katex-auto.js to katex-auto.js
@@ -503,8 +503,8 @@
If you think you might print stickers, make sure to comply with the de facto standard for sticker size. hexb.in is a reliable source for the dimensions and also provides a list of potential vendors for printed stickers.