class: center, middle, inverse, title-slide # Everything you need to know
to create your own
package ### Earo Wang ### May 24, 2018
slides at
http://slides.earo.me/rladies-pkg
--- class: center middle inverse This page has been intentionally left blank. --- ## Why write a π¦? -- <br> <br> <br> <br> <br> <blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Seriously, it doesnβt have to be about sharing your code (although that is an added benefit!). It is about saving yourself time.</p>— Hilary Parker <a href="https://hilaryparker.com/2014/04/29/writing-an-r-package-from-scratch/">April 29, 2014</a></blockquote> --- ## Tools to automate the π¦ development * RStudio * [`available`](https://CRAN.R-project.org/package=available): Check if the Title of a Package is Available, Appropriate and Interesting * [`usethis`](http://usethis.r-lib.org): Automate Package and Project Setup * [`devtools`](https://CRAN.R-project.org/package=devtools): Tools to Make Developing R Packages Easier * [`testthat`](http://testthat.r-lib.org): Unit Testing for R * [`covr`](https://CRAN.R-project.org/package=covr): Test Coverage for Packages * [`pkgdown`](http://pkgdown.r-lib.org): Make Static HTML Documentation for a Package --- class: center middle inverse # Today's mission ##
Create an
package to query the Zomato API --- ## Step 0: come up with a catchy π¦ name * Unique & googleable * Describes what the package does (for example, **knitr** is neater than Sweave) * Only contains letters, numbers, and dots * Begins with a letter, and not end with a period -- ### Good practices * Avoid capitalisation for less memorisation and less typing --- ## Step 0: come up with a catchy π¦ name -- `available::available()` makes sure that the package name is valid and unique. -- ```r available::available("romato", browse = FALSE) ``` ``` #> ββ romato βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ #> Name valid: β #> Available on CRAN: β #> Available on Bioconductor: β #> Available on GitHub: β #> Bad Words: β #> Abbreviations: http://www.abbreviations.com/romato #> Wikipedia: https://en.wikipedia.org/wiki/romato #> Wiktionary: https://en.wiktionary.org/wiki/romato #> Urban Dictionary: #> Not found. #> Sentiment:??? ``` --- ## Step 1: setup π¦ -- `usethis::create_package()` initialises the basic components of a package. -- ```r usethis::create_package("~/Rpkg/romato") ``` ``` #> Changing active project to romato #> β Creating 'R/' #> β Creating 'man/' #> β Writing 'DESCRIPTION' #> β Writing 'NAMESPACE' #> β Writing 'romato.Rproj' #> β Adding '.Rproj.user' to './.gitignore' #> β Adding '^romato\\.Rproj$', '^\\.Rproj\\.user$' to '.Rbuildignore' #> β Opening project in RStudio ``` * `R/`: the R code * `man/`: the documentation (generated by roxygen2: do not edit by hand) * `DESCRIPTION`: the metadata of the package * `NAMESPACE`: how the package interacts with R and with other packages (do not edit by hand) --- ## Step 1: setup π¦ ![](img/romato-proj.png) --- ## Step 2: edit `DESCRIPTION` ``` Package: romato Version: 0.0.0.9000 Title: What the Package Does (One Line, Title Case) Description: What the package does (one paragraph). Authors@R: person("First", "Last", email = "first.last@example.com", role = c("aut", "cre")) License: What license is it under? Encoding: UTF-8 LazyData: true ByteCompile: true ``` -- ### Version numbering convention * major.minor.patch.dev: `0.0.0.9000` --- ## Step 3: choose a license * MIT License `use_mit_license()`: free for anyone to do anything with. * GPLv3 `use_gpl3_license()`: changes and bundles must also be GPL. * CC0 `use_cc0_license()`: dedicated to public domain, best for data packages. -- ```r usethis::use_mit_license("Earo Wang") ``` ``` β Setting License field in DESCRIPTION to 'MIT + file LICENSE' β Writing 'LICENSE.md' β Adding '^LICENSE\\.md$' to '.Rbuildignore' β Writing 'LICENSE' ``` .footnote[more about license: [choose a license](https://choosealicense.com)] --- ## Step 4: π¦-oriented functions We are going to write some Zomato API wrappers. ```r usethis::use_r("zomato-api") ``` ```r β Modify 'zomato-api.R' ``` * An API key is needed, which you can sign up [here](https://developers.zomato.com/api). * Check out [the documentation](https://developers.zomato.com/documentation) for developers. --- ## Non π¦-oriented functions in a script ```r library(httr) library(jsonlite) zomato_search <- function(api_key = NULL, query) { # URL to Zomato API url <- modify_url( url = "https://developers.zomato.com" , path = "/api/v2.1/search" ) # request URL resp <- GET( url = url, config = add_headers("user-key" = api_key), query = list(q = query) ) # read the JSON file parsed <- fromJSON( content( resp, as = "text", type = "application/json", encoding = "UTF-8" ), flatten = TRUE ) # return results parsed$restaurants } ur_key <- "your-api-key" zomato_search(ur_key, query = "Brother Budan Baba Melbourne") ``` --- ## Step 4: π¦-oriented functions In the `R/` folder of a package: * π« `library()` or `require()`: dependencies are managed in the `NAMESPACE` file. * π« `source()` * π« `options()` or `par()` * π« `setwd()` --- class: middle center <iframe src="https://giphy.com/embed/xonOzxf2M8hNu" width="480" height="270" frameBorder="0" class="giphy-embed" allowFullScreen></iframe><p><a href="https://giphy.com/gifs/xonOzxf2M8hNu"></a></p> --- class: middle Click [me](https://gist.github.com/earowang/e027a3f5ceac9f91ef8307415d02e0f5) to download the R scripts for this workshop. --- ## Step 5: use π¦`::fun()` ```r usethis::use_package("httr") ``` ``` #> β Adding 'httr' to Imports field in DESCRIPTION #> β Refer to functions with `httr::fun()` ``` ```r usethis::use_package("jsonlite") ``` ``` #> β Adding 'jsonlite' to Imports field in DESCRIPTION #> β Refer to functions with `jsonlite::fun()` ``` -- .pull-left[ ### one way ``` httr::fun `%>%` <- magrittr::`%>%` ``` ] .pull-right[ ### alternative ``` #' @importFrom httr fun #' @import httr #' @importFrom magrittr %>% ``` ] --- ## Step 6: explore in console
write
code In RStudio, `Cmd/Ctrl` + `Shift` + `L` loads all the functions into the environment. ```r devtools::load_all(".") #> Loading romato ``` --- ## Step 7: document * Enable markdown support in roxygen ```r usethis::use_roxygen_md() ``` ``` #> β Setting Roxygen field in DESCRIPTION to 'list(markdown = TRUE)' #> β Setting RoxygenNote field in DESCRIPTION to '6.0.1' #> β Re-document ``` --- ## Step 7: document * `Cmd/Ctrl` + `Opt/Alt` + `Shift` + `R` inserts roxygen skeleton ```r #' Title #' #' @param api_key #' @param query #' #' @return #' @export #' #' @examples zomato_search <- function(api_key = NULL, query) { ... } ``` -- Each roxygen comment is preceded by `#'`. * `@param`: inputs of the function, followed by a description * `@return`: what the function returns * `@export`: make the function visible/accessible to users * `@examples`: examples of how to use the function To know the list of parameters: `browseVignettes("roxygen2")`. --- ## Step 7: document * `Cmd/Ctrl` + `Shift` + `D` to generate `/man` files (`*.Rd`) ``` ==> devtools::document(roclets=c('rd', 'collate', 'namespace')) Updating romato documentation Loading romato Writing NAMESPACE Documentation completed ``` `?zomato_search` to preview the man page --- ## Step 8: install π¦ * `Cmd/Ctrl` + `Shift` + `B` to build the package ``` ==> R CMD INSTALL --no-multiarch --with-keep.source romato * installing to library β/Library/Frameworks/R.framework/Versions/3.5/Resources/libraryβ * installing *source* package βromatoβ ... ** R ** byte-compile and prepare package for lazy loading ** help *** installing help indices ** building package indices ** testing if installed package can be loaded * DONE (romato) ``` --- ## Step 9: R CMD check * `Cmd/Ctrl` + `Shift` + `E` to perform R CMD check ``` Status: OK R CMD check results 0 errors | 0 warnings | 0 notes R CMD check succeeded ``` --- class: center middle inverse ## π ##
Create an
package to query the Zomato API --- class: center middle inverse # Good practices ### Build a π¦ that sustains --- ## Unit tests Catch bugs before they happen, and guarantee the validity of the code. -- ```r usethis::use_test("api") ``` ``` #> β Adding 'testthat' to Suggests field in DESCRIPTION #> β Creating 'tests/testthat/' #> β Writing 'tests/testthat.R' #> β Writing 'tests/testthat/test-api.R' #> β Modify 'test-api.R' ``` -- For example in the `test-api.R` ```r context("Zomato API") test_that("invalid input", { expect_error(zomato_search(api_key = NULL), "Please provide an API key.") }) ``` * `Cmd/Ctrl` + `Shift` + `T` to perform tests --- ## Unit tests <br> <br> <br> <br> <blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Sometime I self-soothe by writing unit tests.<br>This is how the world should be and β¦ it is.<br>This is how the world should be and β¦ it is.<br>This is how β¦ WTF!?! OK, fixed.<br>This is how the world should be and β¦ it is.</p>— Jenny Bryan (@JennyBryan) <a href="https://twitter.com/JennyBryan/status/983096934209482752?ref_src=twsrc%5Etfw">April 8, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> --- ## Code coverage Code coverage is the % of code that is covered by unit tests. ```r covr::package_coverage() ``` ![tsibble-coverage](img/tsibble-coverage.png) `covr::report()` displays covr results in a standalone report. --- ## README ```r usethis::use_readme_rmd() ``` ``` β Writing 'README.Rmd' β Adding '^README\\.Rmd$' to '.Rbuildignore' β Modify 'README.Rmd' ``` -- A README file should include: * A brief overview of the package * instructions on how to install * A few examples --- ## Vignette ```r usethis::use_vignette("intro-romato") ``` ``` #> β Adding 'knitr' to Suggests field in DESCRIPTION #> β Setting VignetteBuilder field in DESCRIPTION to 'knitr' #> β Adding 'rmarkdown' to Suggests field in DESCRIPTION #> β Creating 'vignettes/' #> β Adding '*.html', '*.R' to 'vignettes/.gitignore' #> β Adding 'inst/doc' to './.gitignore' #> β Creating 'vignettes/intro-romato.Rmd' #> β Modify 'intro-romato.Rmd' ``` -- Rmarkdown + metadata ``` --- title: "Vignette Title" author: "Vignette Author" date: "2018-05-24" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Vignette Title} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ``` --- ## Website Build a website for your π¦ in 2 minutes. -- ```r pkgdown::build_site() ``` [![pkgdown](img/pkgdown.png)](http://pkgdown.r-lib.org) --- ## Release * Creates `cran-comments.md`, a template for your communications with CRAN when submitting a package. ```r usethis::use_cran_comments() ``` ``` #> β Writing 'cran-comments.md' #> β Adding '^cran-comments\\.md$' to '.Rbuildignore' #> β Modify 'cran-comments.md' ``` * Submit the π¦ to CRAN. ~~[CRAN online submission form](https://cran.r-project.org/submit.html)~~ ```r devtools::release(spelling = "en_GB") ``` --- class: center middle inverse # More -- Version control (Git & Github) -- Continuous integration -- Track changes in NEWS -- S3, S4, R6, Reference class -- ... -- # Keep calm # and write unit tests! --- ## References * [R packages](http://r-pkgs.had.co.nz)
by Hadley Wickham * [Building a package that lasts](https://github.com/ColinFay/erum2018)
by Colin Fay at eRum 2018 workshop * [Women's coding workshop](http://bit.ly/forwards-nz)
by Hadley Wickham, Jenny Brian, Di Cook -- ## Resources * [You can make a package in 20 minutes](https://www.rstudio.com/resources/videos/you-can-make-a-package-in-20-minutes/)
by Jim Hester * [`usethis` README](http://usethis.r-lib.org)
* [Prepare for CRAN](https://github.com/ThinkR-open/prepare-for-cran)
* [Tidyverse style guide](http://style.tidyverse.org)
--- class: inverse middle center <img src="img/rladies.png" height=100px> <img src="img/forwards.png" height=100px> -- <hr> ### Made with
-- ### Slides created via xaringan βοΈ <http://slides.earo.me/rladies-pkg> -- ### Open source <https://github.com/earowang/rladies-pkg> -- ### This work is under licensed [
BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/). -- ### Thank you!