# install.packages('pak')
::pak('mjfrigaard/shinypak') pak
5 Documentation
This chapter covers function documentation in an app-package by introducing the basic fundamentals of the roxygen2
syntax (i.e., the ‘bare minimum’ to include for each function in your app-package). I’ll also touch on a few helpful roxygen2
tags specific to Shiny modules and standalone app functions.
The current state of shinyrPkgs
is stored displayed in the folder tree below:
shinyrPkgs/
├── DESCRIPTION
├── NAMESPACE
├── R
│ ├── mod_scatter_display.R
│ ├── mod_var_input.R
│ └── utils.R
├── README.md
├── app.R
├── man
├── movies.RData
├── shinyrPkgs.Rproj
└── www
└── shiny.png
4 directories, 10 files
(the rsconnect/
folder from deploying shinyrPkgs
has been removed)
If you recall, the output from the previous install()
returned the following message regarding the documentation:
No man pages found in package ‘shinyrPkgs’
Documenting the functions below R/
will address message, and I strongly encourage checking out the roxygen2
vignettes and R Packages, 2ed chapter for more information on documenting your app-package.
5.1 roxygen2
intro
See the 04_devtools
branch of shinyrPkgs
.
roxygen2
connects the package code (i.e., the .R
files in the R/
folder) to its documentation files (i.e., the .Rd
files in the man/
folder):
%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'Inconsolata', 'primaryColor': '#8dd38d', 'edgeLabelBackground':'#02577A'}}}%% flowchart TD X1(Code in <code>R/</code> Folder) --> X2(<code>roxygen2</code> Comments and Tags in <code>.R</code> Files) X2 --> X3(Run <code>devtools::document</code>) X3 --> X4(Parse <code>roxygen2</code> Comments and Tags) X4 --> X5(Generate <code>.Rd</code> Files in <code>man/</code> Folder) X4 --> X6(Generate <code>NAMESPACE</code> File) style X1 fill:#8dd38d,stroke:none,rx:10,ry:10; style X2 fill:#8dd38d,stroke:none,rx:10,ry:10; style X3 fill:#89D6FB,stroke:none,rx:10,ry:10; style X4 fill:#89D6FB,stroke:none,rx:10,ry:10; style X5 fill:#89D6FB,stroke:none,rx:10,ry:10; style X6 fill:#89D6FB,stroke:none,rx:10,ry:10;
roxygen2
creates man/
documentationThe two pieces of roxygen2
syntax to know are comment blocks and tags:
Comment blocks are any lines beginning with
#'
#' #' #'
Tags begin with
@
#' #' @tag #'
When documenting functions, roxygen2
tags and comment blocks are placed directly above any functions stored in R/
.
#'
#' @tag
#'
<- function() {
my_fun
}
In the following sections, we’ll cover roxygen2
basics using examples for the scatter_plot()
function found in R/utils.R
.
5.1.1 markdown = TRUE
When we created our app-package with usethis::create_package()
, support for markdown formatting in package help files is automatically included by adding Roxygen: list(markdown = TRUE)
to the DESCRIPTION
file:
Package: shinyrPkgs
Version: 0.0.0.9000
Type: Package
Title: movies app
Description: A movies data Shiny application.
Author: John Smith [aut, cre]
Maintainer: John Smith <John.Smith@email.io>
License: GPL-3
DisplayMode: Showcase
RoxygenNote: 7.2.3
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
# Always leave at least one empty final line in
# your DESCRIPTION file
5.1.2 @title
& @description
The first two sections of roxygen2
documentation are the title and description. These sections don’t require tags–roxygen2
will detect each section as long as there is at least one #'
line separating them (and their contents don’t extend past the length indicated in parentheses below):
#' function title (one line)
#'
#' A short description... (one paragraph)
#'
The @title
and @description
for the scatter_plot()
function stored in R/utils.R
might look like this:
#' Create scatter plot
#'
#' Custom [`ggplot2`](https://ggplot2.tidyverse.org/) function for building scatter plots in `shinyrPkgs`.
#'
5.1.3 @param
& @return
Document function arguments and outputs with @param
and @return
:
@param
: should include the name and description of each function input (i.e., their type and what they do)#' @param name description of its action
- Read more here
@return
: these describe the type (or class) and structure of the function output#' @return type/structure of the output
- Read more here
Below are examples for @param
and @return
in the scatter_plot()
function stored in R/utils.R
:
#'
#' @param df `data.frame` or `tibble`
#' @param x_var string variable mapped to `x` axis
#' @param y_var string variable mapped to `y` axis
#' @param col_var string variable mapped to `color`
#' @param alpha_var number for point `alpha`
#' @param size_var number for point `size`
#'
#' @return A `ggplot2` plot object
To view how the roxygen2
syntax will appear in the .Rd
file, I’ll document shinyrPkgs
:
To reduce the amount of repetitive code, I’ll just be showing the keyboard shortcuts for each devtools
function from this point forward
Ctrl/Cmd + Shift + D
I can see the scatter_plot.Rd
file is written to the man/
folder:
ℹ Updating shinyrPkgs documentation
ℹ Loading shinyrPkgs
Writing scatter_plot.Rd
Documentation completed
Shiny apps often contain utility functions in helper.R
or utils.R
files. Storing non-shiny code in these files isn’t a bad practice (in fact, it’s encouraged in Mastering Shiny). However, if we’re following the advice in R Packages,1 we should rename R/utils.R
to R/scatter_plot.R
so it matches man/scatter_plot.Rd
as we transition to an app-package.2
R documentation (.Rd
) files have a formatting style similar to (La)TeX (but roxygen2
saves us from having to learn this syntax). When we open man/scatter_plot.Rd
, we see it contains the following:
.Rd
documentation fileTo view the help file, I can enter ?scatter_plot
in the console:
?scatter_plot
And an informative message tells me that the development version scatter_plot.Rd
is being rendered:
ℹ Rendering development documentation for "scatter_plot"
Previewing the development documentation is a great way to verify the content in each .Rd
file meets expectations.
5.1.4 @examples
@examples
are unique because they include executable code demonstrating how a function works. In the Posit Workbench IDE, @examples
are especially helpful because they come with a ‘single click’ hyperlink (the @examples
from ggplot2::aes()
are below):
![](images/doc_aes_examples.png)
![](images/doc_aes_run_examples.png)
@examples
code
The syntax for an example plot with out scatter_plot()
utility function is below:
#' @examples
#' scatter_plot(mtcars,
#' x_var = "mpg",
#' y_var = "disp",
#' col_var = "cyl",
#' alpha_var = 0.5,
#' size_var = 3)
To preview the @examples
in the help file, I’ll document()
and open the development .Rd
file:
Ctrl/Cmd + Shift + D
?scatter_plot
ℹ Rendering development documentation for "scatter_plot"
The Run examples hyperlink won’t be active in the .Rd
file preview, but reviewing the code in @examples
allows me to correct any errors or typos early.
The scatter_plot()
function has a documented Title, Description, Usage, Arguments, Value, and Examples. I consider these tags the minimum documentation to include for functions I’m making available to other users.
5.2 Documenting app functions
Your app-package will likely contain at least two functions specific to Shiny: modules and a standalone app function. roxygen2
has tags that can make the documentation for these functions more informative for readers.
Below are a few things to consider when documenting module functions:
Modules will typically include two functions in a single
.R
file: one for the UI and a counterpart in the server.Every module function will include at least one
@param
for the sharedid
.Returned objects are critical in Shiny’s reactive programming model, so I recommend extending the
@description
to include whether or not the returned output is reactive.If the returned output is an input (i.e.
@param
) for another function, link to that documentation functions with@seealso
or@family
.
5.2.1 @seealso
When documenting modules, think of the audience as someone looking to understand the execution path through the application. In our standalone shinyrPkgs
functions, the inputs are collected with the var_input
module, then passed to the scatter_display
module to render in the UI.
The @seealso
tag can be used to connect mod_var_input_ui()
to it’s server function counterpart. The hyperlink to mod_var_input_server()
from mod_var_input_ui()
is created by placing the destination function in square brackets [fun()]
:
show/hide mod_var_input_ui() roxygen2
#' Variable input module (UI)
#'
#' @description
#' `var_input` collects the following graph inputs:
#' * `input$x`
#' * `input$y`
#' * `input$z`
#' * `input$alpha`
#' * `input$size`
#' * `input$plot_title`
#'
#' @param id UI module id
#'
#' @return module UI (HTML)
#'
#' @seealso [mod_var_input_server()]
#'
Ctrl/Cmd + Shift + D
@seealso
creates a hyperlink readers can use to open the next module in the execution path.
var_input
to scatter_display
In mod_var_input_server()
, a link can be made to mod_scatter_display_server()
(the module function collecting the returned values) using the square brackets in the @return
section (without @seealso
):
show/hide mod_var_input_server() roxygen2
#' Variable input module (server)
#'
#' @param id server module id
#'
#' @seealso [mod_var_input_ui()]
#'
#' @return reactive inputs are returned in a `list()`:
#' * `"y" = input$y`
#' * `"x" = input$x`
#' * `"z" = input$z`
#' * `"alpha" = input$alpha`
#' * `"size" = input$size`
#' * `"plot_title" = input$plot_title`
#'
#' These become in the `var_inputs()` argument in [mod_scatter_display_server()]
#'
Ctrl/Cmd + Shift + D
![](images/doc_see_also.png)
![](images/doc_see_also_mod_var_input.png)
mod_var_input_ui()
to (b) mod_var_input_server()
5.2.2 @family
Module functions can also be grouped into families using @family {"description"}
. Below is an example of grouping the “scatter plot module functions” into a family:
show/hide mod_scatter_display_ui() roxygen2
#' Scatter plot display module (UI)
#'
#' @param id UI module id
#'
#'
#' @return module UI (HTML)
#'
#' @family {"scatter plot module functions"}
#'
5.2.3 @section
The @section
tag will create a level two header (##
/<h2></h2>
) section, which can help organize topics (like how to use an input or argument).
@seealso
can also be used to link back to the scatter_plot()
utility function:
show/hide mod_scatter_display_server() roxygen2
#' Scatter plot display module (server)
#'
#'
#' @param id server module id
#' @param var_inputs returned reactive list from [mod_var_input_server()].
#'
#' @section Referring to `var_inputs`:
#' Refer to the reactive returned values from `mod_var_input_server()` as:
#' * `var_inputs()$x`
#' * `var_inputs()$y`
#' * `var_inputs()$z`
#' * `var_inputs()$alpha`
#' * `var_inputs()$size`
#' * `var_inputs()$plot_title`
#'
#'
#' @return rendered plot and title output from [scatter_plot()]
#'
#' @family {"scatter plot module functions"}
#'
Ctrl/Cmd + Shift + D
![](images/doc_family.png)
![](images/doc_usage_seealso.png)
@family
in mod_scatter_display_ui()
(a) and @seealso
in mod_scatter_display_server()
(b)
The figure above shows how the @seealso
links can create a path for the graph parameters through the var_input
and scatter_display
modules. It also demonstrates how @family
groups the scatter_display
UI and server functions.
The scatter_plot()
utility function should also include a @seealso
tag for the module function it’s used in.
show/hide scatter_plot() roxygen2
#' Create scatter plot
#'
#' Custom [`ggplot2`](https://ggplot2.tidyverse.org/) function for building scatter plots in `shinyrPkgs`.
#'
#'
#' @param df `data.frame` or `tibble`
#' @param x_var string variable mapped to `x` axis
#' @param y_var string variable mapped to `y` axis
#' @param col_var string variable mapped to `color`
#' @param alpha_var number for point `alpha`
#' @param size_var number for point `size`
#'
#' @return A `ggplot2` plot object
#'
#'
#' @examples
#' scatter_plot(mtcars,
#' x_var = "mpg",
#' y_var = "disp",
#' col_var = "cyl",
#' alpha_var = 0.5,
#' size_var = 3)
#'
#' @seealso [mod_scatter_display_server()]
#'
Ctrl/Cmd + Shift + D
Connections between (a) modules and (b) utility function
The goal when cross-referencing functions in your app-package is for anyone reading your documentation to follow the links and better understand any modules, their inputs, reactive values, and outputs.
In this case, linking to the scatter_plot()
function gives readers an interactive example to preview the output.
5.3 UI & Server functions
Splitting the standalone app function in app.R
into separate UI and server functions has multiple benefits:
Developing and loading each function will be easier when it’s stored in the
R/
folder.Having dedicated UI, server, and app function means we can develop them independently.
Standalone app functions means we can have multiple applications in the same app-package.
I’ve split the UI and server from launch_app()
into separate movies_ui()
and movies_server()
below.
The movies_ui()
doesn’t have any arguments–the only change is wrapping the shiny::fluidPage()
in shiny::tagList()
:
<- function() {
movies_ui ::tagList(
shiny::fluidPage(
shinytheme = shinythemes::shinytheme("spacelab"),
::titlePanel(
shiny::div(
shiny::img(
shinysrc = "shiny.png",
height = 60,
width = 55,
style = "margin:10px 10px"
), "Movies Reviews"
)
),::sidebarLayout(
shiny::sidebarPanel(
shinymod_var_input_ui("vars")
),::mainPanel(
shinymod_scatter_display_ui("plot")
)
)
)
) }
movies_server()
is written to be passed to the server
argument in shinyApp()
:
<- function(input, output, session) {
movies_server
<- mod_var_input_server("vars")
selected_vars
mod_scatter_display_server("plot", var_inputs = selected_vars)
}
5.3.1 @usage
I’ll explicitly describe the use of movies_ui()
and set @usage
to NULL
(note the use of a code block). I’ll also use @section
to describe each module UI function (and link to the server functions with @seealso
).
show/hide roxygen2 for movies_ui()
#' Movies UI function
#'
#' UI function for standalone app function
#'
#' @usage NULL
#'
#' @details
#' The [launch_app()] function is as a wrapper for `shiny::shinyApp()`:
#'
#' ```
#' shinyApp(ui = movies_ui, server = movies_server)
#' ```
#'
#' In [launch_app()]:
#' * UI is stored in `movies_ui()`
#' * server is stored in [movies_server()]
#'
#' @section `var_input` module:
#' [mod_var_input_ui()] is used to collect the following inputs:
#' * `input$x`
#' * `input$y`
#' * `input$z`
#' * `input$alpha`
#' * `input$size`
#' * `input$plot_title`
#'
#' @seealso [mod_var_input_server()]
#'
#'
#' @section `scatter_display` module:
#' [mod_scatter_display_ui()] displays the graph output using [scatter_plot()]
#'
#' @seealso [mod_scatter_display_server()]
#'
#' @return `ui` argument in `shiny::shinyApp()`
#'
Ctrl/Cmd + Shift + D
movies_ui()
The documentation for movies_server()
is very similar to the Ui function–each module server function is documented in it’s own @section
.
Still, I’ll include a @section
for Communication
that describes how values are passed between mod_var_input_server()
and mod_scatter_display_server()
.
show/hide roxygen2 for movies_server()
#' Movies server function
#'
#' Server function for standalone app function
#'
#' @usage NULL
#'
#' @details
#' The [launch_app()] function is as a wrapper for `shiny::shinyApp()`:
#'
#' ```
#' shinyApp(movies_ui, movies_server)
#' ```
#'
#' In [launch_app()]:
#' * UI is stored in [movies_ui()]
#' * server is stored in `movies_server()`
#'
#' @section `var_input` module:
#' [mod_var_input_server()] returns following reactive values:
#' * `x`
#' * `y`
#' * `z`
#' * `alpha`
#' * `size`
#' * `plot_title`
#'
#' @seealso [mod_var_input_ui()]
#'
#' @section `scatter_display` module:
#' [mod_scatter_display_server()] displays the `ggplot2` graph with the [scatter_plot()] function.
#'
#' @seealso [mod_scatter_display_ui()]
#'
#' @section Communication:
#' The output from [mod_var_input_server()] should be supplied to the
#' `var_inputs` argument of [mod_scatter_display_server()].
#
#' @return `server` argument in `shiny::shinyApp()`
#'
Ctrl/Cmd + Shift + D
movies_server()
5.3.2 launch_app()
The standalone app function (launch_app()
) automatically calls shinyApp()
, with the movies_ui
and movies_server
functions supplied to ui
and server
(without parentheses).
<- function() {
launch_app ::shinyApp(ui = movies_ui, server = movies_server)
shiny }
The documentation for the standalone app function can be minimal–as long as it provides links to the UI and server.
#' Movies app standalone function
#'
#' Wrapper function for `shiny::shinyApp()`
#'
#' @return Shiny app
#'
#'
#' @seealso [mod_var_input_ui()], [mod_var_input_server()], [mod_scatter_display_ui()], [mod_scatter_display_server()]
#'
Ctrl/Cmd + Shift + D
launch_app()
The contents of app.R
can be changed to the following:
# pkgs <- c("shiny", "shinythemes", "stringr", "ggplot2", "rlang")
# install.packages(pkgs, quiet = TRUE)
# packages ------------------------------------
library(shiny)
library(shinythemes)
library(stringr)
library(ggplot2)
library(rlang)
# launch_app ------------------------------------
launch_app()
Now that we’ve documented everything in R/
, we’ll load()
, document()
, and install()
our package:
Ctrl/Cmd + Shift + L
::load_all(".") devtools
ℹ Loading shinyrPkgs
Ctrl/Cmd + Shift + D
==> devtools::document(roclets = c('rd', 'collate', 'namespace'))
ℹ Updating shinyrPkgs documentation
ℹ Loading shinyrPkgs
Documentation completed
Ctrl/Cmd + Shift + B
==> R CMD INSTALL --preclean --no-multiarch --with-keep.source shinyrPkgs
* installing to library ‘/path/to/local/install/shinyrPkgs-090c61fc/R-4.2/x86_64-apple-darwin17.0’
* installing *source* package ‘shinyrPkgs’ ...
** using staged installation
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (shinyrPkgs)
In the Console, we should see the following:
Restarting R session...
> library(shinyrPkgs)
The roxygen2
documentation for shinyrPkgs
is saved in the 02-roxygen
branch. As we can see, calling devtools::document()
generates the .Rd
files in the man/
folder for each function in the R/
folder:
R/
├── launch_app.R
├── mod_scatter_display.R
├── mod_var_input.R
├── movies_server.R
├── movies_ui.R
└── scatter_plot.R
1 directory, 6 files
man/
├── launch_app.Rd
├── mod_scatter_display_server.Rd
├── mod_scatter_display_ui.Rd
├── mod_var_input_server.Rd
├── mod_var_input_ui.Rd
├── movies_server.Rd
├── movies_ui.Rd
└── scatter_plot.Rd
1 directory, 8 files
Recap
Good documentation aims to make it as easy as possible for others (and future you) to understand what your function does and how to use it.
Launch app with the shinypak
package:
launch('05_roxygen2')
Below is a recap of the topics covered in this chapter.
In the next section, we’ll use roxygen2
to manage our app-package dependencies.
‘most of our packages have a
utils.R
file (Section 6.1) that contains small utility functions that are useful internally, but aren’t part of the core purpose of those packages. We don’t export such functions.’↩︎Having a 1:1 match on function and file names will also make writing and running tests easier.↩︎