Building R packages (part I)

PHS 7045: Advanced Programming

George G. Vega Yon, Ph.D.

The University of Utah

2024-11-04

Intro

Today

What we’ll be using today

  1. devtools: “Package development tools for R” (read more).

  2. roxygen2: “A ‘Doxygen’-like in-source documentation system for Rd, collation, and ‘NAMESPACE’ files” (read more).

  3. covr: (An R package for) “Test Coverage for Packages” (read more).

  4. RStudio: “RStudio is an integrated development environment (IDE) for R” (read more).

  5. valgrind: (if you use C/C++ code) “a memory error detector” (read more).

Why ‘spend’ time writing an R package?

To name a few:

  1. Easy way to share code: Type install.packages and voila!

  2. Already standardized: So you don’t need to think about how to structure it.

  3. CRAN checks everything for you: Force yourself to code things right.

  4. Reproducible Research

Not so obvious examples

  • Implementing a pipeline: A collection of functions that are used to process data in a specific way. e.g., CDC’s epinow2-pipeline (link).

  • Branding: You can create an extension for Rmarkdown, quarto, or ggplot2 (to name a few) that contains your company’s colors and logos. e.g., University of Southern California’s IMAGE grant (link) (see also this for quarto formats.)

  • Shiny apps: So it is easier to share them with other users, e.g., the epiworldRShiny implements a shiny-version of the epiworldR R package.

  • Sharing Data: There are multiple R packages that only contain data, for instance, the gapminder package (link).

What’s an R package, anyway?

According to Hadley Wickham’s “R packages”

Packages are the fundamental units of reproducible R code. They include reusable R functions, the documentation that describes how to use them, and sample data.

What’s an R package, anyway? (cont.)

Folders

  • R Where the R scripts live, e.g., addnums.r, boring-pkg.r.

  • tests Where the tests live, e.g., testthat/test-addnums.r (using testthat or tinytest).

  • vignettes Where vignettes live, e.g., vignettes/mode_details_on_addnums.rmd.

  • data Where the data lives, e.g., fakedata.rda, aneattable.csv.

  • man Where the manuals live, e.g., addnums.Rd, boringpkg.Rd.

And files

  • DESCRIPTION The metadata of the package: name, description, version, author(s), dependencies, etc.

  • NAMESPACE Declares the name of the objects (functions mainly) that will be part of the package (and will be visible to the user)

More info here and here

Dev cycle

Writing R packages is an iterative process

  1. Set up the structure: Create folders and files R/, data/, src/, tests/, man/, vignettes/, DESCRIPTION, NAMESPACE(?)

  2. Code! For f in F do

    1. Write f

    2. Document f: what it does, what the inputs are, what it returns, and examples.

    3. Add some tests: is it doing what it is supposed to do?

    4. R CMD Check: Will CRAN and the rest of the functions ‘like’ it?

    5. Commit your changes!: Update ChangeLog+news.md, and commit (so travis and friends run)!

    6. next f

  3. Submit your package to CRAN/Bioconductor (read more)

Handson

Step 1: Set up the structure

You have several methods: package.skeleton(), Rstudio’s “New Project”, devtools::new(), etc. You can also use usethis::create_package().

library(usethis)

# I'm using a temp folder, but you should use something else like
# "C:/Users/vegayon/Documents/boringpkg"
tmp_folder <- tempfile("boringpkg") 

# Creating the package
usethis::create_package(
  path = tmp_folder,
  roxygen = TRUE,
  rstudio = TRUE,
  fields = list(
    Package = "boringpkg",
    Version = "0.0.0.9000",
    Title = "A boring package",
    Description = "A boring package"
  )
)

# We can inspect what things it created:
list.files(tmp_folder, recursive = TRUE)

Try it out!

Step 1: Set up the structure (cont. 1)

And, using the usethis package, we can add some extras. Locally:

# Creating a README for the project
usethis::use_readme_rmd()

# Infrastructure for testing
usethis::use_testthat()

# A LICENSE file (required by CRAN)
# The line "license: MIT + file" in the DESCRIPTION file
usethis::use_mit_license(copyright_holder = "George G. Vega Yon")
usethis::use_news_md()

If you are planning to use GitHub:

# Some basic CI infrastructure
usethis::use_github_action_check_standard()

