|
| 1 | +--- |
| 2 | +date: 2025-02-10 |
| 3 | +authors: |
| 4 | + - SaranjeetKaur |
| 5 | +categories: |
| 6 | + - Technology |
| 7 | +tags: |
| 8 | + - R |
| 9 | + - Package |
| 10 | +--- |
| 11 | + |
| 12 | + |
| 13 | +# **Building an R package using {fusen}** |
| 14 | + |
| 15 | +{: style="display:block;margin:auto;width:69%" } |
| 16 | + |
| 17 | +Writing your first full R package can feel overwhelming and {fusen} |
| 18 | +can help support at this stage (Even if you are an experienced developer, there is something for you too in this blog. Please read on!). "Fusen" is a type of |
| 19 | +[Japanese origami](https://en.wikipedia.org/wiki/Kamif%C5%ABsen) in which |
| 20 | +a flat piece of paper, when folded in a specific way and inflated, turns |
| 21 | +into a nice paper box/balloon. Similarly, the {fusen} package inflates a |
| 22 | +flat `.Rmd` template (which is filled in a specific way) and returns a |
| 23 | +nice package. In this blog post, I am sharing my experience of |
| 24 | +exploring {fusen} for the first time. |
| 25 | +<!-- more --> |
| 26 | + |
| 27 | +## Pre-requisites |
| 28 | + |
| 29 | +- [RStudio installed](https://posit.co/download/rstudio-desktop/) |
| 30 | +- [Connect RStudio to Git and GitHub](https://happygitwithr.com/rstudio-git-github.html) (_Optional, but recommended_) |
| 31 | + |
| 32 | +## Installation and initial setup |
| 33 | + |
| 34 | +The CRAN released version of {fusen} can be installed using: |
| 35 | + |
| 36 | +``` r |
| 37 | +install.packages("fusen") |
| 38 | +``` |
| 39 | + |
| 40 | +Then we will have to create a new new project in RStudio by following: |
| 41 | +`File > New Project > New directory > Package using {fusen}`. |
| 42 | +This will open a `Create Package using {fusen}` wizard. |
| 43 | +Specify the directory name and choose a {fusen} template to work with. |
| 44 | +If you want to see how {fusen} works then select the `teaching` template |
| 45 | +(recommended when using for the first time), else select the `full` template. |
| 46 | +You can also provide a name for the flat file that is to be generated. |
| 47 | +Check `Create a git repository`, `Open in new session` and |
| 48 | +finally click on `Create Project` to create the initial structure. |
| 49 | + |
| 50 | +## Convert a `.Rmd` file into a package |
| 51 | + |
| 52 | +Following the installation and initial setup mentioned above |
| 53 | +(Select the `teaching` template and default name (`first`) for the flat filename. |
| 54 | +For the directory name, I am using `my.teaching.fusen.package`) |
| 55 | +will open a new RStudio session with an initial structure as seen in the image below: |
| 56 | + |
| 57 | +{: style="display:block;margin:auto;width:69%" } |
| 58 | + |
| 59 | +Since a new RStudio session is open, you will have to install {fusen} again (using `install.packages("fusen")`) in this session to be able to use its functionalities in the new session. |
| 60 | +Navigate to `dev/flat_first.Rmd` flat file and observe the different chunks present in it. |
| 61 | +The chunks in the flat file help to set up your package: |
| 62 | + |
| 63 | +- `description`: In this chunk, you will add metadata about your package (for example, package author, package license, etc.). |
| 64 | +- `development`: This chunk can be used to write code for development purposes (we will not be using this for now). |
| 65 | +- `function`: In this chunk, you will write code for a function in your package. |
| 66 | +- `examples`: Here you would add examples of using the function which will become a part of the |
| 67 | +`@examples` field in the corresponding vignette. |
| 68 | +- `tests`: In this chunk, you can add unit tests for your function. |
| 69 | + |
| 70 | +Add the appropriate information (title, description, author(s), email(s), license) in the `description` chunk and run it. It will generate the `DESCRIPTION` and `LICENSE` files for your package. |
| 71 | + |
| 72 | +{: style="display:block;margin:auto;width:69%" } |
| 73 | + |
| 74 | +There is a default `function` chunk, named `add_one` (along with its `examples` and `tests` chunk) present in the flat file. This flat file has the minimal structure required to be transformed into a package using the function `fusen::inflate()`: |
| 75 | + |
| 76 | +``` r |
| 77 | +fusen::inflate( |
| 78 | + flat_file = "dev/flat_first.Rmd", |
| 79 | + vignette_name = "Get started", |
| 80 | + check = TRUE |
| 81 | +) |
| 82 | +``` |
| 83 | + |
| 84 | +After running the above function, if you explore the `Files` pane on RStudio, you will notice that it looks very similar to a package structure. Notice the new directories and files that have been generated: |
| 85 | + |
| 86 | +- `R` directory: It has the `add_one.R` function file, which is generated from `function-add_one` chunk of the flat file. |
| 87 | +- `man` directory: It has the `add_one.Rd` file generated from the `roxygen2` comments in the `function-add_one` chunk. |
| 88 | +- `tests` directory: It has the `testthat/test-add_one.R` generated by the `tests-add_one` chunk of the the flat file. |
| 89 | +- `vignettes` directory: It has the `get-started.Rmd` file which is generated by the `fusen::inflate()` command. |
| 90 | +- `NAMESPACE` file: It exports the `add_one` function (provided the `@export` field is mentioned in the `roxygen2` comments in the `function-add_one` chunk of the flat file). |
| 91 | + |
| 92 | +{: style="display:block;margin:auto;width:69%" } |
| 93 | + |
| 94 | +At this stage, we have a proper R package structure - if you want to you can [clean up any fusen related files and tags](#clean-fusen-related-files-and-tags) from it and can also [publish it on GitHub](#share-package-on-github)! |
| 95 | + |
| 96 | +You can also add new family of functions to your package using new flat template files. I will add two more functions (`squared` and `is_even`) using: |
| 97 | + |
| 98 | +``` r |
| 99 | +fusen::add_flat_template( |
| 100 | + template = "additional", |
| 101 | + dev_dir = "dev", |
| 102 | + flat_name = "squared", # and later on replace this with "is_even" |
| 103 | +) |
| 104 | +``` |
| 105 | + |
| 106 | +This will create a new flat file template named `dev/flat_squared.Rmd` (and `dev/flat_is_even.Rmd` when the corresponding `flat_name` is used) with the various (empty) chunks that can be populated. The chunks that I used for the respective flat files are given below: |
| 107 | + |
| 108 | +<!-- markdownlint-disable-next-line MD033 --> |
| 109 | +<details> |
| 110 | +<!-- markdownlint-disable-next-line MD033 --> |
| 111 | +<summary>Square of a number</summary> |
| 112 | + |
| 113 | +- `function` chunk to square a number: |
| 114 | + |
| 115 | +```` markdown |
| 116 | +```{r function-squared} |
| 117 | +#' Compute squared value |
| 118 | +#' |
| 119 | +#' @param value A numeric value |
| 120 | +#' |
| 121 | +#' @return Numeric. |
| 122 | +#' @export |
| 123 | +
|
| 124 | +squared <- function(value) { |
| 125 | + result <- value^2 |
| 126 | + return(result) |
| 127 | +} |
| 128 | +``` |
| 129 | +```` |
| 130 | + |
| 131 | +- `examples` chunk to square a number: |
| 132 | + |
| 133 | +```` markdown |
| 134 | +```{r examples-squared} |
| 135 | +squared(10) |
| 136 | +squared(73) |
| 137 | +``` |
| 138 | +```` |
| 139 | + |
| 140 | +- `tests` chunk to test the function: |
| 141 | + |
| 142 | +```` markdown |
| 143 | +```{r tests-squared} |
| 144 | +test_that("squared works", { |
| 145 | + expect_equal(squared(10), 100) |
| 146 | + expect_equal(squared(73), 5329) |
| 147 | +}) |
| 148 | +``` |
| 149 | +```` |
| 150 | + |
| 151 | +</details> |
| 152 | + |
| 153 | +<!-- markdownlint-disable-next-line MD033 --> |
| 154 | +<details> |
| 155 | +<!-- markdownlint-disable-next-line MD033 --> |
| 156 | +<summary>Is a number even?</summary> |
| 157 | + |
| 158 | +- `function` chunk to check if a number is even: |
| 159 | + |
| 160 | +```` markdown |
| 161 | +```{r function-is_even} |
| 162 | +#' Check if a value is even |
| 163 | +#' |
| 164 | +#' @param value A numeric value |
| 165 | +#' |
| 166 | +#' @return Logical. TRUE if value is even, FALSE otherwise |
| 167 | +#' @export |
| 168 | +#' |
| 169 | +
|
| 170 | +is_even <- function(value) { |
| 171 | + result <- value %% 2 == 0 |
| 172 | + return(result) |
| 173 | +} |
| 174 | +``` |
| 175 | +```` |
| 176 | + |
| 177 | +- `examples` chunk to check if a number is even: |
| 178 | + |
| 179 | +```` markdown |
| 180 | +```{r examples-is_even} |
| 181 | +is_even(20) |
| 182 | +is_even(47) |
| 183 | +``` |
| 184 | +```` |
| 185 | + |
| 186 | +- `tests` chunk to test the function: |
| 187 | + |
| 188 | +```` markdown |
| 189 | +```{r tests-is_even} |
| 190 | +test_that("is_even works", { |
| 191 | + expect_true(is_even(20)) |
| 192 | + expect_false(is_even(47)) |
| 193 | +}) |
| 194 | +``` |
| 195 | +```` |
| 196 | + |
| 197 | +</details> |
| 198 | + |
| 199 | +Inflate each of the two new flat files individually using: |
| 200 | + |
| 201 | +``` r |
| 202 | +fusen::inflate( |
| 203 | + flat_file = "dev/flat_squared.Rmd", # and "dev/flat_is_even.Rmd" |
| 204 | + vignette_name = "Square of a number", # and "Check if even number" |
| 205 | + check = TRUE |
| 206 | +) |
| 207 | +``` |
| 208 | + |
| 209 | +This will update the package to include the two new functions, their unit tests, corresponding vignettes and `.Rd` files, and the `NAMESPACE` (if the functions are exported) in the appropriate locations. |
| 210 | + |
| 211 | +## Clean fusen related files and tags |
| 212 | + |
| 213 | +Now that I have included all the functions that I want in this package, I will clean the package to remove any fusen related files and tags. This step is optional. It can be done using the function `fusen::sepuku()`. It will delete all files starting with `flat` in the `dev/` folder and also remove any fusen configuration file, if present. |
| 214 | + |
| 215 | +## Share package on GitHub |
| 216 | + |
| 217 | +Finally, I will share the package on GitHub using `fusen::init_share_on_github()`. Running this command will start the process of committing the package to GitHub. Follow along the prompts to publish the various files - it will ask you to commit the current state. Eventually, you will be redirected to GitHub where your package will appear (don't forget to return to back to RStudio as the function is still running and there are more prompts waiting for you!). Keep following the prompts and the `gh-pages` branch will be created for you on the GitHub repository. Once the `gh-pages` branch is created, you need to tell GitHub to follow it, by going to `settings > pages` and in the `Branch` drop-down menu selecting `gh-pages` instead of `None` and then clicking `Save`. Wait for a few seconds and refresh the page to see the website link deployed for your package. |
| 218 | + |
| 219 | +!!! Note |
| 220 | + You will notice a `README.Rmd` (and the corresponding rendered `README.md`) on |
| 221 | + GitHub with boilerplate content. Please feel free to update the `README.Rmd` file, |
| 222 | + `knit()` it, and share the updated version on GitHub. |
| 223 | + |
| 224 | +If you want to add more functionality to your package, you can continue working on the it locally, inflate any flat files that you generate, and push to GitHub. |
| 225 | + |
| 226 | +[View my.fusen.teaching.package](https://github.com/SaranjeetKaur/my.teaching.fusen.package) (generated by following the above instructions and hosted on GitHub) |
| 227 | + |
| 228 | +## Takeaways |
| 229 | + |
| 230 | +{fusen} ensures that you are writing the documentation as well as any associated tests at the same time as writing your code (the best research software engineering practice to begin with!). It is not only useful for first time R package developers, but also for more experienced developers, in the sense that they don't have to switch between R files, tests files, and vignettes while they are prototyping their functions. Since all the related chunks at located in the same flat file, it avoids the risks of forgetting any crucial step. If you retain the flat file(s), then it is also easier to review the code, because everything related to a particular function is available in the same file. |
| 231 | + |
| 232 | +## Resources |
| 233 | + |
| 234 | +- [Official documentation of {fusen}](https://thinkr-open.github.io/fusen/) |
| 235 | +- [Comparison: the classical way of building packages v/s the {fusen} way](https://thinkr-open.github.io/fusen/articles/Maintain-packages-with-fusen.html?q=add_flat_template#compare-a-classical-way-of-building-packages-with-the-fusen-way) |
0 commit comments