Chapter 2 The Whole Game

Spoiler alert!

This chapter runs through the development of a small toy package. It’s meant to paint the Big Picture and suggest a workflow, before we descend into the detailed treatment of the key components of an R package.

To keep the pace brisk, we exploit the modern conveniences in the devtools package and the RStudio IDE. In later chapters, we are more explicit about what those helpers are doing for us.

Work in progress! Adapting from material originally developed as part of STAT 545 and as a possible vignette for devtools.

2.1 Load devtools and friends

Load the devtools package, which is the public face of a set of packages that support various aspects of package development.

library(devtools)

For presentation purposes only, we use fs, for filesystem work, and the tidyverse, for light data wrangling.

library(tidyverse)
#> Registered S3 methods overwritten by 'ggplot2':
#>   method         from 
#>   [.quosures     rlang
#>   c.quosures     rlang
#>   print.quosures rlang
#> Registered S3 method overwritten by 'rvest':
#>   method            from
#>   read_xml.response xml2
library(fs)

2.2 Toy package: foofactors

We use various functions from devtools to build a small toy package from scratch, with features commonly seen in released packages:

  • Functions to address a specific need, such as helpers to work with factors.
  • Access to established workflows for installation, getting help, and checking basic quality.
  • Version control and an open development process.
    • This is completely optional in your work, but recommended. You’ll see how Git and GitHub helps us expose all the intermediate stages of our package.
  • Documentation for individual functions via roxygen2.
  • Unit testing with testthat.
  • Documentation for the package as a whole via an executable README.Rmd.

We call the package foofactors and it will have a couple functions for handling factors. Please note that these functions are super simple and definitely not the point! For a proper package for factor handling, please see forcats.

The foofactors package itself is not our goal here. It is a device for demonstrating a typical workflow for package development with devtools.

2.3 Peek at the finished product

The foofactors package is tracked during its development with the Git version control system. This is purely optional and you can certainly follow along without implementing this. A nice side benefit is that we eventually connect it to a remote repository on GitHub, which means you can see the glorious result we are working towards by visiting foofactors on GitHub: https://github.com/jennybc/foofactors. By inspecting the commit history and especially the diffs, you can see exactly what changes at each step of the process laid out below.

FIXME: I think these diffs are extremely useful and would like to surface them better here.

2.4 create_package()

Call create_package() to initialize a new package in a directory on your computer (and create the directory, if necessary).

Make 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 are creating here into an installed package is part of what devtools facilitates. Don’t try to do devtools’ job for it!

Substitute your chosen path into a create_package() call like this:

create_package("~/path/to/foofactors")

We have to work in a temp directory, because this 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.

#> Warning: `recursive` is deprecated, please use `recurse` instead
#> ✔ Creating '/tmp/RtmppsYpXJ/foofactors/'
#> ✔ Setting active project to '/tmp/RtmppsYpXJ/foofactors'
#> Warning: `recursive` is deprecated, please use `recurse` instead
#> ✔ Creating 'R/'
#> ✔ Writing 'DESCRIPTION'
#> Package: foofactors
#> Title: What the Package Does (One Line, Title Case)
#> Version: 0.0.0.9000
#> Authors@R (parsed):
#>     * First Last <first.last@example.com> [aut, cre] (<https://orcid.org/YOUR-ORCID-ID>)
#> Description: What the package does (one paragraph).
#> License: What license it uses
#> Encoding: UTF-8
#> LazyData: true
#> ✔ Writing 'NAMESPACE'
#> ✔ Writing 'foofactors.Rproj'
#> ✔ Adding '.Rproj.user' to '.gitignore'
#> ✔ Adding '^foofactors\\.Rproj$', '^\\.Rproj\\.user$' to '.Rbuildignore'
#> ✔ Setting active project to '<no active project>'
#> ✔ Setting active project to '/tmp/RtmppsYpXJ/foofactors'

If you’re working in RStudio, you should find yourself in a new instance of RStudio, opened into your new foofactors package (and Project). If you somehow need to do this manually, navigate to the directory and double click on foofactors.Rproj. RStudio has special handling for packages and you should now see a Build tab in the same pane as Environment and History.

FIXME: good place for a screenshot

What’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):

