# exRcises - 2023-09-20 R packaging ## Week 1 ### Getting started #### Exercise: edit the function, and reload the package. The default package generates `Hello, world!` when the only function`hello()` is called. 1. Change this function to generate a different output. (Have fun with it, perhaps add an argument or two!) 2. Build and install the package, and call the function again to see that it works differently. #### Exercise: tell me how you load your functions? 1. How do you usually work with (your own) functions? A. Do you source them from an external file? B. Do you usually work on a single, long script that includes the functions? C. .... other (please share!) 2. What would be the advantages of using a package instead? ### Accessing packages #### Exercise: Install a package from a repository Find an R package on a public repository, such as GitHub or GitLab, and install it with devtools. If you have trouble finding a package, you can try: From GitHub: github.com/Lchiffon/wordcloud2 From GitLab: gitlab.com/r-packages/psyverse For an extra challenge: try to install them using different functions. #### Exercise: To attach or not to attach Your package will likely use functions from other packages. As you call functions from the other packages, you should use the syntax `<package>::<function>`, and not `library(<package>)` followed by `<function>` inside a package. Do you have any idea why? Write your idea below. If there are reasons listed by others that you think are important, add a :thumbsup: to them. ### Writing our own functions #### Exercise: Open the `DESCRIPTION` file. What do you see here? Take 5 minutes to edit this file with the information it asks. In particular, edit the following fields (when needed): Title, Version, Author, Maintainer, Description. For now, ignore the rest. ### Licenses #### Exercise: what license does your favorite package have? Use `packageDescription("myFavoritePackage", fields = "License")` to check the license of (a) package(s) you like to use. What licenses do you find? #### Exercise: look at licenses on [choosealicense.com](https://choosealicense.com/). What license would you choose? What is important to you? #### Exercise: change the license of your package. 1. Change the license of your package to Apache, using `usethis::use_apache_license()`. Look at the `DESCRIPTION` and `License` files and see how they have changed. 2. Change the license of your package to the license you want to use. What function did you use? ### Data #### Exercise: adding data 1. Create an object that you want to include in your package (for example, a vector of names) 2. Save the object as data in your package 3. Remove the object from your environment (you can use the Rstudio user interface for this, or the function `remove()`) 4. Install and reload your package. Confirm that you can still access your object! Share your code below. #### Exercise: adding raw data 1. Use an existing raw data file (an excel, csv, or other file), or create one (for example, save the content below as `names.csv`) ``` name,age,function anders,28,sales bert,44,hr christine,61,ceo dina,28,manager emma,44,sales frank,30,logistics ``` 2. Save the file in your package in `./inst/extdata` 3. Install and reload the package. 4. Confirm that you can access the file: ``` filepath <- system.file("extdata", "names.csv", package = "mysterycoffee") names <- read.csv(filepath) ``` Share a :thumbsup: when you're done, or paste your error message below. #### For the note-taker Summary of data use in a package: ![](https://hackmd.io/_uploads/HJpHq_vJp.png) `*`) `R/sysdata.Rda` is a file dedicated to (larger) data needed by your functions. [Read more about it here](https://r-pkgs.org/Data.html#sec-data-sysdata). `**`) `data-raw/` is a folder dedicated to the origin and cleanup of your data. [Read more about it here](https://r-pkgs.org/Data.html#sec-data-data-raw). ### Homework after week 1 Create a package using your own R project, if you have one (see the last point below if you do not have one). Create functions in the R folder of that package. Remember the following: * When you are using external functions inside your package, add double colons `::` before calling that function * If you start from a script, you can keep the leftovers (if there are any after separating out functions) in a script that calls your package's functions and runs your workflow. * It is very good if **at least one of your functions gives an output** (so, it doesn't just "do" something, like print `Hello World`): it actually creates a data frame, or a vector, or an object of some other kind! We will see why next week. * Don't forget through `Install (and Restart)` whenever you change anything. * If you are creating your own package, don't forget to update the `DESCRIPTION` file. * If you are not using your own project, take some time to improve the current `make_groups` function and perhaps add another function. Don’t worry if you run into issues, this is expected. See if you can fix them, but don't be afraid to bring them next week, so we can solve them together. ## Week 2 ### Testing #### Exercise: what happened? Run `usethis::use_testthat()` in the console, and take a look at its output. Also take a look at your folder system (press the refresh button if needed). What happened? #### Exercise: what does this test do? Open a newly created testfile (after running `usethis::use_test(<function>)`, you will find it in `/tests/testthat/`) and take a look at its contents. It should contain something like this: ```r test_that("multiplication works", { expect_equal(2 * 2, 4) }) ``` This is just an example test, that was created to make your life easier. You can use it as a template for writing your own tests. Take 10 minutes to: - Figure out what is happening inside this test. Can you understand it? - Write another test, just below this one, that checks that addition also works. Tip: you can copy-paste and edit the current test. #### Exercise: write your own test. Now it is your turn to write a test. With a vector of 6 names as input, check that the output of `make_groups()` is a matrix with 3 rows and 2 columns. Tip 1: tests can contain multiple assertions. Tip 2: the functions `nrow` and `ncol` may come in handy. #### Exercise: test an uneven number. Write a new test that passes an uneven number of names to `make_groups`. Run the test again. What happened? #### Exercise: Write a test using three different `expect_` functions. Write one or multiple tests, based on a function of your choice, in which you use three different assertion types. For instance, `expect_error`, or `expect_length`. See [the documentation](https://testthat.r-lib.org/reference/index.html) for a list of functions. ### Test-coverage #### Exercise: Check and increase your test coverage If you have not installed the `covr` package, do so now: `install.packages("covr")`. You may also need to install `DT`: `install.packages("DT")` 1. Make a coverage report with `covr::report()`. What is your coverage percentage? 2. Look at the coverage of an individual function. Does the coverage percentage make sense to you? 3. Write a test for a part of the function that you think is not yet covered. Re-run `covr::report()`. Did you increase your coverage? ### Homework after week 2 In this coming week, we would like you to apply the things you learned today to your own package. Start with the following steps: 1. Setup test infrastructure for your package with `usethis::use_testthat()` 2. Create a test using `usethis::use_test("test-context")` Then, please write tests for one or more of your functions. You can do this as you see fit. For example: - Write multiple tests for a single function, to test that the function deals well with multiple kinds of input. Remember you can put multiple assertions in a single test (an assertion is stating an expectation, e.g. `expect_equal`, `expect_true`). - Check if your function produces specific error messages (using e.g. `expect_error`). - Write a test for (part of) your workflow, in which multiple functions are used (remember that this is called *integration testing*). - Use `covr::report()` to identify parts of your package that could benefit from more tests. As you do this, this may be useful: - Testthat documentation (including a description of all expect_ functions): https://testthat.r-lib.org/reference/index.html Finally, and most importantly, do continue to develop the functions in your package. You can do this together with newly developed tests: your tests can help you confirm that functions still work as expected while you edit (refactor) them. ## Week 3 ### Dependencies #### Exercise: Add dependencies to the description file, using `usethis::use_package()`. For practice only: - Place one package in Imports, and one in Suggests. If you don't know which one, or you wish to find inspiration, here's a list of all packages on CRAN: https://cran.r-project.org/web/packages/available_packages_by_name.html #### Exercise: Specify a minimum version to your dependencies 1. run `?usethis::use_package` and find out how to specify a minimum version required for your package, i.e., which argument you need to use within `usethis::use_package`? 2. specify a version number, e.g., "3.0.0". 3. EXTRA: Rather than specifying a number, try to set it to `TRUE` and report what has happened. #### Exercise: run 'Check' 1. Run the 'Check' function (either with the button, or with `devtools::check()`). 2. Look for information about your dependencies in the resulting text. Are any dependencies missing, or declared dependencies unused? 3. How are you going to solve the problem? Please take some minutes to try to solve this problem by yourself. ### Documentation #### Exercise: add a README Use `usethis::use_readme_md()` or `usethis::use_readme_rmd()` to generate a template README file. (What is the difference? Which one has your preference?) Filling this file with information is going to be very important! However, we will not do this during the class. You can take some time to do this during the week, as part of the homework. #### Exercise: Edit your documentation entry Take 5 minutes to edit the skeleton with real documentation. In particular: 1. Add a title. 2. Describe, below the title, what the function does. 3. Describe the parameter names. 4. Describe the output that is returned. 5. Ignore @export. 6. Delete @examples. Share your documentation skeleton, including the first line of your function (namely: `functionName <- function(param1, param2){`), below. #### Exercise: document your data 1. Create a new R file called `data.R` using `usethis::use_r("data")` (`data.R` is an example; you may call this whatever you want). 2. In this file, document the data object, using this example: ``` #' Title #' #' A short description. #' #' @format What format is the data in? #' @source Where did it come from? \url{https://google.com} "object_name" ``` 3. Call `devtools::document()` to generate the documentation file and add this data to the package namespace! 4. Install and verify that your documentation can be accessed with `?data` (or `?anothername` if your data object is called differently). Raise your hand when you are done! ### Homework after week 3 As you perhaps already expect, the homework for this week is… write documentation for your package! Some additional tips and points of attention: - Make sure you are clear on what functions are for the user — label them with `@export` — and what functions are only used internally. - For the exported functions: insert a Roxygen skeleton (Code > Insert Roxygen Skeleton), and fill it out. Do explore the Roxygen documentation (https://roxygen2.r-lib.org/) and experiment with different tags. - After documenting the package (e.g. with `devtools::document()` or `roxygen2::roxygenise()`), explore what the help pages for your functions look like, by pulling them up as you normally would with `?function_name`. Adjust as necessary. Aside from the Roxygen documentation, do write a README.md file with some basic information about your package! This will be the first thing potential users see, so what do you want them to know? How can they get started? (Note that you do not need to add the installation information yet, we will do this together next week.) Finally, make sure the dependencies of your package are clearly stated in the DESCRIPTION file. Note also that running a Check will only check your code for functions that are labeled with `packagename::function_name()`, and it will not detect any functions from external packages if they are not labeled correctly. Go through your code to be extra sure that functions are called correctly and you are not missing dependencies! Remember that next week we will share our respective packages. Your package will not be complete and that is completely expected and perfectly OK. However, it would be nice if your package has at least one function that works and is well documented! ## Week 4 ### Vignettes #### Exercise: explore vignettes Take some time to explore the vignette(s) of your favorite package(s). What things do you notice? What properties of vignettes stand out to you? Share them below! #### Exercise: add a vignette. - Use `usethis::use_vignette(vignette-name)` to create a vignette fit for your package. - In the .Rmd file that is now created, add a code chunk that uses a function from your package. (Feel free to add some descriptive text, too!) - Preview the vignette with the Knit button. - Create the vignette with `devtools::build_rmd("vignettes/vignette-name.Rmd")` - Verify that the vignette was built: a `.html` file should now appear in the vignettes/ folder. ### Share your package (final exercise) As the final exercise today, we will share the packages we created with each other. - On your GitHub profile, add a new repository (with the name of your package). - Upload the entire package to the repository. - Ensure your README file contains: - Instructions to install the package: `devtools::install_github("username/repository")` will install the package from github. - Note that if there are vignettes, you need to request their installation explicitly: `devtools::install_github("username/repository", build_vignettes = TRUE)` - A first function or small script to run (or: a link to the Vignette that contains this). - Place the link to the package in the table below. - Find a package from another user, install it and run their example. Comment BELOW the table (otherwise it will become a mess). #### Packages | Name | Link to repository | |:--|:--| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | #### Comments from users ##### [package name](https://github.com/package_link) - - - ##### [package name](https://github.com/package_link) - - - ##### [package name](https://github.com/package_link) - - - ##### [package name](https://github.com/package_link) - - - ##### [package name](https://github.com/package_link) - - - ##### [package name](https://github.com/package_link) - - - ##### [package name](https://github.com/package_link) - - - ##### [package name](https://github.com/package_link) - - - ##### [package name](https://github.com/package_link) - - -