-
Notifications
You must be signed in to change notification settings - Fork 0
/
development.qmd
794 lines (557 loc) · 28.7 KB
/
development.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
# Development {#sec-dev}
```{r}
#| eval: true
#| echo: false
#| include: false
source("_common.R")
```
```{r}
#| label: co_box_tldr
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "b",
look = "default",
hsize = "1.10",
size = "1.05",
header = "![](images/devtools.svg){height=40} TLDR",
fold = TRUE,
contents = "
<br>
Package development involves three habits:
1. Loading the code in the `R/` folder:\n
    [<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd>]{style='font-weight: bold; font-size: 0.75em;'} / `devtools::load_all()`
2. Creating the `NAMESPACE` and help files in the `man/` folder:\n
    [<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>D</kbd>]{style='font-weight: bold; font-size: 0.75em;'} / `devtools::document()`
3. Installing the package\n:
    [<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>B</kbd>]{style='font-weight: bold; font-size: 0.75em;'} / `devtools::install()`
"
)
```
---
::: {layout="[85, 15]"}
After creating a `DESCRIPTION` file with the mandatory fields, moving the `.R` files into the `R/` folder, and configuring the project build tools in `.Rproj`, we're ready to test our app-package functionality with the [`devtools` package](https://devtools.r-lib.org/).
![](images/devtools.svg){width="220"}
:::
:::: {.callout-tip collapse='true' appearance='default' icon-=false}
## [Accessing applications]{style='font-weight: bold; font-size: 1.15em;'}
::: {style='font-size: 0.95em; color: #282b2d;'}
I've created the [`shinypak` R package](https://mjfrigaard.github.io/shinypak/) In an effort to make each section accessible and easy to follow:
Install `shinypak` using `pak` (or `remotes`):
```{r}
#| code-fold: false
#| message: false
#| warning: false
#| eval: false
# install.packages('pak')
pak::pak('mjfrigaard/shinypak')
```
Review the chapters in each section:
```{r}
#| code-fold: false
#| message: false
#| warning: false
#| collapse: true
library(shinypak)
list_apps(regex = '^04')
```
Launch the app:
```{r}
#| code-fold: false
#| eval: false
launch("04_devtools")
```
Download the app:
```{r}
#| code-fold: false
#| eval: false
get("04_devtools")
```
:::
::::
By ‘functionality’, I mean our app-package can call the `devtools` functions for loading the code in `R/`, creating documentation, and successfully installing the package from the source files.
If you'd like a refresher on the Shiny and R package chapters, I've provided a refresher of these topics below:
```{r}
#| label: co_box_desc_recap
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "b",
look = "default", hsize = "1.10", size = "1.05",
fold = TRUE,
header = "Chapters 1 & 2 Refresher:",
contents = "
**Shiny apps:**
- **`R/` folder**: converting the application code into functions (i.e., modules and a standalone app function) and placing them alongside any utility functions in an `R/` folder removes the need to call `source()` in `app.R`.
- **`www/` folder**: images, CSS, JavaScript, and other static resources can be stored in `www/` and Shiny will serve these files when the application is run.
**R Packages:**
- R packages require a **`DESCRIPTION`** file with the following fields:
- `Package`, `Version`, `License`, `Description`, `Title`, `Author`, and `Maintainer`.
- `usethis::create_package()` will create new app-packages, and can be used to convert existing Shiny app projects into Shiny app-packages.
- The **Build** pane in the IDE requires the package build fields in the `.Rproj` file
- The package development settings can be accessed via **Tools** > **Project Options...** > **Build Tools**
"
)
```
## Package dev with [`devtools`]{style="font-size: 1.10em"}
If you’re new to package development, having a little background on the `devtools` package is helpful. Earlier versions of `devtools` contained most of the functions used for package development. In version 2.0, `devtools` went under a [conscious uncoupling](https://www.tidyverse.org/blog/2018/10/devtools-2-0-0/#conscious-uncoupling), which means there was a "division of labor" for its core functionality:
- The [`usethis`](https://usethis.r-lib.org/) package contains the functions for *creating* package folders and files (`.R` files, tests, vignettes, etc.). `usethis` is also automatically loaded when you call `library(devtools)`.
- Loading and building your app-package is handled by [`pkgload`](https://pkgload.r-lib.org/) and [`pkgbuild`](https://pkgbuild.r-lib.org/)
- For app-packages destined for CRAN, the `R CMD check` is handled by [`rcmdcheck`](https://rcmdcheck.r-lib.org/) and [`revdepcheck`](https://github.com/r-lib/revdepcheck)
- Installing packages from non-CRAN repositories (i.e., `install_github()`) is handled by [`remotes`](https://remotes.r-lib.org/)
You don't have to install all of these packages (they will be loaded with `devtools`), but the information is essential because it affects the dependencies in your app-package:
> '*Package developers who wish to depend on `devtools` features should also pay attention to which package the functionality is coming from and depend on that rather than `devtools`. In most cases, packages should not depend on `devtools` directly.*' - [devtools 2.0.0, tidyverse blog](https://www.tidyverse.org/blog/2018/10/devtools-2-0-0/#conscious-uncoupling)
We will cover this topic more in the [dependencies chapter.](dependencies.qmd).
Let's assume we're continuing with the app project we converted manually in the [previous branch](https://github.com/mjfrigaard/sap/tree/03.1_description) of `sap` (the files and folders are below).
```{r}
#| label: git_box_03.1_description
#| echo: false
#| results: asis
#| eval: true
git_margin_box(
fig_pw = '70%',
branch = "03.1_description",
repo = 'sap')
```
```{bash}
#| eval: false
#| code-fold: false
sap/
├── DESCRIPTION
├── R
│ ├── mod_scatter_display.R
│ ├── mod_var_input.R
│ └── utils.R
├── README.md
├── app.R
├── man
├── movies.RData
├── sap.Rproj
└── www
└── shiny.png
4 directories, 9 files
```
We'll revert to the previous version of `sap` we created by manually editing the `DESCRIPTION` file[^dev-0] to show the connection between the `devtools` functions and specific fields in the `DESCRIPTION` file.[^dev-1]
[^dev-0]: View the `DESCRIPTION` file in the `03.1_description` branch [here](https://github.com/mjfrigaard/sap/tree/03.1_description/DESCRIPTION).
[^dev-1]: If you create or convert your Shiny app project with `usethis::create_package()`, a few fields (i.e., `Roxygen` and `RoxygenNote`) are added automatically without explaining their role or purpose.
### The [`DESCRIPTION`]{style="font-size: 1.05em; font-weight: bold;"} file {.unnumbered}
The version of `sap` in this branch has a `DESCRIPTION` file with the seven mandatory fields:
```{bash}
#| eval: false
#| code-fold: false
Package: sap
Version: 0.0.0.9000
Type: Package
Title: Shiny App-Packages
Description: An R package with a collection of Shiny applications.
Author: John Smith [aut, cre]
Maintainer: John Smith <[email protected]>
License: GPL-3
```
:::{.column-margin}
Leave an empty final line in the `DESCRIPTION`
:::
### The [`.Rproj`]{style="font-size: 1.05em; font-weight: bold;"} file {.unnumbered}
However, the `.Rproj` file is still configured to work with a Shiny project:[^dev-2]
[^dev-2]: If you created your Shiny app using the **New Project Wizard**, your `.Rproj` file has been configured to work with project, not a package.
```{bash}
#| eval: false
#| code-fold: false
Version: 1.0
RestoreWorkspace: Default
SaveWorkspace: Default
AlwaysSaveHistory: Default
EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 2
Encoding: UTF-8
RnwWeave: Sweave
LaTeX: XeLaTeX
```
The differences between developing an R package and a Shiny app can be boiled down to a handful [habits]{style="font-weight: bold; color: #5f2042;"}, each of which calls a `devtools` function:
::: {.column-margin}
I'll use [this font style]{style="font-weight: bold; color: #5f2042;"} to indicate each `devtools` habit and accompanying function.
:::
1. [Load]{style="font-weight: bold; color: #5f2042;"} all the functions and data in your app-package with `load_all()`
2. [Document]{style="font-weight: bold; color: #5f2042;"} the app-package functions and data with `document()`
3. [Install]{style="font-weight: bold; color: #5f2042;"} the app-package with `install()`
In the sections below, I'll cover each function and some opinions about how they should be used when your Shiny app transitions to an app-package.[^dev-3]
[^dev-3]: The topics covered in this section shouldn't be considered a replacement for the ['Whole Game' chapter in R packages (2 ed)](https://r-pkgs.org/whole-game.html) or the ['Workflow' section of Mastering Shiny](https://mastering-shiny.org/scaling-packaging.html#workflow) (and I highly recommend reading both).
Before we can start developing, we need to install `devtools`:
```{r}
#| eval: false
#| code-fold: false
install.packages("devtools")
library(devtools)
```
`usethis` is automatically loaded/attached with `devtools`.
```{verbatim}
#| eval: false
#| code-fold: false
Loading required package: usethis
```
```{r}
#| label: co_box_keyboard_shortcuts
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "r", look = "default",
hsize = "1.15", size = "1.05", fold = FALSE,
header = "Keyboard shortcuts",
contents = "I strongly recommend using the keyboard shortcuts for each `devtools` function. Shortcuts reduce typing and bundle all those keystrokes into a single action. They also create a kind of 'muscle memory' for each step.
In RStudio ![](images/rstudio-icon.png){height=20}, new keyboard shortcuts can be created using the [`shrtcts` package](https://pkg.garrickadenbuie.com/shrtcts/) or by clicking on **Tools** > **Modify Keyboard Shortcuts**.
In Positron ![](images/positron.png){height=20}, the `devtools` functions covered below are already mapped to the keyboard shortcuts. Follow the instructions [found in Positron's Wiki](https://github.com/posit-dev/positron/wiki/Keyboard-Shortcuts) to add new shortcuts."
)
```
## [Load]{style="font-weight: bold;"} {#sec-dev-load}
`load_all()` is the most common `devtools` function we'll use during development because we should [load the package when anything changes in the `R/` folder.]{style="font-weight: bold; font-size: 1.10em;"}
> '*`load_all()` removes friction from the development workflow and eliminates the temptation to use workarounds that often lead to mistakes around namespace and dependency management*' - [Benefits of `load_all()`, R Packages, 2ed](https://r-pkgs.org/workflow101.html#benefits-of-load_all)
<br>
:::{layout="[45, 10, 45]" layout-valign="top"}
[<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd>]{style="font-size: 1.15em; font-weight: bold;"}
[=]{style="font-size: 1.50em; font-weight: bold;"}
[<code>devtools::load_all()</code>]{style="font-size: 1.15em; font-weight: bold;"}
:::
<br>
Using `load_all()` is similar to calling `library(sap)` because it loads the code in `R/` along with any data files. `load_all()` is also designed for iteration (unlike using `source()`), and when it's successful, the output is a single informative message:
```{verbatim}
#| eval: false
#| code-fold: false
ℹ Loading sap
```
## [Document]{style="font-weight: bold;"} {#sec-dev-document}
The `document()` function from `devtools` serves two purposes:
1. Writing the package `NAMESPACE` file
2. Creates the help files in the `man/` folder
[Document the package whenever changes are made to any `roxygen2` syntax (or settings).]{style="font-weight: bold; font-size: 1.10em;"}
<br>
:::{layout="[45, 10, 45]" layout-valign="top"}
[<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>D</kbd>]{style="font-size: 1.15em; font-weight: bold;"}
[=]{style="font-size: 1.50em; font-weight: bold;"}
[<code>devtools::document()</code>]{style="font-size: 1.15em; font-weight: bold;"}
:::
<br>
`devtools` is smart enough to recognize the first time `document()` is called, so when I initially run it in the **Console**, it prompts me that the `roxygen2` version needs to be set in the `DESCRIPTION` file:[^devtools-roxygen2]
[^devtools-roxygen2]: `devtools` relies on `roxygen2` for package documentation, so the `RoxygenNote` field is required in the `DESCRIPTION`.
```{verbatim}
#| eval: false
#| code-fold: false
ℹ Updating sap documentation
First time using roxygen2. Upgrading automatically...
Setting `RoxygenNote` to "7.3.2"
```
You may have noticed calling `document()` also [calls `load_all()`](https://github.com/r-lib/devtools/blob/2c642ab44f4fb9d4117f0e198dfcf42ad79e432a/R/document.R#L22), which scans the loaded package contents for special documentation syntax before writing the `NAMESPACE` file (we'll cover the `NAMESPACE` in the chapter on Dependencies).
```{verbatim}
#| eval: false
#| code-fold: false
ℹ Loading sap
Writing NAMESPACE
```
If we open the `NAMESPACE` file, we see it's empty (and that we shouldn't edit this file by hand).
![Initial `NAMESPACE` file](images/dev_namespace.png){#fig-dev_namespace width='100%' fig-align='center'}
The last few output lines warn us to include the `Encoding` field in the `DESCRIPTION`.
```{verbatim}
#| eval: false
#| code-fold: false
Warning message:
roxygen2 requires Encoding: "UTF-8"
ℹ Current encoding is NA
```
`devtools` won't automatically add `Encoding` (like it did with `RoxygenNote` above), so we’ll need to add it to the `DESCRIPTION` file manually:
:::{.column-margin}
Always leave an empty final line in the `DESCRIPTION`
:::
```{bash}
#| eval: false
#| code-fold: false
Package: sap
Version: 0.0.0.9000
Type: Package
Title: movies app
Description: A movies data Shiny application.
Author: John Smith [aut, cre]
Maintainer: John Smith <[email protected]>
License: GPL-3
RoxygenNote: 7.2.3
Encoding: UTF-8 # <1>
```
1. The `Encoding` value shouldn't include quotes like the warning message above (i.e., `UTF-8`)
After adding the required fields to the `DESCRIPTION` file,[^dev-4] we'll `document()` the package again using the keyboard shortcut:
[^dev-4]: Always leave an empty final line in the `DESCRIPTION` file.
In the **Build** pane, we see the following:
```{verbatim}
#| eval: false
#| code-fold: false
==> devtools::document(roclets = c('rd', 'collate', 'namespace'))
ℹ Updating sap documentation
ℹ Loading sap
Documentation completed
```
## [Install]{style="font-weight: bold;"} {#sec-dev-install}
```{r}
#| label: co_box_positron_version
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "b",
look = "simple",
hsize = "1.10",
size = "1.05",
header = "![](images/positron.png){height=20} Positron Version",
fold = TRUE,
contents = "At the time of this writing, the [2024.09.0-1](https://github.com/posit-dev/positron/releases/tag/2024.09.0-1) pre-release of Positron was available for testing."
)
```
The final development habit checking if our app-package can be installed locally with `devtools::install()` or `pak::local_install(upgrade = FALSE)` (depending on the IDE you're using).
[Install a package after the initial setup, after major changes to the code, documentation, or dependencies, and before committing or sharing.]{style='font-weight: bold; font-size: 1.10em;'}
<br>
:::{layout="[45, 10, 45]" layout-valign="top"}
[<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>B</kbd>]{style="font-size: 1.15em; font-weight: bold;"}
[=]{style="font-size: 1.50em; font-weight: bold;"}
[<code>devtools::install()</code>]{style="font-size: 1.15em; font-weight: bold;"}
:::
::: {.panel-tabset}
### RStudio ![](images/rstudio-icon.png){height=20}
<br>
`install()` will prompt the following output in the **Build** pane:
```{verbatim}
#| eval: false
==> R CMD INSTALL --preclean --no-multiarch --with-keep.source sap # <1>
* installing to library ‘/path/to/local/install/sap-090c61fc/R-4.2/x86_64-apple-darwin17.0’ # <2>
* installing *source* package ‘sap’ ... # <3>
** using staged installation
** R
** byte-compile and prepare package for lazy loading
No man pages found in package ‘sap’ # <4>
** help # <5>
*** installing help indices # <5>
** building package indices
** testing if installed package can be loaded from temporary location # <6>
** testing if installed package can be loaded from final location # <6>
** testing if installed package keeps a record of temporary installation path # <7>
* DONE (sap) # <8>
```
1. We saw both of these ` R CMD INSTALL` settings in the `sap.Rproj` file from the previous chapter
2. Full file path for installation
3. `install()` attempts to install the package from the `*source*` files and a 'bundle' or source tarball file (i.e., `.tar.gz`)
4. `No man pages found in package 'sap'` tells us none of the code in `R/` has adequately been documented (which we'll cover in the `roxygen2` chapter)
5. Building the `?help` files
6. Checks to see if package can be loaded from multiple locations and stores
7. Checks to see if package stores the install location
8. `DONE (sap)` means `sap` was successfully installed!
### Positron ![](images/positron.png){height=20}
<br>
In Positron, <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>B</kbd> will call `pak::local_install(upgrade = FALSE)`. This command will be run in a new Terminal window:
```{verbatim}
#| eval: false
* Executing task: /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/bin/R -e 'pak::local_install(upgrade = FALSE)' # <1>
R version 4.4.0 (2024-04-24) -- "Puppy Cup" # <2>
Copyright (C) 2024 The R Foundation for Statistical Computing
Platform: x86_64-apple-darwin20
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.
Natural language support but running in an English locale
R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.
Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R. # <2>
> pak::local_install(upgrade = FALSE) # <3>
✔ Updated metadata database: 7.50 MB in 12 files.# <4>
✔ Updating metadata database ... done
→ Will update 1 package. # <5>
→ The package (0 B) is cached.
+ sap 0.0.0.9000 → 0.0.0.9000 👷🏾♂️
ℹ No downloads are needed, 1 pkg is cached
✔ Got sap 0.0.0.9000 (source) (96 B) # <5>
ℹ Packaging sap 0.0.0.9000 # <6>
✔ Packaged sap 0.0.0.9000 (18.2s)
ℹ Building sap 0.0.0.9000 # <7>
✔ Built sap 0.0.0.9000 (3.1s)
✔ Installed sap 0.0.0.9000 (local) (63ms) # <8>
✔ 1 pkg + 54 deps: kept 54, upd 1, dld 1 (NA B) [53.2s] # <9>
>
>
* Terminal will be reused by tasks, press any key to close it. # <10>
```
1. Name of task and terminal
2. Starts new R session
3. Calls `pak::local_install(upgrade = FALSE)`
4. `pak` will check the package database for updates
5. the `upgrade = FALSE` means `pak` is going to do "the minimum amount of work to give you the latest version(s) of `pkg`"
6. Packaging `sap`
7. Building `sap`
8. Installing `sap`
9. Summary ('kept 54 dependencies, updated 1, downloaded 1 package')
10. Close Terminal message
:::
<br>
#### What's the difference? {.unnumbered}
`devtools::install()` focuses on helping package developers by managing all necessary steps for installation, including rebuilding documentation and running tests. `devtools::install()` also automatically updates outdated dependencies during installation unless `dependencies` is set to `FALSE`.
`pak::local_install()` is designed to use parallel downloads and more efficient dependency resolution, making faster and more reliable than `devtools` in many cases.[^pak-performance] The `upgrade = FALSE` installs a package without upgrading its dependencies, keeping the current package versions intact.
[^pak-performance]: It stands to reason that installing a package with `pak::local_install()` in Positron would be faster than installing a package using `devtools::install()` in RStudio, but this has not been my experience.
```{r}
#| label: git_box_04_devtools
#| echo: false
#| results: asis
#| eval: true
git_margin_box(contents = "launch",
fig_pw = '75%',
branch = "04_devtools",
repo = 'sap')
```
## [Check?]{style="font-weight: bold;"} {#sec-dev-check}
`devtools::check()` performs a series of checks to ensure a package meets the standards set by [CRAN](https://cran.r-project.org/). You can consider `check()` as a 'quality control' function for documentation, `NAMESPACE` dependencies, unnecessary or non-standard folders and files, etc. [R Packages recommends](https://r-pkgs.org/workflow101.html#sec-workflow101-r-cmd-check) using `check()` often, but I agree with the advice in [Mastering Shiny](https://mastering-shiny.org/scaling-packaging.html#r-cmd-check) on using `check()` with app-packages,
> *'I don't recommend that you [call `devtools::check()`] the first time, the second time, or even the third time you try out the package structure. Instead, I recommend that you get familiar with the basic structure and workflow before you take the next step to make a fully compliant package.'*
However, I've included an example of running `check()` on `sap` in the callout box below to demonstrate how it works.
::: {.callout-caution collapse='true' appearance="default"}
### [Running devtools::check()]{style="font-size: 1.10em"}
```{r}
#| eval: false
#| code-fold: false
devtools::check()
```
The output from `check()` can be rather lengthy (it's pretty comprehensive!), and it provides feedback on each item in the form of a note (`N`), warning (`W`), or error (`E`).
```{verbatim}
#| eval: false
#| code-fold: false
==> devtools::check()
Duration: 15.3s
N checking top-level files
Non-standard files/directories found at top level:
‘app.R’ ‘movies.RData’
W checking dependencies in R code ...
'::' or ':::' imports not declared from:
‘ggplot2’ ‘shiny’ ‘stringr’
N checking R code for possible problems (3.1s)
mod_scatter_display_server : <anonymous>: no visible binding for global
variable ‘movies’
scatter_plot: no visible binding for global variable ‘.data’
Undefined global functions or variables:
.data movies
W checking for missing documentation entries ...
Undocumented code objects:
‘mod_scatter_display_server’ ‘mod_scatter_display_ui’
‘mod_var_input_server’ ‘mod_var_input_ui’ ‘scatter_plot’
All user-level objects in a package should have documentation entries.
See chapter ‘Writing R documentation files’ in the ‘Writing R
Extensions’ manual.
0 errors ✔ | 2 warnings ✖ | 2 notes ✖
```
A summary of each item is below:
- `checking top-level files`: This note refers to the two non-standard (i.e., not typically found in an R package) files, `app.R` and `movies.RData`.
- `checking dependencies in R code`: This warning tells I need to namespace functions from add-on packages (in this case, `ggplot2`, `shiny`, and `stringr`)
- `checking R code for possible problems`: This item refers to the call to load the `movies` data in the module server function (`mod_scatter_display_server`).
- `checking for missing documentation entries`: This is warning me that the module functions aren't properly documented and refers me to the [official R documentation](https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Writing-R-documentation-files).
Each of these items is also printed under the `── R CMD check results` heading:
```{bash}
#| eval: false
#| code-fold: true
#| code-summary: 'show/hide R CMD check results'
Duration: 15.3s
❯ checking dependencies in R code ... WARNING
'::' or ':::' imports not declared from:
‘ggplot2’ ‘shiny’ ‘stringr’
❯ checking for missing documentation entries ... WARNING
Undocumented code objects:
‘mod_scatter_display_server’ ‘mod_scatter_display_ui’
‘mod_var_input_server’ ‘mod_var_input_ui’ ‘scatter_plot’
All user-level objects in a package should have documentation entries.
See chapter ‘Writing R documentation files’ in the ‘Writing R
Extensions’ manual.
❯ checking top-level files ... NOTE
Non-standard files/directories found at top level:
‘app.R’ ‘movies.RData’
❯ checking R code for possible problems ... NOTE
mod_scatter_display_server : <anonymous>: no visible binding for global
variable ‘movies’
scatter_plot: no visible binding for global variable ‘.data’
Undefined global functions or variables:
.data movies
0 errors ✔ | 2 warnings ✖ | 2 notes ✖
```
If you're submitting your app-package to CRAN (or want to use `check()` for other reasons), follow the [suggested workflow](https://r-pkgs.org/workflow101.html#check-workflow) for `check()`:
> *The workflow for checking a package is simple, but tedious:*
>
> 1. *Run `devtools::check()` or press* [<kbd>Shift</kbd> + <kbd>Ctrl/Cmd</kbd> + <kbd>E</kbd>]{style="font-weight: bold; font-style: italic; font-size: 0.75em"}
>
> 2. *Fix the first problem.*
>
> 3. *Repeat until there are no more problems*.'
I've found a good habit for when to `check()` to be:
[After adding a bug fix or feature, check a package and keep any notes, warnings, or errors from accumulating.]{style="font-weight: bold; font-size: 1.10em;"}
:::
## Hidden package files
You might notice additional 'hidden' files in your new app-package:[^hidden-files] `.gitignore`, `.Rbuildignore`, and `.Rprofile`:
[^hidden-files]: By convention, files that begin with `.` ([dot files](https://en.wikipedia.org/wiki/Hidden_file_and_hidden_directory)) are considered hidden.
### `.gitignore` {#sec-dev-gitignore}
`.gitignore` will ignore some of the standard hidden files created by R or RStudio. The initial contents will include something like the following:
```{bash}
#| eval: false
#| code-fold: false
.Rproj.user
.Rhistory
.RData
.Ruserdata
.DS_Store # for mac users
```
### `.Rbuildignore` {#sec-dev-rbuildignore}
`.Rbuildignore` includes files that we need to have in our app-package, but don't conform to the standard R package structure (and shouldn't be included when building our app-package from the source files).
```{bash}
#| eval: false
#| code-fold: false
^.*\.Rproj$
^\.Rproj\.user$
```
*Note the syntax for detecting file patterns.*
### `.Rprofile` {#sec-dev-rprofile}
The `.Rprofile` is specific to the user (you) and might include options for loading packages or tests:
```{r}
#| eval: false
#| code-fold: false
if (interactive()) {
require(usethis, quietly = TRUE)
}
options(shiny.testmode = TRUE)
```
`.Rprofile` is also included in your directory if you're using `renv` to manage packages/versions.
## Recap {.unnumbered}
Creating an app-package involves adopting some new `devtools` [habits]{style="font-weight: bold; font-size: 1.0em;"}, and the initial contents of `sap` hopefully helped demonstrate the purpose of each function.
```{r}
#| label: co_box_workflow_recap
#| echo: false
#| results: asis
#| eval: true
co_box(
color = "g",
look = "default", hsize = "1.10", size = "1.05",
header = "Recap: Package development habits",
contents = "
After installing and loading `devtools`:
1. [Load the package whenever changes occur in the `R/` folder]{style='font-weight: bold; font-size: 1.0em;'}.\n
- [<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd>]{style='font-weight: bold; font-size: 0.75em'} load all the code in the package.\n
2. [Document the package whenever changes are made to any `roxygen2` syntax (or settings)]{style='font-weight: bold; font-size: 1.0em;'}.\n
- [<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>D</kbd>]{style='font-weight: bold; font-size: 0.75em'} record the documentation and dependencies.\n
3. [Install the package after the initial setup, after major changes to the code, documentation, or dependencies, and before committing or sharing]{style='font-weight: bold; font-size: 1.0em;'}.\n
- [<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>B</kbd>]{style='font-weight: bold; font-size: 0.75em'} confirms the package can be installed.\n
- `pak::local_install()` benefits from optimized dependency resolution and download methods
- `devtools::install()` handles a broader range of tasks during installation (including rebuilding documentation and running tests)
Habits require repetition to develop, and I hope the workflow above can be applied to your Shiny app-packages, provided you're using `devtools` and Posit workbench.
",
fold = FALSE
)
```
The following section will cover documenting functions with [`roxygen2`](https://roxygen2.r-lib.org/)
```{r}
#| label: git_contrib_box
#| echo: false
#| results: asis
#| eval: true
git_contrib_box()
```