Try it out! Other things we are not setting up: Codecoverage (checkout the usethis::use_coverage() function).

Step 1: Set up the structure (cont. 2)

What did happen?

  1. use_readme_rmd(): Creates a readme file that will be in the project’s main folder. Think about it as the home page of your project.

  2. use_testthat(): The basic infrastructure for testing packages will be created.

  3. use_mit_license(copyright_holder = "George G. Vega Yon"): Creates the LICENSE file and puts it under ‘George G. Vega Yon’.

  4. use_news_md(): Creates the news.md file which us used for tracking changes and communicating them to the users (e.g. netdiffuseR)

Now that we have the structure, we can start coding!

Step 2.1 and 2.2: Write f and Document it

  • Using roxygen2 is very straight forward. For our fist pice of code, we create the R/addnums.r file (right).

  • Type devtools::document(), or press Ctrl + Shift + D (RStudio will: create the manual and the NAMESPACE). Make sure you activate this option in RStudio (not the default).

  • Notice that the output is defined using S3-type objects (read more)

#' The title of -addnums-
#'
#' Here is a brief description
#' 
#' @param a Numeric scalar. A brief description.
#' @param b Numeric scalar. A brief description.
#' 
#' @details Computes the sum of \code{x} and \code{y}.
#' @return A list of class \code{boringpkg_addnums}:
#' \item{a}{Numeric scalar.}
#' \item{b}{Numeric scalar.}
#' \item{ab}{Numeric scalar. the sum of \code{a} and \code{b}}
#' @examples
#' foo(1, 2)
#' 
#' @export
addnums <- function(a, b) {
    ans <- a + b
    structure(list(a = a, b = b, ab = ans)
    , class = "boringpkg_addnums")
}

Detour: S3 classes

Here we are leveraging S3 classes. In R, the S3 is a simple way to do object oriented programming. The way it works:

  1. Objects should have a class attribute (e.g., class(x) <- "myclass").

  2. You can then define “methods” for that class, e.g., print.myclass and plot.myclass.

  3. These are valid as long as there is a generic function (e.g., print and plot) that can handle the class.

You can inspect the methods using the methods() function, e.g.,

methods(generic.function = "print")
  [1] print.acf*                                          
  [2] print.activeConcordance*                            
  [3] print.AES*                                          
  [4] print.anova*                                        
  [5] print.aov*                                          
  [6] print.aovlist*                                      
  [7] print.ar*                                           
  [8] print.Arima*                                        
  [9] print.arima0*                                       
 [10] print.AsIs                                          
 [11] print.aspell*                                       
 [12] print.aspell_inspect_context*                       
 [13] print.bibentry*                                     
 [14] print.Bibtex*                                       
 [15] print.browseVignettes*                              
 [16] print.by                                            
 [17] print.changedFiles*                                 
 [18] print.check_bogus_return*                           
 [19] print.check_code_usage_in_package*                  
 [20] print.check_compiled_code*                          
 [21] print.check_demo_index*                             
 [22] print.check_depdef*                                 
 [23] print.check_details*                                
 [24] print.check_details_changes*                        
 [25] print.check_doi_db*                                 
 [26] print.check_dotInternal*                            
 [27] print.check_make_vars*                              
 [28] print.check_nonAPI_calls*                           
 [29] print.check_package_code_assign_to_globalenv*       
 [30] print.check_package_code_attach*                    
 [31] print.check_package_code_data_into_globalenv*       
 [32] print.check_package_code_startup_functions*         
 [33] print.check_package_code_syntax*                    
 [34] print.check_package_code_unload_functions*          
 [35] print.check_package_compact_datasets*               
 [36] print.check_package_CRAN_incoming*                  
 [37] print.check_package_datalist*                       
 [38] print.check_package_datasets*                       
 [39] print.check_package_depends*                        
 [40] print.check_package_description*                    
 [41] print.check_package_description_encoding*           
 [42] print.check_package_license*                        
 [43] print.check_packages_in_dir*                        
 [44] print.check_packages_used*                          
 [45] print.check_po_files*                               
 [46] print.check_pragmas*                                
 [47] print.check_Rd_line_widths*                         
 [48] print.check_Rd_metadata*                            
 [49] print.check_Rd_xrefs*                               
 [50] print.check_RegSym_calls*                           
 [51] print.check_S3_methods_needing_delayed_registration*
 [52] print.check_so_symbols*                             
 [53] print.check_T_and_F*                                
 [54] print.check_url_db*                                 
 [55] print.check_vignette_index*                         
 [56] print.checkDocFiles*                                
 [57] print.checkDocStyle*                                
 [58] print.checkFF*                                      
 [59] print.checkRd*                                      
 [60] print.checkRdContents*                              
 [61] print.checkReplaceFuns*                             
 [62] print.checkS3methods*                               
 [63] print.checkTnF*                                     
 [64] print.checkVignettes*                               
 [65] print.citation*                                     
 [66] print.cli_ansi_html_style*                          
 [67] print.cli_ansi_string*                              
 [68] print.cli_ansi_style*                               
 [69] print.cli_boxx*                                     
 [70] print.cli_diff_chr*                                 
 [71] print.cli_doc*                                      
 [72] print.cli_progress_demo*                            
 [73] print.cli_rule*                                     
 [74] print.cli_sitrep*                                   
 [75] print.cli_spark*                                    
 [76] print.cli_spinner*                                  
 [77] print.cli_tree*                                     
 [78] print.codoc*                                        
 [79] print.codocClasses*                                 
 [80] print.codocData*                                    
 [81] print.colorConverter*                               
 [82] print.compactPDF*                                   
 [83] print.condition                                     
 [84] print.connection                                    
 [85] print.CRAN_package_reverse_dependencies_and_views*  
 [86] print.data.frame                                    
 [87] print.Date                                          
 [88] print.default                                       
 [89] print.dendrogram*                                   
 [90] print.density*                                      
 [91] print.difftime                                      
 [92] print.dist*                                         
 [93] print.Dlist                                         
 [94] print.DLLInfo                                       
 [95] print.DLLInfoList                                   
 [96] print.DLLRegisteredRoutines                         
 [97] print.dummy_coef*                                   
 [98] print.dummy_coef_list*                              
 [99] print.ecdf*                                         
