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.
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)
#> Loading required package: usethis
For presentation purposes only, we use fs, for filesystem work, and the tidyverse, for light data wrangling.
library(tidyverse)
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.
TODO: 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). See section 5.1.3 for more.
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! See 5.1.4 for more.
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.
#> ✔ Creating '/tmp/RtmpigeX17/foofactors/'
#> ✔ Setting active project to '/tmp/RtmpigeX17/foofactors'
#> ✔ 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] (YOUR-ORCID-ID)
#> Description: What the package does (one paragraph).
#> License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a
#> license
#> Encoding: UTF-8
#> LazyData: true
#> Roxygen: list(markdown = TRUE)
#> RoxygenNote: 7.1.1
#> ✔ 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/RtmpigeX17/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.
TODO: 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. More in 4.3.1..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 withcreate_package(..., rstudio = FALSE)
. More in 5.2.
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 16.
The foofactors directory is an R source package and an RStudio Project. Now we make it also a Git repository, with 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.
#> [1e9689a] 2020-07-31: 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.
TODO: 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()
:
#> [1e9689a] 2020-07-31: 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.
factor(c("character", "hits", "your", "eyeballs")))
(a <-#> [1] character hits your eyeballs
#> Levels: character eyeballs hits your
factor(c("but", "integer", "where it", "counts")))
(b <-#> [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'
#> ● Call `use_test()` to create a matching test file
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 6.
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. See section 5.4 for more.
Call 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 vialibrary(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.
#> [bb7bb2e] 2020-07-31: 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. 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: 16.4s
#>
#> ❯ checking DESCRIPTION meta-information ... WARNING
#> Non-standard license specification:
#> `use_mit_license()`, `use_gpl3_license()` or friends to pick a
#> license
#> Standardizable: FALSE
#>
#> 0 errors ✔ | 1 warning ✖ | 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 7.
Make these edits:
- Make yourself the author.
- Write some descriptive text in the
Title
andDescription
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
[27ca885] 2020-07-31: Edit DESCRIPTION
2.11 use_mit_license()
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 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: 2020
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.
[7f268f4] 2020-07-31: 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 8.
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)])
TODO: mention how RStudio helps you execute examples here?
[2dad8c6] 2020-07-31: 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 document()
:
document()
#> Updating foofactors documentation
#> Updating roxygen version in /tmp/RtmpigeX17/foofactors/DESCRIPTION
#> 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 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).
[bc4da38] 2020-07-31: 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: 19.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 install()
:
install()
checking for file ‘/tmp/RtmpigeX17/foofactors/DESCRIPTION’ ...
✔ checking for file ‘/tmp/RtmpigeX17/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 /usr/lib/R/bin/R CMD INSTALL \
/tmp/RtmpigeX17/foofactors_0.0.0.9000.tar.gz --install-tests
* installing to library ‘/usr/local/lib/R/site-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)
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)
factor(c("character", "hits", "your", "eyeballs"))
a <- factor(c("but", "integer", "where it", "counts"))
b <-
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 use_testthat()
:
use_testthat()
#> ✔ Adding 'testthat' to Suggests field in DESCRIPTION
#> ✔ 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
.
[9312ad0] 2020-07-31: 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")
#> ✔ Writing 'tests/testthat/test-fbind.R'
#> ● Edit 'tests/testthat/test-fbind.R'
This creates the file tests/testthat/test-fbind.R
. Put this content in it:
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 test()
:
TODO: work on the aesthetics of this output.
test()
#> ✔ | OK F W S | Context
#>
| 0 | fbind
⠏ | 2 | fbind
✔ #>
#> ══ Results ═════════════════════════════════════════════════════════════════════
#> [ PASS x2 FAIL x0 WARN x0 SKIP x0 ]
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 10.
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 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.
[691c77c] 2020-07-31: 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'
#> ● Call `use_test()` to create a matching test file
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 11.
[0c71a50] 2020-07-31: 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
#> Loading foofactors
#> Writing NAMESPACE
#> Writing NAMESPACE
#> Writing fcount.Rd
[b797e81] 2020-07-31: 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?
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.- 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.
- 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 9), or submit it to CRAN (see chapter 18).
The 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/RtmpigeX17/foofactors/DESCRIPTION’ ...
✔ checking for file ‘/tmp/RtmpigeX17/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
Removed empty directory ‘foofactors/tests/testthat/_snaps’
─ building ‘foofactors_0.0.0.9000.tar.gz’
Running /usr/lib/R/bin/R CMD INSTALL \
/tmp/RtmpigeX17/foofactors_0.0.0.9000.tar.gz --install-tests
* installing to library ‘/usr/local/lib/R/site-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)
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.
[95ded7e] 2020-07-31: Set up README.Rmd
The README.Rmd
we use is here: README.Rmd and these are the contents:
TODO: 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.
::render("README.Rmd") ## or use "Knit HTML" in RStudio rmarkdown
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.
[d490ce4] 2020-07-31: 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: 17s
#>
#> 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/RtmpigeX17/foofactors/DESCRIPTION’ ...
✔ checking for file ‘/tmp/RtmpigeX17/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
Removed empty directory ‘foofactors/tests/testthat/_snaps’
─ building ‘foofactors_0.0.0.9000.tar.gz’
Running /usr/lib/R/bin/R CMD INSTALL \
/tmp/RtmpigeX17/foofactors_0.0.0.9000.tar.gz --install-tests
* installing to library ‘/usr/local/lib/R/site-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)
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.