ETC5523: Communicating with Data
Workshop 11: Trace, package and audit a Shiny app
🎯 Objectives
By the end of this workshop, you will be able to:
- use
reactlogto inspect a Shiny dependency graph; - replace repeated calculations with one shared reactive expression;
- add a package function that launches a Shiny app; and
- audit an app before web deployment.
1. Clone and run the app (5 minutes)
Clone the workshop repository and open wplshare.Rproj:
git clone https://github.com/numbats/etc5523-wpl-share.git
cd etc5523-wpl-shareInstall any packages you do not already have:
install.packages(c(
"bslib", "devtools", "dplyr", "ggplot2", "reactlog", "rsconnect", "shiny"
))Start a fresh R session, enable reactive logging, and run the app:
reactlog::reactlog_enable()
shiny::runApp("inst/app")Change both inputs and check the number, plot and table.
2. Inspect the current graph (10 minutes)
With the app running, open the reactive log:
- macOS: Cmd + F3
- Windows/Linux: Ctrl + F3
Find input$season, input$minimum_runs and the four outputs. The log may also contain browser and layout nodes; use search or double-click an output to isolate its dependency family.
Then return to the app and set Minimum runs to exactly 150 in season 2024/25. Compare the number, plot and table before inspecting app.R.
Answer:
- Why does the table disagree with the other outputs?
- Why do both inputs connect directly to every output in
reactlog?
If the keyboard shortcut does not work, stop the app and run shiny::reactlogShow() in the console.
3. Put the filter in one place (15 minutes)
Add this labelled reactive expression at the top of server():
selected_batters <- reactive({
batters |>
filter(
season == input$season,
runs >= input$minimum_runs
)
}, label = "selected_batters")Replace the repeated filtering code in all four outputs with calls to selected_batters().
Run the app and repeat the 150-run test. Then stop the app, clear the old log, run it again, and change one input:
shiny::reactlogReset()
shiny::runApp("inst/app")Reopen reactlog. Identify which nodes depend on selected_batters and step through what happens after the input changes.
4. Make the app launchable from the package (12 minutes)
The repository already has the structure of an R package. Files under inst/app will be installed with it.
Create R/run_app.R, then add a documented, exported run_app() function:
usethis::use_r("run_app")The function should:
- locate the installed
appdirectory withsystem.file(); and - pass that directory to
shiny::runApp().
Generate the documentation, install the package and test the launcher:
devtools::document()
devtools::install()
wplshare::run_app()Why would a path such as /Users/name/wpl-share/inst/app fail for another package user?
5. Audit the web deployment (10 minutes)
Treat the hosting service as a clean Linux computer that receives only the app bundle. Mark each item ready, not ready, or not applicable:
- all required files are inside
inst/app; - file paths are relative and match the exact case of the file names;
- package dependencies can be detected from the app code;
- there is no
install.packages()call in the app; - no password, token or secret is stored in the repository;
- every possible input state produces a useful response; and
- any durable writes use storage outside the app’s local filesystem.
Ask rsconnect what it detects:
rsconnect::appDependencies("inst/app")Which audit item does the current app handle least well? If time permits, make one improvement and rerun the app.