#> # A tibble: 6 x 2
#>   path             type     
#>   <fs::path>       <fct>    
#> 1 .Rbuildignore    file     
#> 2 .gitignore       file     
#> 3 DESCRIPTION      file     
#> 4 NAMESPACE        file     
#> 5 R                directory
#> 6 foofactors.Rproj file

In the file browser, go to More > 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.

  • .Rbuildignore lists files that we need to have around but that should not be included when building the R package from source.
  • .Rproj.user, if you have it, is a directory used internally by RStudio.
  • .gitignore anticipates Git usage and ignores some standard, behind-the-scenes files created by R and RStudio. Even if you do not plan to use Git, this is harmless.
  • DESCRIPTION provides metadata about your package. We edit this shortly.
  • NAMESPACE declares the functions your package exports for external use and the external functions your package imports from other packages. At the moment, it holds temporary-yet-functional placeholder content.
  • The R/ directory is the “business end” of your package. It will soon contain .R files with function definitions.
  • foofactors.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).

2.5 use_git()

The use of Git or another version control system is optional, but a recommended practice in the long-term. We explain its importance in FUTURE LINK (FIXME: link to right place when exists).

The foofactors directory is an R source package and an RStudio Project. Now we make it also a Git repository, with devtools::use_git().

use_git()  
#> ✔ Initialising Git repo
#> ✔ Adding '.Rhistory', '.RData' to '.gitignore'

In an interactive session, you will be asked if you want to commit some files here and you should probably accept the offer. Behind the scenes, we’ll cause the same to happen for us.

#> [bdb1029] 2019-05-09: Initial commit

What’s new? 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.

#> # A tibble: 1 x 2
#>   path       type     
#>   <fs::path> <fct>    
#> 1 .git       directory

If you’re using RStudio, it probably requested permission to relaunch itself in this Project. You can do so manually by quitting and relaunching by double clicking on foofactors.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.

FIXME: good place for a screenshot

Click on History (the clock icon) and, if you consented, you will see an initial commit made via use_git():

#> [bdb1029] 2019-05-09: Initial commit

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

2.6 Write the first function

It is not too hard to find a puzzling operation involving factors. Let’s see what happens when we catenate two factors.

(a <- factor(c("character", "hits", "your", "eyeballs")))
#> [1] character hits      your      eyeballs 
#> Levels: character eyeballs hits your
(b <- factor(c("but", "integer", "where it", "counts")))
#> [1] but      integer  where it counts  
#> Levels: but counts integer where it
c(a, b)
#> [1] 1 3 4 2 1 3 4 2

Huh? Many people do not expect the result of catenating two factors to be an integer vector consisting of the numbers 1, 2, 3, and 4. What if we coerce each factor to character, catenate, then re-convert to factor?

factor(c(as.character(a), as.character(b)))
#> [1] character hits      your      eyeballs  but       integer   where it 
#> [8] counts   
#> Levels: but character counts eyeballs hits integer where it your

That seems to produce a result that makes more sense. Let’s drop that logic into the body of a function called fbind():

fbind <- function(a, b) {
  factor(c(as.character(a), as.character(b)))
}

This book does not teach you how to write functions in R. To learn more about that take a look at the Functions chapter of R for Data Science and the Functions chapter of Advanced R.

2.7 use_r()

Where shall we define fbind()? Save it in a .R file, in the R/ subdirectory of your package. A reasonable starting position is to make a new .R file for each function in your package and name the file after the function. As you add more functions, you’ll want to relax this and begin to group related functions together. We’ll save the definition of fbind() in the file R/fbind.R.

The helper use_r() creates and/or opens a script below R/. It really shines in a more mature package, when navigating between .R files and the associated tests. But, even here, it’s useful to keep yourself from getting too carried away while working in Untitled4.

use_r("fbind")
#> ● Edit 'R/fbind.R'

Put the definition of fbind() and only the definition of fbind() in R/fbind.R and save it. The file R/fbind.R should NOT contain any of the other top-level code we have recently executed, such as the definitions of factors a and b, library(devtools) or use_git(). This foreshadows an adjustment you’ll need to make as you transition from writing R scripts to R packages. Packages and scripts use different mechanisms to declare their dependency on other packages and to store example or test code. We explore this further in chapter 4.

2.8 load_all()