[100] print.eigen                                         
[101] print.evaluate_evaluation*                          
[102] print.factanal*                                     
[103] print.factor                                        
[104] print.family*                                       
[105] print.fileSnapshot*                                 
[106] print.findLineNumResult*                            
[107] print.formula*                                      
[108] print.ftable*                                       
[109] print.function                                      
[110] print.getAnywhere*                                  
[111] print.glm*                                          
[112] print.hashtab*                                      
[113] print.hclust*                                       
[114] print.help_files_with_topic*                        
[115] print.hexmode                                       
[116] print.HoltWinters*                                  
[117] print.hsearch*                                      
[118] print.hsearch_db*                                   
[119] print.htest*                                        
[120] print.html*                                         
[121] print.html_dependency*                              
[122] print.htmltools.selector*                           
[123] print.htmltools.selector.list*                      
[124] print.infl*                                         
[125] print.integrate*                                    
[126] print.isoreg*                                       
[127] print.json*                                         
[128] print.key_missing*                                  
[129] print.kmeans*                                       
[130] print.knitr_kable*                                  
[131] print.Latex*                                        
[132] print.LaTeX*                                        
[133] print.libraryIQR                                    
[134] print.listof                                        
[135] print.lm*                                           
[136] print.loadings*                                     
[137] print.loess*                                        
[138] print.logLik*                                       
[139] print.ls_str*                                       
[140] print.medpolish*                                    
[141] print.MethodsFunction*                              
[142] print.mtable*                                       
[143] print.NativeRoutineList                             
[144] print.news_db*                                      
[145] print.nls*                                          
[146] print.noquote                                       
[147] print.numeric_version                               
[148] print.object_size*                                  
[149] print.octmode                                       
[150] print.packageDescription*                           
[151] print.packageInfo                                   
[152] print.packageIQR*                                   
[153] print.packageStatus*                                
[154] print.paged_df*                                     
[155] print.pairwise.htest*                               
[156] print.person*                                       
[157] print.POSIXct                                       
[158] print.POSIXlt                                       
[159] print.power.htest*                                  
[160] print.ppr*                                          
[161] print.prcomp*                                       
[162] print.princomp*                                     
[163] print.proc_time                                     
[164] print.quosure*                                      
[165] print.quosures*                                     
[166] print.raster*                                       
[167] print.Rconcordance*                                 
[168] print.Rd*                                           
[169] print.recordedplot*                                 
[170] print.restart                                       
[171] print.RGBcolorConverter*                            
[172] print.RGlyphFont*                                   
[173] print.rlang_box_done*                               
[174] print.rlang_box_splice*                             
[175] print.rlang_data_pronoun*                           
[176] print.rlang_dict*                                   
[177] print.rlang_dyn_array*                              
[178] print.rlang_envs*                                   
[179] print.rlang_error*                                  
[180] print.rlang_fake_data_pronoun*                      
[181] print.rlang_lambda_function*                        
[182] print.rlang_message*                                
[183] print.rlang_trace*                                  
[184] print.rlang_warning*                                
[185] print.rlang_zap*                                    
[186] print.rlang:::list_of_conditions*                   
[187] print.rle                                           
[188] print.rlib_bytes*                                   
[189] print.rlib_error_3_0*                               
[190] print.rlib_trace_3_0*                               
[191] print.roman*                                        
[192] print.scalar*                                       
[193] print.sessionInfo*                                  
[194] print.shiny.tag*                                    
[195] print.shiny.tag.env*                                
[196] print.shiny.tag.list*                               
[197] print.shiny.tag.query*                              
[198] print.simple.list                                   
[199] print.smooth.spline*                                
[200] print.socket*                                       
[201] print.srcfile                                       
[202] print.srcref                                        
[203] print.stepfun*                                      
[204] print.stl*                                          
[205] print.StructTS*                                     
[206] print.subdir_tests*                                 
[207] print.summarize_CRAN_check_status*                  
[208] print.summary.aov*                                  
[209] print.summary.aovlist*                              
[210] print.summary.ecdf*                                 
[211] print.summary.glm*                                  
[212] print.summary.lm*                                   
[213] print.summary.loess*                                
[214] print.summary.manova*                               
[215] print.summary.nls*                                  
[216] print.summary.packageStatus*                        
[217] print.summary.ppr*                                  
[218] print.summary.prcomp*                               
[219] print.summary.princomp*                             
[220] print.summary.table                                 
[221] print.summary.warnings                              
[222] print.summaryDefault                                
[223] print.table                                         
[224] print.tables_aov*                                   
[225] print.terms*                                        
[226] print.ts*                                           
[227] print.tskernel*                                     
[228] print.TukeyHSD*                                     
[229] print.tukeyline*                                    
[230] print.tukeysmooth*                                  
[231] print.undoc*                                        
[232] print.vignette*                                     
[233] print.warnings                                      
[234] print.xfun_raw_string*                              
[235] print.xfun_record_results*                          
[236] print.xfun_rename_seq*                              
[237] print.xfun_strict_list*                             
[238] print.xgettext*                                     
[239] print.xngettext*                                    
[240] print.xtabs*                                        
see '?methods' for accessing help and source code
methods(class = "glm")
 [1] add1           anova          coerce         confint        cooks.distance
 [6] deviance       drop1          effects        extractAIC     family        
