class: center, middle, inverse, title-slide # 📦 Developing Packages 📦 ## The Life-Cycle of Creating and Deploying Packages with R ###
Kyle Harris
@KoderKow
###
https://koderkow.rbind.io
Follow Along:
http://bit.ly/developing-packages
--- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"> # Overview - [Presentation Goal](#presentation-goal) - [Prerequisites](#prerequisites) - [What is a Package?](#what-is-a-package) - [Creating a Package](#create-package) - [First Function](#first-function) - [Test Functions](#test-functions) - [check() it!](#check-it) - [Editing the DESCRIPTION File](#description) - [Setting up Git](#setup-git) - [Setting up GitHub](#setup-github) - [Documenting Functions](#document) - [Commit and Push Changes](#commit-and-push) - [Automate Testing](#automate-testing) - [Importing Functions](#importing-functions) - [Create a README](#readme) - [Travis CI](#travis-ci) - [pkgdown](#pkgdown) - [What's Next?](#whats-next) --- name: presentation-goal # Presentation Goal The goal of this presentation is to serve as a quick start guide to making a package by bring together multiple sources of amazing information and tutorials to show how easy it is to not only create a package, but to integrate automated building, testing, and web documentation by utilizing pre-built R packages. I hope this inspires others to create and contribute to packages that are used daily in R. ## Goals - General understanding of packages - Follow methods from Hadley's [*R Packages*][r-pkg-book] book for setting up your envinroment and package creating methods - Hosting on github - Automate build testing - Code coverage - Generate a package webpage with `pkgdown` - Automate `pkgdown` site with **Travis CI** --- name: prerequisites # Prerequisites - It is recommended that you insure R is up to date on your device by downloading the most recent version [here](https://www.rstudio.com/products/rstudio/download/preview/) - In a new R session install these packages: ``` r install.packages(c("devtools", "roxygen2", "testthat", "knitr", "pkgdown")) # get the development versions from github devtools::install_github("r-lib/devtools") devtools::install_github("r-lib/usethis") ``` #### C compiler - Check if you have by running this in your console: ``` r devtools::has_devel() ``` - If `FALSE` is returned, follow section 1.3 in Hadley's book [*R Packages*][r-pkg-getting-started] for assistance in getting everything set up #### Git - To host the package you will need Git and GitHub setup with RStudio - Follow Chapter 14.2 "Initial set up" of [*R Packages*][git-setup] if you do not have this already --- name: what-is-a-package # What is a Package? - Think of a package as a collection of information that makes your life easier - Packages are often thought of as a collection of functions with help documentation. - However, packages may also include datasets. - All of the information in a package once loaded is stored in a nice virtual library, which is why you call: library(package_name). In essence, you are adding the tools to your temporary library. --- name: create-package # Creating a Package With **usethis** ``` r library(usethis) create_package("~/path/to/directory") ``` .pull-left[ - Choose a file path you want your package to be saved - `create_package("~/desktop/samplepackage")` where `samplepackage` is the name of your package - The package name should be lowercase and have no spaces, underderscores, or hyphens - Run the code in the RStudio console - `create_package` will: - Create the directory - Create the basic file structure - Creates .Rproj file for project work flows - Switch your RStudio to the project made for your package - `.gitignore`: anticipates Git usage and ignores some standard, behind-the-scenes files created by R and RStudio ] .pull-right[ - `.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 - `DESCRIPTION`: provides [metadata about your package][r-pkg-metadata] - [`NAMESPACE`][r-pkg-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 - `R/`: directory is the [“business end” of your package][r-pkg-r]. It will soon contain .R files with function definitions. - `YOURPACKAGE.Rproj`: is the file that makes this directory an RStudio Project ] --- name: first-function # First Function - For this example we will make a simple `is_even()` function for demonstration purposes - If you have created a package before you may be acustomed to creating a new script, writing the code, and then saving it inside the `R/` directory - With the package `usethis` we can follow a smoother flow - Type `use_r("is_even")` into the console - This will Create a .R file called `is_even.R` for us inside the `R/` directory - `use_r()` Takes care of naming and saving the script into the correct directory for us! - We will define our function in the .R script that has been made: ```r is_even <- function(x) { x %% 2 == 0 } ``` --- name: test-functions # Testing Functions Within a Package - How can we efficiently test our functions for our package? - Avoid running the function locally, instead we can use `load_all()` to experiment with our new function - This will automatically save your .R files if they have not been saved previously, this ensures you are locally testing what you have coded - Note that `load_all()` has made the `is_even()` function available, although it does not exist in the global workspace - `load_all()` simulates the process of building, installing, and attaching the foofactors package; this is much faster than building, installing, and attaching the package - Now we can type `is_even(10)` into our console to test our package - We can see our function is from our new package! 🥳 .center[<img src="https://i.imgur.com/q72GBKc.png" width="40%"/>] --- name: check-it # *check()* it! - Now that we know our `is_even()` function is working as expected, how can we check if our package as a whole will work? - `check()` is the gold standard for checking that an R package is in full working order - Make a habit to run `check()` after each change to *check* if your package is producing any warnings, errors, or notes - `check()` will produce a lot of out put, generally the part of the output we are interested in will be the ending that displays: ``` r 0 errors ✔ | 2 warnings ✖ | 0 notes ✔ ``` - If you have been following along your package will have these warnings - Whenever there is an error, warning, or note, the output above this text will show you why these statuses were generated - `Non-standard license specification: What license it uses` - `Undocumented code objects: ‘is_even’` - These two warnings will be addressed soon - We have made a lot of progress, next we will edit the `DESCRIPTION` file and push our progress to GitHub --- name: description # Editing the **DESCRIPTION** File - The `DESCRIPTION` file provides metadata about your package - For more information look at chapter 5 [metadata about your package][r-pkg-metadata] - In the `DESCRIPTION` file edit these: - Make yourself the author - Write some descriptive text in the `Title` and `Description` fields ``` Package: samplepackage Title: Is That Number Even? Version: 0.0.0.9000 Authors@R: person(given = "Kyle", family = "Harris", role = c("aut", "cre"), email = "koderkow@gmail.com") Description: There are a few times in ones programming life where one needs to find out if a number is truly even. This package unlocks the puzzling matter that is even numbers. License: What license it uses Encoding: UTF-8 LazyData: true ``` --- name: license # Adding a License - Hadley provides a fantastic link that summarizes the different kinds of software licenses: [Pick a License, Any License][pick-a-license] - We will set up a MIT license - Simple and permissive license - Includes generic legal disclaimer of liability - `DESCRIPTION` requires: `License: MIT + file LICENSE` - The `+ file LICENSE` also requires an additonal file, `LICENSE` - This file generally has `YEAR:` and `COPYRIGHT HOLDER:` fields - Lucky for us, `usethis` has a function that will set this up for us - Run `usethis::use_mit_license("FIRST-NAME LAST-NAME")` in the console - This will create the files for MIT and update your `DESCRIPTION` file for you - `usethis` will print out to the consoles what it is adding and/or changing to files in your package directory - This addresses the `Non-standard license specification: What license it uses` warning we received from `check()` earlier --- name: setup-git # Setting up Git - Run `usethis::use_git()` in the console - This will initialise your project as a Git repo - `use_git()` will also automatically ask us if we want to commit our current files - Here type the number corresponding for "*I agree*" to commit them all by hitting the enter key - Next within the console it will ask to restart Rstudio to activate the Git pane - Type the number corresponding for "*Yes*" and hit the enter key --- name: setup-github # Setting Up GitHub - Now we will give RStudio the power to create GitHub repositories for us! - Run `browse_github_token()` in the console - This will open your default browser and bring you to the "New personal access token" (PAT) page - Scroll to the bottom and click <img src="https://i.imgur.com/ZVvVhOw.png" width="7%"/> - Now you have a PAT generated for RStudio, select <img src="https://i.imgur.com/GGzWB50.png" width="1.5%"/> to copy it to your clipboard - Type `edit_r_environ()` to open the `/.Renviron` file - Create a new line and enter `GITHUB_PAT="PASTE-YOUR-COPIED-PAT-HERE"` - Save and reset R (Session > Restart R) - Run `use_github()` in the console and choose the **https** option and then choose the option corresponding to **Yes** for *Are title and description ok?* - If everything was done correctly your package is now in your GitHub! - Moving forward for other projects using git you can use follow the `use_git()` followed by `use_github()` to create repositories for each project! Nice! - Notice that the `Title:` text we typed in the `DESCRIPTION` file is used as our description on our GitHub page - In a few more slides we will see how we can commit and push changes to our GitHub repo --- name: document # Documenting Functions - When we need help learning more about functions we use we look to its documentation to help us - Example: `?mean()` - This requires that your package have a special R documentation file, `man/is_even.Rd` - This is written in an R-specific markup language that is sort of like LaTeX - Following the flow of what we have done, there are functions made to make our life easy for documenting our functions - Specially formatted comments are written above functions for documentation - In this example this will be placed in `R/is_even.R` - A package called `roxygen2` will handle creating the documentation files in `man/` for us - More information about `roxygen2` can be found in chapter 6 [Object documentation][roxygen2] .pull-left[ - Open `R/is_even.R` and put the cursor somewhere in the `is_even()` function definition - In the menu bar at the top of the screen go to <br> `Code > Insert Roxygen Skeleton` - As the command states, this is only the skeleton. We will need to fill in the rest manually as seen in the image below ] .pull-right[ <img src="https://media.giphy.com/media/hWLRtkEeU6eAVwVu8W/giphy.gif" /> ] --- # Documentation Example ``` r #' Is this value even? #' #' Reveals if x is even, where x is a numeric value #' #' @param x numeric value #' #' @return boolean #' @export #' #' @examples is_even <- function(x) { x %% 2 == 0 } ``` - Now we need to tell RStudio to convert our roxygen comments into `man/is_even.R` by running `devtools::document()` - Hotkeys: Ctrl + Shift + D (Windows & Linux) or Cmd + Shift + D (macOS) --- # Documentation Example - This will update `DESCRIPTION` and `NAMESPACE` with the neccessary information to make our function work with the package - `DESCRIPTION`: added `RoxygenNote: 6.1.1` - `NAMESPACE`: added `export(is_even)` - This means we want our package to make our function available to users when they use `library(samplepackage)` - This has also created a new folder, `man/` - This is the folder where our documentation for function will go - When users run `?is_even()` this is the file that is brought up - Whenever you change a function be sure to update the roxygen2 comments - You will need to run `devtools::document()` each time! - Follow this process for every function you create for your package - This addresses the `Undocumented code objects: ‘is_even’` warning we received from `check()` earlier - Lets run `check()` again to see if we fixed the warnings - If everything was done correctly we should have checkmarks across the board! - This is a great time to commit our changes to GitHub --- name: commit-and-push # Commit and Pushing Changes <center> <video autoplay loop muted inline width="450"> <source src="https://i.imgur.com/9hn3GsO.mp4" type="video/mp4"> </video> </center> - In the **Environment** pane select the **Git** tab and select **Commit** 1. Select all of the boxes to the left of your files 2. Write a commit message summarizeing your updates - The gif above is a simple example - It is good to describe all the additions and changes - You can also commit each file seperately with its own message 3. Hit the **Commit** button 4. After the commiting is done close the box and select **Push** - Check your GitHub repo online to confirm the code has been updated --- name: automate-testing # Automate Testing of Functions - Earlier we informaly tested `is_even()` with `load_all()`, as we create more functions and make them more robust we will look to unit tests to simplify this process - What does this mean? We will declare what we expect our function to do, this will ensure our functions acting accordingly - To declare we want to use these testing methods with our package we will run `use_testthat()` - Three things happened with this function call 1. Added the `testthat` library to *Suggests* in the `DESCRIPTION` - More on this soon! 2. Created a directory `tests/testthat/` 3. Adds the script `test/testthat.R` - Now we need to write the test(s) for our `is_even()` function - Two ways to begin this process: 1. In RStudio have `is_even.R` open so you can see the function and run `use_test()` 2. Or run `use_test("is_even") - This will create `tests/testthat/test-is_even.R`, our script for creating tests --- # Writing Tests for *is_even.R* - Put this in the created `test-is_even.R` script: ``` r test_that("this will return TRUE", { expect_true(is_even(10)) }) test_that("this will return FALSE", { expect_false(is_even(11)) }) ``` - Due to the simplicity of our function we only need a few simple tests - First parameter of `test_that()` is `desc`, the name of the test - Next we put the code for testing - There are many different `expect_*` functions that make testing easy - For each parameter there should be a test to make sure we are covering every possilbity of our functions - This will align with **Code Coverage** testing that we will go over soon --- # Running the Tests - It is encouraged to run these tests locally to make sure they are not returning errors - Make sure you have ran `library(testthat)` or `load_all()` to test the function - Try to purposely make `is_even()` fail so you are familiar with the output of testing - Be sure to change the test back to the correct method if you do this! - Run `test()` to run all tests that have been made for the package - Hotkeys: Ctrl + Shift + T (Windows & Linux) or Cmd + Shift + T (macOS) - For `is_even()`, you should get `OK: 2`, one for each one of the tests we made - Now would be a good tie to go through and practice the developing flow 1. document() 2. check() 3. test() 4. Commit and push --- name: importing-functions # Importing Other Functions - When we write our functions we may use another libraries functions - Such as the pipe, `%>%`, from magrittr - We needed `NAMESPACE` to **export** is_even(), we will need to **import** other packages and functions from their namespace - Run `use_package("magrittr")` to enable access to the pipe, `%>%` - This wiill add `magrittr` to the `DESCRIPTION` file - Lets add a second function to the package to demonstrate the structore of a function that uses other packages, `use_r("evens_only")` - There are multiple methods for correctly importing functions - If you are only using one function from a function use `library::function` - This will make easier to keep track of other functions - Adding `#' @importFrom magrittr, %>%` to the roxygen comments is also an option - If you are using multiple functions from a package use `#' @import library` in the roxygen comments --- # **evens_only()** ``` r #' Return Evens #' #' Given a vector of numbers, this will allow the user to see the even numbers only #' #' @import magrittr #' @param x vector of numeric values #' #' @return even values #' @export #' #' @examples evens_only <- function(x) { x[x %>% is_even()] } ``` - I strongly recommend to read more on importing other functions if this is an area you come across - Chapter 9 [Namespace][r-pkg-namespace] - Make sure you run `document()` to create new documentation for the function - Or if you are curious, run `check()` to see the error - Getting familiar with errors is a great learning experience --- name: readme # Create a README - We now have a package on GitHub, it would be nice to explain our package in depth with code examples - This is where the README comes in - For a simple example check out my readme for my package I am using to learn [here][koderkow] - Run `use_readme_rmd()` - This will create a README.Rmd file - Adds lines to `.Rbuildignore` so package building won't throw a warning - The README.Rmd will throw an warning if it is not ignored due to it being an exepcted file type in the main directory - Adds a hook to the .md and .Rmd to ensure they are always up to date with one another - This will remind you if you forgot to knit the .Rmd - There are helpful sections included in this skeleton - Describe the purpose of the package - Code to install your package - The default is to install from CRAN, I suggest switching this to `devtools::install_github("YOUR-USE-NAME/REPO-NAME")` - A section to show a bit of usage - These can be populated with our previous work done - Copy and paste information from the `DESCRIPTION` file - Show code examples from start to finish, reproducibility is important! - Knit the .RMD and verify the README looks as expected --- # What's Next? - If everything is set to go, commit and push - You now have your own package with documentation on GitHub! - The next sections will go over setting up automatic build testing for our package, we will start with **Travis CI** --- # Travis CI - If you are familiar with packages on GitHub this may look familiar: <img src="https://i.imgur.com/jrOk8iP.png" width="9%"/> - `build: passing`, what does this mean? - Rewind to the output of `check()`, if there is 1 warning, error, or note this will show as `build: fail:` - This shows users if there are any of these issues in the current version on GitHub - Travis CI (Continuous Integration) is essentially doing a `check()` for us every commit and making sure it can be successfully installed on others machines - If your package is open source and not private then Travis CI will be free for you --- name: travis-ci # Setting up Travis CI - Head to their website [travis-ci.org][travis-ci] - Hit the "Sign In with GitHub" button - Authorize and give permissions that Travis CI is asking for - You may land on a page with "GitHub Apps Integration" on it - Hit the "Activate" button - You may also be taken to GitHub to approve Travis CI with your repos - Make sure "All repositories" is selected and hit "Approve & Install" at the bottom - In the RStudio console run `use_travis()` and your browser will open to the Travis CI site - This creates the needed `travis.yml` for you with the specific code needed to make sure Travis runs correctly - This function call also adds the `build: pass/fail` badge to our README.Rmd for us - Here "switch on" the name of your package/repo to enable build testing .center[<img src="https://i.imgur.com/4pBClQ7.png" width="70%"/>] - Go back to RStudio and knit the .Rmd then commit/push the related README files and `travis.yml` - With this commit, it will trigger the first build on Travis CI! --- # Travis CI Notes - You can follow along with Travis CIif you want to see what goes on during the process by click on your repo while it says it is building - This log will always be available no matter what status the build ends with - If the build passes or fails this will automatically update your badge on GitHub - Head to your repo and check out the README! - If the build fails check out the log and inspect where it failed - Make sure `check()` returns no wanrings, errors, or notes! These will trigger Travis CI to fail - Make sure to use `devtools` and `usethis` to create files for the packages, it will automatically write and update files that need to be ignored for us --- name: pkgdown # Build a Website with *pkgdown* - "pkgdown is designed to make it quick and easy to build a website for your package. You can see pkgdown in action at https://pkgdown.r-lib.org" - Run `use_github_links` to update your description file with the link to your source code and issues for GitHug - Having this in the description will allow *pkgdown* to write these links on the website - To build a website for the package, run `build_site()` - That's it! After the function runs your main browser should open up with a preview of your site! - The default view is the README we made earlier - This presentation will not go over README's in depth, be sure to view other README's to get an idea of their structure and what to cover! - On the right side of the screen we can see the associated GitHub links to our package, the packages license, the developers (that's you!), and build status - Now it is time to commit and push up all of these changes to the GitHub repo - On the GitHub repo page go to **Settings** and scroll down to **GitHub Pages** - Change the **Source** to *master branch/docs folder* - Now under **Source** there will be a URL that is the website representation of your package! --- name: whats-next # What's Next? - I would recommend that you make note of frequent actions you take in coding - If you see yourself doing something frequently, make it a function in your package! - There is a good chance others come across this as well and would appreciate a package that reduces the repetitive code that could be possibly happening - There are more package flows out there! - [tic](https://github.com/ropenscilabs/tic): - Takes care a lot of package creating steps for you - This includes Travis CI and other CI flows - Option to automatically update the **pkgdown** site when a push to GitHub is made! - I used tic to create my [kowr](https://github.com/KoderKow/kowr) package - [golem](https://github.com/ThinkR-open/golem): - Build a **Shiny** app in a package - Functions take care of a lot of the work - Test the **Shiny** app with **testthat** - I am sure there is more out there and I hope this lights a firey passion in you to continue exploring packaing building with R! --- # Thanks! Big thanks to Logan Harris (@lharris4211998) for testing the flow and constributing to the presentation! .pull-left[ ### Source Code for Slides - https://github.com/KoderKow/intro-to-neural-networks ] .pull-right[ ### Slides Made with Xaringan - [R Package: Xaringan](https://github.com/yihui/xaringan) ] <!-- links used in slides --> [r-pkg-book]: https://r-pkgs.org/ [git-setup]: https://r-pkgs.org/git.html#git-rstudio [r-pkg-getting-started]: https://r-pkgs.org/intro-why.html#intro-get [r-pkg-metadata]: https://r-pkgs.org/description.html#description [r-pkg-namespace]: https://r-pkgs.org/namespace.html#namespace [r-pkg-r]: https://r-pkgs.org/r.html#r [git-site]: https://github.com [pick-a-license]: http://blog.codinghorror.com/pick-a-license-any-license/ [roxygen2]: https://r-pkgs.org/man.html [koderkow]: https://github.com/KoderKow/koderkow [travis-ci]: https://travis-ci.org/