How do we test drive fbind()? If this were a regular R script, we might use RStudio to send the function definition to the R Console and define fbind() in the global workspace. Or maybe we’d call source("R/fbind.R"). For package development, however, devtools offers a more robust approach.

Call devtools::load_all() to make fbind() available for experimentation.

load_all()
#> Loading foofactors

Now call fbind(a, b) to see how it works.

fbind(a, b)
#> [1] character hits      your      eyeballs  but       integer   where it 
#> [8] counts   
#> Levels: but character counts eyeballs hits integer where it your

Note that load_all() has made the fbind() function available, although it does not exist in the global workspace.

exists("fbind", where = ".GlobalEnv", inherits = FALSE)
#> [1] FALSE

load_all() simulates the process of building, installing, and attaching the foofactors package. As your package accumulates more functions, some exported, some not, some of which call each other, some of which call functions from packages you depend on, load_all() gives you a much more accurate sense of how the package is developing than test driving functions defined in the global workspace. Also load_all() allows much faster iteration than actually building, installing, and attaching the package.

Review so far:

  • We wrote our first function, fbind(), to catenate two factors.
  • We used load_all() to quickly make this function available for interactive use, as if we’d built and installed foofactors and attached it via library(foofactors).

RStudio exposes load_all() in the Build menu, in the Build pane via More > Load All, and in keyboard shortcuts Ctrl + Shift + L (Windows & Linux) or Cmd + Shift + L (macOS).

2.8.1 Commit fbind()

If you’re using Git, use your preferred method to commit the new R/fbind.R file. We do so behind the scenes here and here’s the associated diff.

#> [7190f2d] 2019-05-09: Add fbind()
#> diff --git a/R/fbind.R b/R/fbind.R
#> new file mode 100644
#> index 0000000..7b03d75
#> --- /dev/null
#> +++ b/R/fbind.R
#> @@ -0,0 +1,3 @@
#> +fbind <- function(a, b) {
#> +  factor(c(as.character(a), as.character(b)))
#> +}

From this point on, we commit after each step. Remember these commits are available in the public repository.

2.9 check()

We have empirical evidence that fbind() works. But how can we be sure that all the moving parts of the foofactors 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.

R CMD check, executed in the shell, is the gold standard for checking that an R package is in full working order. devtools::check() is a convenient way to run this without leaving your R session.

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

check()
#> ── R CMD check results ───────────────────────── foofactors 0.0.0.9000 ────
#> Duration: 6.7s
#> 
#> ❯ checking DESCRIPTION meta-information ... WARNING
#>   Non-standard license specification:
#>     What license it uses
#>   Standardizable: FALSE
#> 
#> ❯ checking for missing documentation entries ... WARNING
#>   Undocumented code objects:
#>     ‘fbind’
#>   All user-level objects in a package should have documentation entries.
#>   See chapter ‘Writing R documentation files’ in the ‘Writing R
#>   Extensions’ manual.
#> 
#> 0 errors ✔ | 2 warnings ✖ | 0 notes ✔

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.

At this point, we expect 2 warnings (and 0 errors, 0 notes):

  • Non-standard license specification
  • Undocumented code objects: 'fbind'

We’ll address both soon.

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

2.10 Edit DESCRIPTION

Before we tackle the warnings about the license and documentation, let’s work on the boilerplate content in DESCRIPTION. The DESCRIPTION file provides metadata about your package and is covered fully in chapter 5.

Make these edits:

  • Make yourself the author.
  • Write some descriptive text in the Title and Description fields.

Use Ctrl + . in RStudio and start typing “DESCRIPTION” to activate a helper that makes it easy to open a file for editing. In addition to a filename, your hint can be a function name. This is very handy once a package has lots of functions and files below R/.

When you’re done, DESCRIPTION should look similar to this:

Package: foofactors
Title: Make Factors Less Aggravating
Version: 0.0.0.9000
Authors@R:
    person("Jane", "Doe", email = "jane@example.com", role = c("aut", "cre"))
Description: Factors have driven people to extreme measures, like ordering
    custom conference ribbons and laptop stickers to express how HELLNO we
    feel about stringsAsFactors. And yet, sometimes you need them. Can they
    be made less maddening? Let's find out.
License: What license it uses
Encoding: UTF-8
LazyData: true
[1d8df06] 2019-05-09: Edit DESCRIPTION

