Skip to content

Commit

Permalink
Built site for gh-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
mjfrigaard committed Feb 13, 2024
1 parent cd8cd27 commit 731da8f
Show file tree
Hide file tree
Showing 11 changed files with 445 additions and 94 deletions.
2 changes: 1 addition & 1 deletion .nojekyll
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b807f7ea
42f3f7e9
353 changes: 353 additions & 0 deletions Entanglement.qmd
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
```

12 changes: 6 additions & 6 deletions app_data.html
Original file line number Diff line number Diff line change
Expand Up @@ -472,9 +472,9 @@ <h2 id="toc-title">Contents</h2>
</ul></li>
<li><a href="#step-3" id="toc-step-3" class="nav-link" data-scroll-target="#step-3"><span class="header-section-number">24.1.3</span> Step 3</a></li>
<li><a href="#step-4" id="toc-step-4" class="nav-link" data-scroll-target="#step-4"><span class="header-section-number">24.1.4</span> Step 4</a></li>
<li><a href="#sec-testing-reactive-values" id="toc-sec-testing-reactive-values" class="nav-link" data-scroll-target="#sec-testing-reactive-values">Testing <code>reactiveValues()</code></a></li>
<li><a href="#sec-testing-reactive-values" id="toc-sec-testing-reactive-values" class="nav-link" data-scroll-target="#sec-testing-reactive-values"><span class="header-section-number">24.1.5</span> Testing <code>reactiveValues()</code></a></li>
</ul></li>
<li><a href="#sessionuserdata" id="toc-sessionuserdata" class="nav-link" data-scroll-target="#sessionuserdata"><span class="header-section-number">24.2</span> <code>session$userData</code></a>
<li><a href="#sec-session-user-data" id="toc-sec-session-user-data" class="nav-link" data-scroll-target="#sec-session-user-data"><span class="header-section-number">24.2</span> <code>session$userData</code></a>
<ul>
<li><a href="#non-reactive-objects" id="toc-non-reactive-objects" class="nav-link" data-scroll-target="#non-reactive-objects"><span class="header-section-number">24.2.0.1</span> Non-reactive objects</a></li>
<li><a href="#step-1-1" id="toc-step-1-1" class="nav-link" data-scroll-target="#step-1-1"><span class="header-section-number">24.2.1</span> Step 1</a>
Expand Down Expand Up @@ -952,8 +952,8 @@ <h3 data-number="24.1.4" class="anchored" data-anchor-id="step-4"><span class="h
<p>An important thing to note is that we can only reference <code>rVals$inputs()</code> in a <strong>reactive consumer</strong> (i.e., using <code>reactive()</code>, <code>observe()</code>, etc.). That’s why when we change any of the UI inputs, the values change in <code>rVals$inputs()</code> and in the <code>inputs()</code> object inside the display module.</p>
<p>You can also view these outputs using <code>movies_app(run = 'b', bslib = TRUE)</code>.</p>
</section>
<section id="sec-testing-reactive-values" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="sec-testing-reactive-values">Testing <code>reactiveValues()</code></h3>
<section id="sec-testing-reactive-values" class="level3" data-number="24.1.5">
<h3 data-number="24.1.5" class="anchored" data-anchor-id="sec-testing-reactive-values"><span class="header-section-number">24.1.5</span> Testing <code>reactiveValues()</code></h3>
<p>If you decide to use <code>reactiveValues()</code> or <code>session$userData</code>, you’ll need to confirm these objects in your tests. The module tests for <code>test-mod_scatter_display.R</code> have been redesigned to handle the <code>reactiveValues()</code> input.<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a></p>
<p>I’ll briefly summarize the changes below:</p>
<ul>
Expand Down Expand Up @@ -1023,8 +1023,8 @@ <h3 class="unnumbered anchored" data-anchor-id="sec-testing-reactive-values">Tes
</ul>
</section>
</section>
<section id="sessionuserdata" class="level2 page-columns page-full" data-number="24.2">
<h2 data-number="24.2" class="anchored" data-anchor-id="sessionuserdata"><span class="header-section-number">24.2</span> <code>session$userData</code></h2>
<section id="sec-session-user-data" class="level2 page-columns page-full" data-number="24.2">
<h2 data-number="24.2" class="anchored" data-anchor-id="sec-session-user-data"><span class="header-section-number">24.2</span> <code>session$userData</code></h2>
<p>Objects stored in <code>session$userData</code> are not inherently reactive, which makes it ideal for storing persistent values or data that don’t require (or trigger) reactivity. Below is a demonstration of using <code>session$userData</code> to store a non-reactive function to be used in the <code>inst/dev/</code> application.</p>

<div class="no-row-height column-margin column-container"><div class="">
Expand Down
Loading

0 comments on commit 731da8f

Please sign in to comment.