[11] formula        influence      initialize     logLik         model.frame   
[16] nobs           predict        print          profile        residuals     
[21] rstandard      rstudent       show           sigma          slotsFromS3   
[26] summary        vcov           weights       
see '?methods' for accessing help and source code

Detour: S3 classes (cont.)

You can also define your own methods, e.g.,:

my_generic <- function(x) UseMethod("my_generic")
my_generic.default <- function(x) "I don't know how to handle this"
my_generic.numeric <- function(x) "I know how to handle this"

my_generic("a")
[1] "I don't know how to handle this"
my_generic(1)
[1] "I know how to handle this"

Step 2.1 and 2.2: Write f and Document it (cont. 1)

Here we are defining a method for plot.boringpkg_addnums. Notice that the plot function already has a generic method, which is why we only need to define the method for boringpkg_addnums.

#' @rdname addnums
#' @export
#' @param x An object of class \code{boringpkg_addnums}.
#' @param y Ignored.
#' @param ... Further arguments passed to
#' \code{\link[graphics:plot.window]{plot.window}}.
plot.boringpkg_addnums <- function(x, y = NULL, ...) {
    graphics::plot.new()
    graphics::plot.window(xlim = range(unlist(c(0,x))), ylim = c(-.5,1))
    graphics::axis(1)
    with(x, graphics::segments(0, 1, ab, col = "blue", lwd=3))
    with(x, graphics::segments(0, 0, a, col = "green", lwd=3))
    with(x, graphics::segments(a, .5, a + b, col = "red", lwd=3))
    graphics::legend("bottom", col = c("blue", "green", "red"), 
            legend = c("a+b", "a", "b"), bty = "n", 
            ncol = 3, lty = 1, lwd=3)
}
  • Here, we are adding a function that is documented in addnums. The plot method (left).

  • You’ll need to update the man/*Rd every time that you add new roxygen content. Press Ctrl + Shift + D, and RStudio will do it for you (or enter devtools::document() which calls roxygen2::roxygenize()).

  • Notice that functions from foreign packages are called using the :: operator (read more here and here). Not a requirement, but makes the code easier to maintain (and less error-prone… because of R’s scoping rules).

Step 2.1 and 2.2: Write f and Document it (cont. 2)

Since we added foreign functions, we need to add them to the NAMESPACE and to the DESCRIPTION files:

R/boring-pkg.r

#' @importFrom graphics plot.new plot.window axis legend
NULL

#' boringpkg
#'
#' A (not so) boring collection of functions
#'
#' @description We add stuff up... You can access to the project
#' website at \url{https://github.com/USCbiostats/software-dev/tree/master/rpkgs/boringpkg}
#'
#' @docType package
#' @name boringpkg
#'
#' @author George G. Vega Yon
NULL

DESCRIPTION

Package: boringpkg
Type: Package
Title: What the Package Does (Title Case)
Version: 0.1.0
Author: Who wrote it
Maintainer: The package maintainer <yourself@somewhere.net>
Description: More about what it does (maybe more than one line)
    Use four spaces when indenting paragraphs within the Description.
License: What license is it under?
Encoding: UTF-8
LazyData: true
Suggests:
    testthat,
    covr
RoxygenNote: 5.0.1
Imports: graphics

The Suggests and RoxygenNote fields were added automagically by devtools.

Step 2.3: Add some tests

Test are run every time that you run R CMD check

  • In the tests/testthat/ dir, add/edit a source file with tests, e.g. test-addnums.r

    test_that("addnums(a, b) = a+b", {
      # Preparing the test
      a <- 1
      b <- -2
    
      # Calling the function
      ans0 <- a+b
      ans1 <- addnums(a, b)
    
      # Are these equal?
      expect_equal(ans0, ans1$ab)
    })
    
    test_that("Plot returns -boringpkg_foo-", {
      expect_s3_class(
        plot(addnums(1,2)), "boringpkg_foo"
        ) # This will generate an error as the function plot.boringpkg_addnum
          # does not return an object of that class.
    })
  • You can run the tests using Ctrl + Shift + t

  • Furthermore, tests are used to evaluate code coverage.

Step 2.4: R CMD check

Running R CMD check is easy with RStudio

  • If you don’t have C/C++ code Just press Ctrl + Shift + E

  • If you have C/C++ code, use R CMD Check with valgrind (check for segfaults)

    $ R CMD build boringpkg/
    $ R CMD check --as-cran --use-valgrind boringpkg*.tar.gz

    You can ask RStudio to use valgrind too.

For an extensive list of the checks, see the section 1.3.1 Checking packagesin the “Writing R extensions” manual.

How does the output of R CMD check looks like?

==> devtools::check()

Updating boringpkg documentation
Loading boringpkg
Setting env vars ---------------------------------------------------------------
CFLAGS  : -Wall -pedantic
CXXFLAGS: -Wall -pedantic
Building boringpkg --------------------------------------------------------------
'/usr/lib/R/bin/R' --no-site-file --no-environ --no-save --no-restore --quiet  \
  CMD build  \
  '/home/vegayon/Dropbox/usc/core_project/CodingStandards/rpkgs/boringpkg'  \
  --no-resave-data --no-manual 

* checking for file ‘/home/vegayon/Dropbox/usc/core_project/CodingStandards/rpkgs/boringpkg/DESCRIPTION’ ... OK
* preparing ‘boringpkg’:
* checking DESCRIPTION meta-information ... OK
* checking for LF line-endings in source and make files
* checking for empty or unneeded directories
* building ‘boringpkg_0.1.999.tar.gz’

Setting env vars ---------------------------------------------------------------
_R_CHECK_CRAN_INCOMING_USE_ASPELL_: TRUE
_R_CHECK_CRAN_INCOMING_           : FALSE
_R_CHECK_FORCE_SUGGESTS_          : FALSE
Checking boringpkg --------------------------------------------------------------
'/usr/lib/R/bin/R' --no-site-file --no-environ --no-save --no-restore --quiet  \
  CMD check '/tmp/RtmpBYGSto/boringpkg_0.1.999.tar.gz' --as-cran --timings  \
  --no-manual 

* using log directory ‘/home/vegayon/Dropbox/usc/core_project/CodingStandards/rpkgs/boringpkg.Rcheck’
* using R version 3.3.2 (2016-10-31)
* using platform: x86_64-pc-linux-gnu (64-bit)
* using session charset: UTF-8
* using options ‘--no-manual --as-cran’
* checking for file ‘boringpkg/DESCRIPTION’ ... OK
* checking extension type ... Package
* this is package ‘boringpkg’ version ‘0.1.999’
* package encoding: UTF-8
* checking package namespace information ... OK
* checking package dependencies ... OK
* checking if this is a source package ... OK
* checking if there is a namespace ... OK
* checking for executable files ... OK
* checking for hidden files and directories ... OK
* checking for portable file names ... OK
* checking for sufficient/correct file permissions ... OK
* checking whether package ‘boringpkg’ can be installed ... OK
* checking installed package size ... OK
* checking package directory ... OK
* checking DESCRIPTION meta-information ... OK
* checking top-level files ... OK
* checking for left-over files ... OK
* checking index information ... OK
* checking package subdirectories ... OK
* checking R files for non-ASCII characters ... OK
* checking R files for syntax errors ... OK
* checking whether the package can be loaded ... OK
* checking whether the package can be loaded with stated dependencies ... OK
* checking whether the package can be unloaded cleanly ... OK
* checking whether the namespace can be loaded with stated dependencies ... OK
* checking whether the namespace can be unloaded cleanly ... OK
* checking loading without being on the library search path ... OK
* checking dependencies in R code ... OK
* checking S3 generic/method consistency ... OK
* checking replacement functions ... OK
* checking foreign function calls ... OK
* checking R code for possible problems ... OK
* checking Rd files ... OK
* checking Rd metadata ... OK
* checking Rd line widths ... OK
* checking Rd cross-references ... OK
* checking for missing documentation entries ... OK
* checking for code/documentation mismatches ... OK
* checking Rd \usage sections ... OK
* checking Rd contents ... OK
* checking for unstated dependencies in examples ... OK
* checking examples ... OK
* checking for unstated dependencies in ‘tests’ ... OK
* checking tests ...
  Running ‘testthat.R’ OK
* DONE
Status: OK




R CMD check results
0 errors | 0 warnings | 0 notes

R CMD check succeeded

Step 2.4: R CMD check (cont. 1)

R packages can be shared by “building” them. For example, the following command:

R CMD build ~/Documents/boringpkg

Will generate package the R package into a file named boringpkg_0.1.0.tar.gz that can later be distributed. Users can install this package either through command line:

R CMD INSTALL boringpkg_0.1.0.tar.gz

Or through the console in R

install.packages("boringpkg_0.1.0.tar.gz", repos = NULL)

Step 2.5: Commit your changes

Once you have run R CMD checks and no errors or unexpected issues are detected, you should commit your changes and push them to Github.

  1. Edit the NEWS.md file and the ChangeLog (if you have one).

  2. Add the new files to the tree, commit your changes, and push them to Github

    $ git add R/newfile.r man/newfile.Rd
    $ git commit -a -m "Adding new function"
    $ git push

Once uploaded, I recommend checking Travis and AppVeyor to see if everything went well.

Step 3: Submit your package to CRAN/Bioconductor

  • You can release your package using devtools::release() function.

  • The devtools package was built to make life easier. Such is it’s creator’s confidence in the release function that it comes with a warranty!

    If a devtools bug causes one of the CRAN maintainers to treat you impolitely, I will personally send you a handwritten apology note. Please forward me the email and your address, and I’ll get a card in the mail.

Dev Cycle (once again!)

The most important step

  1. Code! For f in F do

    1. Write f

    2. Document f: what it does, what the inputs are, what it returns, examples.

    3. Add some tests: is it doing what is supposed to do?

    4. R CMD Check: Will CRAN, and the rest of the functions, ‘like’ it?

    5. Commit your changes!: Update ChangeLog+news.md, and commit (so travis and friends run)!

    6. next f

Fin

─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.4.1 (2024-06-14)
 os       macOS 15.0.1
 system   aarch64, darwin23.4.0
 ui       unknown
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       America/Denver
 date     2024-11-12
 pandoc   3.2.1 @ /opt/homebrew/bin/ (via rmarkdown)

─ Packages ───────────────────────────────────────────────────────────────────
 package     * version date (UTC) lib source
 cachem        1.1.0   2024-05-16 [1] CRAN (R 4.4.1)
 cli           3.6.3   2024-06-21 [1] CRAN (R 4.4.1)
 devtools      2.4.5   2022-10-11 [1] CRAN (R 4.4.1)
 digest        0.6.37  2024-08-19 [1] RSPM (R 4.4.1)
 ellipsis      0.3.2   2021-04-29 [1] CRAN (R 4.4.1)
 evaluate      1.0.0   2024-09-17 [1] RSPM (R 4.4.1)
 fastmap       1.2.0   2024-05-15 [1] CRAN (R 4.4.1)
 fs            1.6.4   2024-04-25 [1] CRAN (R 4.4.1)
 glue          1.7.0   2024-01-09 [1] CRAN (R 4.4.1)
 htmltools     0.5.8.1 2024-04-04 [1] RSPM (R 4.4.1)
 htmlwidgets   1.6.4   2023-12-06 [1] CRAN (R 4.4.1)
 httpuv        1.6.15  2024-03-26 [1] CRAN (R 4.4.1)
 jsonlite      1.8.9   2024-09-20 [1] RSPM (R 4.4.1)
 knitr         1.47    2024-05-29 [1] CRAN (R 4.4.1)
 later         1.3.2   2023-12-06 [1] CRAN (R 4.4.1)
 lifecycle     1.0.4   2023-11-07 [1] CRAN (R 4.4.1)
 magrittr      2.0.3   2022-03-30 [1] CRAN (R 4.4.1)
 memoise       2.0.1   2021-11-26 [1] CRAN (R 4.4.1)
 mime          0.12    2021-09-28 [1] CRAN (R 4.4.1)
 miniUI        0.1.1.1 2018-05-18 [1] CRAN (R 4.4.1)
 pkgbuild      1.4.4   2024-03-17 [1] CRAN (R 4.4.1)
 pkgload       1.4.0   2024-06-28 [1] CRAN (R 4.4.1)
 profvis       0.3.8   2023-05-02 [1] CRAN (R 4.4.1)
 promises      1.3.0   2024-04-05 [1] CRAN (R 4.4.1)
 purrr         1.0.2   2023-08-10 [1] CRAN (R 4.4.1)
 R6            2.5.1   2021-08-19 [1] CRAN (R 4.4.1)
 Rcpp          1.0.13  2024-07-17 [1] RSPM (R 4.4.1)
 remotes       2.5.0   2024-03-17 [1] CRAN (R 4.4.1)
 rlang         1.1.4   2024-06-04 [1] CRAN (R 4.4.1)
 rmarkdown     2.27    2024-05-17 [1] CRAN (R 4.4.1)
 sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.4.1)
 shiny         1.8.1.1 2024-04-02 [1] CRAN (R 4.4.1)
 stringi       1.8.4   2024-05-06 [1] CRAN (R 4.4.1)
 stringr       1.5.1   2023-11-14 [1] CRAN (R 4.4.1)
 urlchecker    1.0.1   2021-11-30 [1] CRAN (R 4.4.1)
 usethis       2.2.3   2024-02-19 [1] CRAN (R 4.4.1)
 vctrs         0.6.5   2023-12-01 [1] CRAN (R 4.4.1)
 xfun          0.45    2024-06-16 [1] CRAN (R 4.4.1)
 xtable        1.8-4   2019-04-21 [1] CRAN (R 4.4.1)
 yaml          2.3.8   2023-12-11 [1] CRAN (R 4.4.1)

 [1] /opt/homebrew/lib/R/4.4/site-library
 [2] /opt/homebrew/Cellar/r/4.4.1/lib/R/library

──────────────────────────────────────────────────────────────────────────────

More resources