2.11 use_mit_license()

Pick a License, Any License. – Jeff Atwood

For foofactors, we use the MIT license. This requires specification in DESCRIPTION and an additional file called LICENSE, naming the copyright holder and year. We’ll use the helper devtools::use_mit_license(). Substitute your name here.

use_mit_license("Jane Doe")
#> ✔ Setting License field in DESCRIPTION to 'MIT + file LICENSE'
#> ✔ Writing 'LICENSE.md'
#> ✔ Adding '^LICENSE\\.md$' to '.Rbuildignore'
#> ✔ Writing 'LICENSE'

Open the newly created LICENSE file and confirm it has the current year and your name.

YEAR: 2019
COPYRIGHT HOLDER: Jane Doe

Like other license helpers, use_mit_license() also puts a copy of the full license in LICENSE.md and adds this file to .Rbuildignore. It’s considered a best practice to include a full license in your package’s source, such as on GitHub, but CRAN disallows the inclusion of this file in a package tarball.

[c694483] 2019-05-09: Add LICENSE

2.12 document()

Wouldn’t it be nice to get help on fbind(), just like we do with other R functions? This requires that your package have a special R documentation file, man/fbind.Rd, written in an R-specific markup language that is sort of like LaTeX. Luckily we don’t necessarily have to author that directly.

We write a specially formatted comment right above fbind(), in its source file, and then let a package called roxygen2 handle the creation of man/fbind.Rd. The motivation and mechanics of roxygen2 are covered in chapter 6.

If you use RStudio, open R/fbind.R in the source editor and put the cursor somewhere in the fbind() function definition. Now do Code > Insert roxygen skeleton. A very special comment should appear above your function, in which each line begins with #'. RStudio only inserts a barebones template, so you will need to edit it to look something like that below.

If you don’t use RStudio, create the comment yourself. Regardless, you should modify it to look something like this:

#' Bind two factors
#'
#' Create a new factor from two existing factors, where the new factor's levels
#' are the union of the levels of the input factors.
#'
#' @param a factor
#' @param b factor
#'
#' @return factor
#' @export
#' @examples
#' fbind(iris$Species[c(1, 51, 101)], PlantGrowth$group[c(1, 11, 21)])

FIXME: mention how RStudio helps you execute examples here?

[3faf863] 2019-05-09: Add roxygen header to document fbind()

But we’re not done yet! We still need to trigger the conversion of this new roxygen comment into man/fbind.Rd with devtools::document():

document()
#> Updating foofactors documentation
#> Updating roxygen version in /tmp/RtmppsYpXJ/foofactors/DESCRIPTION
#> Writing NAMESPACE
#> Loading foofactors
#> Writing NAMESPACE
#> Writing fbind.Rd

RStudio exposes document() in the Build menu, in the Build pane via More > Document, and in keyboard shortcuts Ctrl + Shift + D (Windows & Linux) or Cmd + Shift + D (macOS).

You should now be able to preview your help file like so:

?fbind

You’ll see a message like “Rendering development documentation for ‘fbind’”, which reminds that you are basically previewing draft documentation. That is, this documentation is present in your package’s source, but is not yet present in an installed package. In fact, we haven’t installed foofactors yet, but we will soon.

Note also that your package’s documentation won’t be properly wired up until it has been formally built and installed. This polishes off niceties like the links between help files and the creation of a package index.

2.12.1 NAMESPACE changes

In addition to converting fbind()’s special comment into man/fbind.Rd, the call to devtools::document() updates the NAMESPACE file, based on @export directives found in roxygen comments. Open NAMESPACE for inspection. The contents should be:

# Generated by roxygen2: do not edit by hand

export(fbind)

It no longer has the placeholder content that says “export everything”. Instead, there is now an explicit directive to export the fbind() function.

The export directive in NAMESPACE is what makes fbind() available to a user after attaching foofactors via library(foofactors). Just as it is entirely possible to author .Rd files “by hand”, you can manage NAMESPACE explicitly yourself. But we choose to delegate this to devtools (and roxygen2).

[0dbeb9d] 2019-05-09: Run document()

2.13 check() again

foofactors should pass R CMD check cleanly now and forever more: 0 errors, 0 warnings, 0 notes.

