-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cd8cd27
commit 731da8f
Showing
11 changed files
with
445 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
b807f7ea | ||
42f3f7e9 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,353 @@ | ||
# Entanglement {#sec-entanglement} | ||
|
||
[Tools to avoid [dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)]{style="font-size: 1.40em; font-style: italic"} | ||
|
||
```{r} | ||
#| eval: true | ||
#| echo: false | ||
#| include: false | ||
source("_common.R") | ||
library(pak) | ||
``` | ||
|
||
```{r} | ||
#| label: co_box_dev | ||
#| echo: false | ||
#| results: asis | ||
#| eval: true | ||
co_box( | ||
color = "y", | ||
look = "simple", | ||
header = "Caution", | ||
contents = "The contents for section are being revised. Thank you for your patience." | ||
) | ||
``` | ||
|
||
This chapter covers a few packages and tools to help explore, understand and keep track of your app-package dependencies. It's not likely you'll build an application that *only* relies on `shiny`, so it's important to 1) know the packages and versions required for your application to function, and 2) ensure these packages are included in the correct `DESCRIPTION` field (or `NAMESPACE`). | ||
|
||
## Exploring dependencies | ||
|
||
The first package we'll cover is [`pak`](https://pak.r-lib.org/), which is, "*A Fresh Approach to R Package Installation.*" `pak` includes two tools I've found to be incredibly helpful for understanding the dependencies in a given package (or a package I'm building): dependency trees and the dependency explainer. | ||
|
||
### Trees | ||
|
||
`pak::pkg_deps_tree()` shows us the dependencies for a particular package. To demonstrate how this function works, we'll explore the dependencies in three packages: | ||
|
||
1. [`rlang`](https://rlang.r-lib.org/): *"Functions for Base Types and Core R and 'Tidyverse' Features"* | ||
2. [`lifecycle`](https://lifecycle.r-lib.org/): *"Manage the Life Cycle of your Package Functions"*, and | ||
3. [`vctrs`](https://vctrs.r-lib.org/): *"Vector Helpers"* | ||
|
||
Let's start with the [`rlang` package](https://rlang.r-lib.org/): | ||
|
||
```{r} | ||
#| code-fold: false | ||
#| eval: false | ||
#| echo: true | ||
#| collapse: true | ||
#| comment: "" | ||
pak::pkg_deps_tree("rlang") | ||
``` | ||
|
||
```{verbatim} | ||
#| code-fold: false | ||
#| eval: false | ||
rlang 1.1.1 ✨ | ||
Key: ✨ new | ||
``` | ||
|
||
`rlang` is *"a collection of frameworks and APIs for programming with R"* and it's built with only base R packages (that's why it's `DESCRIPTION` file only `Imports` the `utils` package): | ||
|
||
```{verbatim} | ||
#| code-fold: false | ||
#| eval: false | ||
Imports: | ||
utils | ||
``` | ||
|
||
Now lets look at [`lifecycle`](https://lifecycle.r-lib.org/): | ||
|
||
```{r} | ||
#| code-fold: false | ||
#| eval: false | ||
#| echo: true | ||
#| collapse: true | ||
#| comment: "" | ||
pak::pkg_deps_tree(pkg = "lifecycle") | ||
``` | ||
|
||
```{verbatim} | ||
lifecycle 1.0.3 ✨ ⬇ (123.60 kB) | ||
├─cli 3.6.1 ✨ | ||
├─glue 1.6.2 ✨ | ||
└─rlang 1.1.1 ✨ | ||
``` | ||
|
||
`lifecycle` depends on [`cli`](https://cli.r-lib.org/), [`glue`](https://glue.tidyverse.org/), and [`rlang`](https://rlang.r-lib.org/). | ||
|
||
- [`cli`](https://cli.r-lib.org/): *"Helpers for Developing Command Line Interfaces"* | ||
|
||
- [`glue`](https://glue.tidyverse.org/): "*Interpreted String Literals*" | ||
|
||
|
||
If we look at the `DESCRIPTION` file for `lifecycle`, it also imports `cli`, `glue`, and `rlang` (and specifies versions for `cli` and `rlang`) | ||
|
||
```{verbatim} | ||
#| code-fold: false | ||
#| eval: false | ||
Imports: | ||
cli (>= 3.4.0), | ||
glue, | ||
rlang (>= 1.1.0) | ||
``` | ||
|
||
Finally, lets look at the dependencies in the [`vctrs`](https://vctrs.r-lib.org/) package. The `DESCRIPTION` file for `vctrs` imports `cli`, `glue`, `lifecycle`, and `rlang` | ||
|
||
```{verbatim} | ||
#| code-fold: false | ||
#| eval: false | ||
Imports: | ||
cli (>= 3.4.0), | ||
glue, | ||
lifecycle (>= 1.0.3), | ||
rlang (>= 1.1.0) | ||
``` | ||
|
||
If we check the dependency tree, we see the `cli`, `glue`, and `rlang` are listed twice (once for `vctrs`, and again for `lifecycle`): | ||
|
||
```{r} | ||
#| code-fold: false | ||
#| eval: false | ||
#| echo: true | ||
#| collapse: true | ||
#| comment: "" | ||
pak::pkg_deps_tree(pkg = "vctrs") | ||
``` | ||
|
||
```{verbatim} | ||
#| code-fold: false | ||
#| eval: false | ||
vctrs 0.6.4 ✨ | ||
├─cli 3.6.1 ✨ | ||
├─glue 1.6.2 ✨ | ||
├─lifecycle 1.0.3 ✨ ⬇ (123.60 kB) | ||
│ ├─cli | ||
│ ├─glue | ||
│ └─rlang 1.1.1 ✨ | ||
└─rlang | ||
Key: ✨ new | ⬇ download | ||
``` | ||
|
||
`vctrs` depends on `cli`, `glue`, `rlang`, and `lifecycle` (which also depends on `cli`, `glue`, and `rlang`) | ||
|
||
### Explain | ||
|
||
We can show dependency relationships with `pak::pkg_deps_explain()`. For example, | ||
|
||
*How does `lifecycle` depend on `rlang`?* | ||
|
||
```{r} | ||
#| code-fold: false | ||
#| eval: false | ||
#| echo: true | ||
#| collapse: true | ||
#| comment: "\n" | ||
pak::pkg_deps_explain("lifecycle", "rlang") | ||
``` | ||
|
||
```{verbatim} | ||
lifecycle -> rlang | ||
``` | ||
|
||
*How does `vctrs` depend on `rlang`?* | ||
|
||
```{r} | ||
#| code-fold: false | ||
#| eval: false | ||
#| echo: true | ||
#| collapse: true | ||
#| comment: "\n" | ||
pak::pkg_deps_explain("vctrs", "rlang") | ||
``` | ||
|
||
```{verbatim} | ||
vctrs -> lifecycle -> rlang | ||
vctrs -> rlang | ||
``` | ||
|
||
`vctrs` directly depends on `rlang` and `lifecycle` (which also depends on `rlang`). | ||
|
||
### Depends | ||
|
||
So far we've been including add-on functions to the `Imports` field in the `DESCRIPTION`, which ensures the package is installed with our app-package, but not attached to the search list. However, if we include a package in the `Depends` field, it's installed and attached. | ||
|
||
This is rarely needed, but a great example is the relationship between `devtools` `usethis`: | ||
|
||
```{r} | ||
#| code-fold: false | ||
#| eval: false | ||
#| echo: true | ||
#| collapse: true | ||
#| comment: "\n" | ||
pak::pkg_deps_explain("devtools", "usethis") | ||
``` | ||
|
||
```{verbatim} | ||
devtools -> usethis | ||
``` | ||
|
||
In the `DECRIPTION` file for `devtools`, `usethis` is listed with a version number under `Depends`: | ||
|
||
```{verbatim} | ||
Depends: | ||
usethis (>= 2.1.6) | ||
``` | ||
|
||
|
||
### Case study: `devtools` | ||
|
||
The [conscious uncoupling](https://github.com/r-lib/devtools#conscious-uncoupling) of `devtools` split package development across multiple packages. Let's see how this works, starting with the commonly used `devtools` function `load_all()` | ||
|
||
#### `pkgload` | ||
|
||
`load_all()` is handled by the | ||
[pkgload](https://pkgload.r-lib.org/) package, which "*Simulate[s] Package Installation and Attach*". | ||
|
||
How does `devtools` depend on `pkgload`? | ||
|
||
```{r} | ||
#| code-fold: false | ||
#| eval: false | ||
#| echo: true | ||
#| collapse: true | ||
#| comment: "\n" | ||
pak::pkg_deps_explain("devtools", "pkgload") | ||
``` | ||
|
||
```{verbatim} | ||
devtools -> pkgload | ||
devtools -> roxygen2 -> pkgload | ||
devtools -> testthat -> pkgload | ||
``` | ||
|
||
This relationship shows the three actions that call `load_all()` during package development: | ||
|
||
1. `devtools::load_all()` actually calls `pkgload::load_all()` | ||
|
||
2. `devtools::document()` and `devtools::test()` also call `pkgload::load_all()` | ||
|
||
```{r} | ||
#| code-fold: false | ||
#| eval: false | ||
#| echo: true | ||
#| collapse: true | ||
#| comment: "\n" | ||
pak::pkg_deps_explain("devtools", "roxygen2") | ||
``` | ||
|
||
```{verbatim} | ||
devtools -> roxygen2 | ||
``` | ||
|
||
```{r} | ||
#| code-fold: false | ||
#| eval: false | ||
#| echo: true | ||
#| collapse: true | ||
#| comment: "\n" | ||
pak::pkg_deps_explain("devtools", "testthat") | ||
``` | ||
|
||
```{verbatim} | ||
devtools -> testthat | ||
``` | ||
|
||
## Tracking dependencies | ||
|
||
The following packages will help keep your app-package dependencies managed in the `DESCRIPTION` file and the code below `R/`: | ||
|
||
### `attachment` | ||
|
||
[`attachment`](https://thinkr-open.github.io/attachment/index.html) was introduced in the [`golem` chapter](golem.qmd), but you don't have to use the `golem` framework to take advantage of it's functions. [`att_amend_desc()`](https://thinkr-open.github.io/attachment/reference/att_amend_desc.html) will update the package dependencies in the `DESCRIPTION` file. | ||
|
||
|
||
```r | ||
attachment::att_amend_desc() | ||
``` | ||
|
||
```bash | ||
Saving attachment parameters to yaml config file | ||
Updating moviesApp documentation | ||
ℹ Loading moviesApp | ||
Writing NAMESPACE | ||
Writing NAMESPACE | ||
ℹ Loading moviesApp | ||
[+] 6 package(s) added: cli, tools, fst, ggplot2movies, glue, waldo. | ||
``` | ||
|
||
`attachment::att_amend_desc()` will automatically create a [`dev/` folder](https://github.com/mjfrigaard/moviesApp/tree/17_code-tools/dev) with a YAML configuration file: | ||
|
||
```bash | ||
dev | ||
└── config_attachment.yaml | ||
|
||
1 directory, 1 file | ||
``` | ||
|
||
`config_attachment.yaml` contents: | ||
|
||
```yaml | ||
path.n: NAMESPACE | ||
path.d: DESCRIPTION | ||
dir.r: R | ||
dir.v: vignettes | ||
dir.t: tests | ||
extra.suggests: ~ | ||
pkg_ignore: ~ | ||
document: yes | ||
normalize: yes | ||
inside_rmd: no | ||
must.exist: yes | ||
check_if_suggests_is_installed: yes | ||
``` | ||
This can be deleted, but if you're going to continue using `attachment` it's worth customizing some of the options for your app-package. | ||
|
||
### `sinew` | ||
|
||
The [`sinew` package](https://yonicd.github.io/sinew/index.html) also warrants mentioning because it can help ensure you're namespacing functions from add-on packages, although it's not automated like `attachment::att_amend_desc()`. The primary function in `sinew` is [`pretty_namespace()`](https://yonicd.github.io/sinew/reference/pretty_namespace.html). | ||
|
||
```r | ||
sinew::pretty_namespace(con = "app.R") | ||
``` | ||
|
||
![](images/sinew_pretty_namespace.png){width='100%'} | ||
|
||
All Shiny app-packages will inherently depend on `shiny`, so including more dependencies can make developers justifiably uneasy. In this appendix, we'll explore the package dependencies using the [dependency lookup features](https://pak.r-lib.org/dev/reference/get-started.html#dependency-lookup) from the [`pak` package](https://pak.r-lib.org/dev/index.html) | ||
|
||
### `desc` | ||
|
||
The [`desc` package](https://github.com/r-lib/desc) provides functions for creating, reading, writing, and manipulating `DESCRIPTION` files. You can include additional dependencies to your DESCRIPTION using the `desc_set_dep()` function. | ||
|
||
```r | ||
library(desc) | ||
desc_set_dep("glue", "Imports") | ||
desc_get("Imports") | ||
``` | ||
|
||
```{verbatim} | ||
Imports: | ||
bslib, | ||
cli, | ||
glue, | ||
ggplot2, | ||
logger, | ||
rlang, | ||
sass, | ||
shiny, | ||
shinythemes, | ||
stringr, | ||
tools | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.