check()
#> ── R CMD check results ───────────────────────── foofactors 0.0.0.9000 ────
#> Duration: 8.2s
#> 
#> 0 errors ✔ | 0 warnings ✔ | 0 notes ✔

2.14 install()

Since we have a minimum viable product now, let’s install the foofactors package into your library via devtools::install():

install()
   checking for file ‘/tmp/RtmppsYpXJ/foofactors/DESCRIPTION’ ...
✔  checking for file ‘/tmp/RtmppsYpXJ/foofactors/DESCRIPTION’
─  preparing ‘foofactors’:
   checking DESCRIPTION meta-information ...
✔  checking DESCRIPTION meta-information
─  checking for LF line-endings in source and make files and shell scripts
─  checking for empty or unneeded directories
─  building ‘foofactors_0.0.0.9000.tar.gz’
Running /home/travis/R-bin/lib/R/bin/R CMD INSTALL \
  /tmp/RtmppsYpXJ/foofactors_0.0.0.9000.tar.gz --install-tests 
* installing to library ‘/home/travis/R/Library’
* installing *source* package ‘foofactors’ ...
** using staged installation
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (foofactors)
Reloading attached foofactors

RStudio exposes similar functionality in the Build menu and in the Build pane via Install and Restart.

Now we can attach and use foofactors like any other package. Let’s revisit our small example from the top. This is a good time to restart your R session and ensure you have a clean workspace.

library(foofactors)

a <- factor(c("character", "hits", "your", "eyeballs"))
b <- factor(c("but", "integer", "where it", "counts"))

fbind(a, b)
#> [1] character hits      your      eyeballs  but       integer   where it 
#> [8] counts   
#> Levels: but character counts eyeballs hits integer where it your

Success!

2.15 use_testthat()

We’ve tested fbind() informally, in a single example. We can formalize and expand this with some unit tests. This means we express a few concrete expectations about the correct fbind() result for various inputs.

First, we declare our intent to write unit tests and to use the testthat package for this, via devtools::use_testthat():

use_testthat()
#> ✔ Adding 'testthat' to Suggests field in DESCRIPTION
#> Warning: `recursive` is deprecated, please use `recurse` instead
#> ✔ Creating 'tests/testthat/'
#> ✔ Writing 'tests/testthat.R'
#> ● Call `use_test()` to initialize a basic test file and open it for editing.

This initializes the unit testing machinery for your package. It adds Suggests: testthat to DESCRIPTION, creates the directory tests/testthat/, and adds the script test/testthat.R.

[e10f265] 2019-05-09: Add testing infrastructure

However, it’s still up to YOU to write the actual tests!

The helper use_test() opens and/or creates a test file. You can provide the file’s basename or, if you are editing the relevant source file in RStudio, it will be automatically generated. Since this book is built non-interactively, we must provide the basename explicitly:

use_test("fbind")
#> ✔ Increasing 'testthat' version to '>= 2.1.0' in DESCRIPTION
#> ✔ Writing 'tests/testthat/test-fbind.R'

This creates the file tests/testthat/test-fbind.R. Put this content in it:

context("Binding factors")

test_that("fbind() binds factor (or character)", {
  x <- c("a", "b")
  x_fact <- factor(x)
  y <- c("c", "d")
  z <- factor(c("a", "b", "c", "d"))

  expect_identical(fbind(x, y), z)
  expect_identical(fbind(x_fact, y), z)
})

This tests that fbind() gives an expected result when combining two factors and a character vector and a factor.

Run this test interactively, as you will when you write your own. Note you’ll have to attach testthat via library(testthat) in your R session first and you’ll probably want to load_all().

Going forward, your tests will mostly run en masse and at arms’s length via devtools::test():

FIXME: work on the aesthetics of this output.

test()
#> ✔ |  OK F W S | Context
#> |   0       | Binding factors
✔ |   2       | Binding factors
#> 
#> ══ Results ════════════════════════════════════════════════════════════════
#> OK:       2
#> Failed:   0
#> Warnings: 0
#> Skipped:  0

RStudio exposes test() in the Build menu, in the Build pane via More > Test package, and in keyboard shortcuts Ctrl + Shift + T (Windows & Linux) or Cmd + Shift + T (macOS).

Your tests are also run whenever you check() the package. In this way, you basically augment the standard checks with some of your own, that are specific to your package. It is a good idea to use the covr package to track what proportion of your package’s source code is exercised by the tests. More details can be found in chapter 8.

2.16 use_package()

You will inevitably want to use a function from another package in your own package. Just as we needed to export fbind(), we need to import functions from the namespace of other packages. If you plan to submit a package to CRAN, note that this even applies to functions in packages that you think of as “always available”, such as stats::median() or utils::head().

We’re going to add another function to foofactors that produces a sorted frequency table for a factor. We’ll borrow some smarts from the forcats package, specifically the function forcats::fct_count().

First, declare your general intent to use some functions from the forcats namespace with devtools::use_package():

use_package("forcats")
#> ✔ Adding 'forcats' to Imports field in DESCRIPTION
#> ● Refer to functions with `forcats::fun()`

This adds the forcats package to the “Imports” section of DESCRIPTION. And that is all.

[66cdcb5] 2019-05-09: Import forcats

Now we add a second function to foofactors: imagine we want a frequency table for a factor, as a regular data frame with nice variable names, versus as an object of class table or something with odd names. Let’s also sort the rows so that the most prevalent level is at the top.

Initiate a new .R file below R/ with use_r():

use_r("fcount")
#> ● Edit 'R/fcount.R'

Put this content in the file R/fcount.R:

#' Make a sorted frequency table for a factor
#'
#' @param x factor
#'
#' @return A tibble
#' @export
#' @examples
#' fcount(iris$Species)
fcount <- function(x) {
  forcats::fct_count(x, sort = TRUE)
}

Notice how we preface the call to a forcats functions with forcats::. This specifies that we want to call the fct_count() function from the forcats namespace. There is more than one way to call functions in other packages and the one we espouse here is explained fully in chapter 9.

[7d6333d] 2019-05-09: Add fcount()

Try out the new fcount() function by simulating package installation via load_all():

load_all()
#> Loading foofactors
fcount(iris$Species)
#> # A tibble: 3 x 2
#>   f              n
#>   <fct>      <int>
#> 1 setosa        50
#> 2 versicolor    50
#> 3 virginica     50

Generate the associated help file via document().

document()
#> Updating foofactors documentation
#> Writing NAMESPACE
#> Loading foofactors
#> Writing NAMESPACE
#> Writing fcount.Rd
[f56a93a] 2019-05-09: Document fcount()

2.17 use_github()

You’ve seen us making commits during the development process for foofactors. You can see an indicative history at https://github.com/jennybc/foofactors. Our use of version control and the decision to expose the development process means you can inspect the state of the foofactors source at each developmental stage. By looking at so-called diffs, you can see exactly how each devtools helper function modifies the source files that constitute the foofactors package.

How would you connect your local foofactors package and Git repository to a companion repository on GitHub?

  1. use_github() is a helper that we recommend for the long-term. We won’t demonstrate it here because it requires some nontrivial setup on your end. We also don’t want to tear down and rebuild the public foofactors package every time we build this book.
  2. Set up the GitHub repo first! It sounds counterintuitive, but the easiest way to get your work onto GitHub is to initiate there, then use RStudio to start working in a synced local copy. This approach is described in Happy Git’s workflows New project, GitHub first and Existing project, GitHub first.
  3. Command line Git can always be used to add a remote repository post hoc. This is described in the Happy Git workflow Existing project, GitHub last.

Any of these approaches will connect your local foofactors project to a GitHub repo, public or private, which you can push to or pull from using the Git client built into RStudio.

2.18 use_readme_rmd()

Now 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 pkgdown), add a vignette (see chapter 7), or submit it to CRAN (see chapter 16).

The devtools::use_readme_rmd() function initializes a basic, executable README.Rmd ready for you to edit:

use_readme_rmd()
#> ✔ Writing 'README.Rmd'
#> ✔ Adding '^README\\.Rmd$' to '.Rbuildignore'
#> ✔ Writing '.git/hooks/pre-commit'

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

README.Rmd already has sections that:

  • Prompt you to describe the purpose of the package.
  • Provide code to install your package.
  • Prompt you to show a bit of usage.

How to populate this skeleton? Copy stuff liberally from DESCRIPTION and any formal and informal tests or examples you have. Anything is better than nothing. Otherwise … do you expect people to install your package and comb through individual help files to figure out how to use it? They probably won’t.

We like to write the README in R Markdown, so it can feature actual usage. It will load the currently installed version of your package, so this is a good time to do “Install and Restart” in RStudio. Or do this in R Console:

install()
   checking for file ‘/tmp/RtmppsYpXJ/foofactors/DESCRIPTION’ ...
✔  checking for file ‘/tmp/RtmppsYpXJ/foofactors/DESCRIPTION’
─  preparing ‘foofactors’:
   checking DESCRIPTION meta-information ...
✔  checking DESCRIPTION meta-information
─  checking for LF line-endings in source and make files and shell scripts
─  checking for empty or unneeded directories
─  building ‘foofactors_0.0.0.9000.tar.gz’
Running /home/travis/R-bin/lib/R/bin/R CMD INSTALL \
  /tmp/RtmppsYpXJ/foofactors_0.0.0.9000.tar.gz --install-tests 
* installing to library ‘/home/travis/R/Library’
* installing *source* package ‘foofactors’ ...
** using staged installation
** R
** tests
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (foofactors)
Reloading attached foofactors

If RStudio has not already done so, open README.Rmd for editing. Make sure it shows some usage of fbind() and/or fcount(), for example.

[eddf1e9] 2019-05-09: Set up README.Rmd

The README.Rmd we use is here: README.Rmd and these are the contents:

FIXME: update this link after merge into r-pkgs.

---
output:
  md_document:
    variant: markdown_github
---

<!-- README.md is generated from README.Rmd. Please edit that file -->

```{r, echo = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.path = "README-"
)
```

**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 [forcats](https://forcats.tidyverse.org).**

### foofactors

Factors are a very useful type of variable in R, but they can also be very aggravating. This package provides some helper functions for the care and feeding of factors.

### Installation

```{r installation, eval = FALSE}
devtools::install_github("jennybc/foofactors")
```
  
### Quick demo

Binding two factors via `fbind()`:

```{r}
library(foofactors)
a <- factor(c("character", "hits", "your", "eyeballs"))
b <- factor(c("but", "integer", "where it", "counts"))
```

Simply catenating two factors leads to a result that most don't expect.

```{r}
c(a, b)
```

The `fbind()` function glues two factors together and returns factor.

```{r}
fbind(a, b)
```

Often we want a table of frequencies for the levels of a factor. The base `table()` function returns an object of class `table`, which can be inconvenient for downstream work.

```{r}
set.seed(1234)
x <- factor(sample(letters[1:5], size = 100, replace = TRUE))
table(x)
```

The `fcount()` function returns a frequency table as a tibble with a column of factor levels and another of frequencies:

```{r}
fcount(x)
```

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

rmarkdown::render("README.Rmd") ## or use "Knit HTML" in RStudio

You can see the rendered README.md simply by visiting foofactors on GitHub.

Finally, don’t forget to do one last commit. And push, if you’re using GitHub.

[9479cac] 2019-05-09: Write README.Rmd and render

2.19 The End: check() and install()

Let’s run check() again to make sure all is still well.

check()
#> ── R CMD check results ───────────────────────── foofactors 0.0.0.9000 ────
#> Duration: 10s
#> 
#> 0 errors ✔ | 0 warnings ✔ | 0 notes ✔

foofactors should have no errors, warnings or notes. This would be a good time to re-build and install if properly. And celebrate!

install()
   checking for file ‘/tmp/RtmppsYpXJ/foofactors/DESCRIPTION’ ...
✔  checking for file ‘/tmp/RtmppsYpXJ/foofactors/DESCRIPTION’
─  preparing ‘foofactors’:
   checking DESCRIPTION meta-information ...
✔  checking DESCRIPTION meta-information
─  checking for LF line-endings in source and make files and shell scripts
─  checking for empty or unneeded directories
─  building ‘foofactors_0.0.0.9000.tar.gz’
Running /home/travis/R-bin/lib/R/bin/R CMD INSTALL \
  /tmp/RtmppsYpXJ/foofactors_0.0.0.9000.tar.gz --install-tests 
* installing to library ‘/home/travis/R/Library’
* installing *source* package ‘foofactors’ ...
** using staged installation
** R
** tests
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (foofactors)
Reloading attached foofactors

Feel free to visit the foofactors package on GitHub, which is 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.